diff --git a/README.md b/README.md index cc67967..375918a 100644 --- a/README.md +++ b/README.md @@ -2,31 +2,61 @@ 面向内网运行环境的 InSAR 数据管理、生产调度、结果发布与运维自检系统。 -当前项目已经从“以 D-InSAR 结果扫描为中心”的旧形态,收口为“地图主界面 + 生产管理工作台 + 统一结果目录 + 数据库自维护”的架构。 +当前项目已经收口为: -## 当前状态 +- 地图主界面; +- 数据管理与生产规划; +- 生产管理工作台; +- D-InSAR 与 Gamma SBAS-InSAR 生产; +- 统一结果目录与 catalog; +- 数据库启动自维护与运行健康检查。 -- 顶级生产入口已经统一为“生产管理”。 -- “生产管理”同时承载 D-InSAR 运行、时序 InSAR 运行、D-InSAR 产物、时序 InSAR 产物。 -- 前端显示名已经统一为“时序 InSAR”;当前默认接入的是 SBAS 流程,后续可继续扩展 PS-InSAR、SBAS-InSAR 等子类型。 -- D-InSAR 当前支持 `sarscape` 与 `isce2` 两类引擎;`gamma / pyint` 已按共享 WSL 运行时模型预留接口。 -- 结果目录已经统一发布到 `RESULT_PUBLISH_ROOT`,默认根目录为 `D:\production_results`。 +## 当前生产入口 -## 当前有效目录约定 +前端顶级入口为“生产管理”,内部视图如下: -- D-InSAR 发布根目录:`D:\production_results\dinsar` -- 时序 InSAR 发布根目录:`D:\production_results\timeseries` -- 隔离目录:`D:\production_results\_quarantine` -- WSL Broker 作业目录:`backend\runtime\wsl_jobs` +```text +生产管理 + - D-InSAR 运行 + - SBAS-InSAR Production + - SBAS-InSAR 结果 + - D-InSAR 产物 +``` + +当前事实: + +- D-InSAR 生产支持 `sarscape`、`isce2`,并保留 Gamma / PyINT D-InSAR 能力。 +- SBAS-InSAR 主线是独立 Gamma DIFF + IPTA SBAS 工作流。 +- 旧 ISCE2/MintPy 时序生产链和旧 `ps_production` / `ps_products` 页面不再作为生产入口。 +- SBAS 结果通过独立 `/api/sbas-insar-products` catalog 管理。 + +## 目录约定 + +默认发布根: + +```text +D:\production_results +``` + +关键目录: + +```text +D:\production_results\dinsar +D:\production_results\timeseries +D:\production_results\_quarantine +backend\runtime\sbas_insar_production +backend\runtime\wsl_jobs +``` 说明: -- 现在的文件系统事实来源是 `RESULT_PUBLISH_ROOT` 及其下属目录,不再是历史性的 `backend\result_products` 目录。 -- 数据库里的 `result_products / result_assets / result_issues` 表仍然保留,并继续作为结果登记与检索的核心表结构。 +- `RESULT_PUBLISH_ROOT` 是文件系统结果发布事实来源。 +- `result_products / result_assets / result_issues` 是结果登记和检索的数据库事实来源。 +- SBAS 托管运行目录在 `backend/runtime/sbas_insar_production`,完成后由 SBAS catalog 登记为结果产品。 -## 启动时自维护链路 +## 启动自维护 -后端启动时会按固定顺序做自维护: +后端启动时会执行: 1. `ensure_database_ready(...)` 2. `database.init_db()` @@ -34,37 +64,36 @@ 4. `manifest_inventory_service.sync_manifest_roots()` 5. `result_catalog_service.bootstrap_catalog_on_startup_clean()` 6. `psinsar_catalog_service.bootstrap_catalog_on_startup_clean()` -7. `pairing_state_service.bootstrap_pairing_cache_state()` -8. `get_health_status(include_external=False)` +7. `sbas_insar_catalog_service.bootstrap_catalog_on_startup_clean()` +8. `pairing_state_service.bootstrap_pairing_cache_state()` +9. `get_health_status(include_external=False)` -这意味着系统启动后会自动完成数据库结构校验、根目录登记、结果 catalog 自举、配对缓存状态恢复和一次启动健康检查。 +数据库自维护是增量自愈模型: -## 数据库自维护边界 +- 自动创建缺失表; +- 自动补齐缺失列; +- 自动执行已登记迁移; +- 默认不做破坏性重建。 -当前数据库自维护是“增量自愈”模型,不是完整迁移框架。 +只有同时设置: -- 会自动创建缺失表。 -- 会自动补齐缺失列。 -- 会自动执行 `backend/migrations/001` 到 `007` 的 SQL 文件。 -- 只有同时设置 `DB_SCHEMA_RESET_ON_MISMATCH=true` 和 `DB_SCHEMA_RESET_CONFIRM=true`,才允许破坏性重建。 +```env +DB_SCHEMA_RESET_ON_MISMATCH=true +DB_SCHEMA_RESET_CONFIRM=true +``` -不会自动处理的情况: - -- 字段改名 -- 字段类型变更 -- 可空性收紧 -- 索引或约束漂移 -- 删除列 / 删除表 +才允许破坏性重建。 ## 快速启动 -1. 复制 `.env.example` 为 `.env`,按现场路径修改数据库、源数据目录、结果目录、IDL/ENVI、WSL 运行时配置。 -2. 准备 PostgreSQL + PostGIS。 -3. 准备 Windows Python 运行环境。 -4. 如需 ISCE2 / Gamma / 时序 InSAR,准备 WSL2 Ubuntu 运行时与共享 conda 环境。 -5. 启动后端与 Worker。 +1. 复制 `.env.example` 为 `.env`。 +2. 按现场路径配置数据库、源数据目录、轨道目录、结果目录、IDL/ENVI、WSL 和 Gamma。 +3. 准备 PostgreSQL + PostGIS。 +4. 准备 Windows Python 环境。 +5. 如需 WSL/Gamma/ISCE2 能力,准备 Ubuntu-24.04 与共享 conda 环境。 +6. 启动后端和 Worker。 -常用启动方式: +常用方式: ```powershell start_system.bat @@ -77,12 +106,9 @@ python run_backend.py python run_worker.py ``` -## Git Clone Deployment +Worker 必须常驻,否则生产任务、扫描任务和解包任务不会执行。 -This repository is intended to stay deployable after a clean `git clone` on a new -Windows host. - -Recommended bootstrap path: +## Git Clone 部署 ```powershell git clone @@ -93,7 +119,7 @@ powershell -ExecutionPolicy Bypass -File .\scripts\bootstrap_clone.ps1 -InitFron start_system.bat ``` -Optional runtime bootstrap commands: +可选: ```powershell powershell -ExecutionPolicy Bypass -File .\scripts\bootstrap_clone.ps1 -InitWindowsConda @@ -101,46 +127,16 @@ powershell -ExecutionPolicy Bypass -File .\scripts\bootstrap_clone.ps1 -InitWslC powershell -ExecutionPolicy Bypass -File .\scripts\bootstrap_clone.ps1 -All ``` -The clone bootstrap script keeps the existing startup, database self-maintenance, -and health-check chain unchanged. See [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md) -for the current deployment model and switch details. +## 文档入口 -说明: - -- Worker 必须常驻,否则生产任务、扫描任务、解包任务不会执行。 -- 当前扫描模式默认是 manual-only,不会自动开启后台定时扫描。 - -## 文档导航 +当前文档以 [docs/INDEX.md](docs/INDEX.md) 为准。 建议阅读顺序: -1. [文档治理约定](docs/DOCUMENTATION_GOVERNANCE.md) -2. [文档索引](docs/INDEX.md) -3. [当前状态快照](docs/CURRENT_STATUS_20260425.md) -4. [部署与运行说明](docs/DEPLOYMENT.md) -5. [数据库自维护审计](docs/DATABASE_SELF_MAINTENANCE_AUDIT_20260425.md) +1. [部署与运行说明](docs/DEPLOYMENT.md) +2. [SBAS-InSAR 当前工作流](docs/SBAS_INSAR_CURRENT_WORKFLOW.md) +3. [前端导航架构](docs/FRONTEND_NAVIGATION_ARCHITECTURE.md) +4. [多引擎生产结果管理与路径设计](docs/PRODUCTION_RESULTS_MULTI_ENGINE_DESIGN_20260423.md) +5. [文档治理约定](docs/DOCUMENTATION_GOVERNANCE.md) -说明: - -- `docs/INDEX.md` 是当前有效文档与历史参考文档的总入口。 -- `INIT.md` 是工作笔记,不应替代正式文档。 -- 历史材料可从 `docs/archive/INDEX.md` 进入。 - -当前仍在生效的专题设计文档: - -- [多引擎结果目录设计](docs/PRODUCTION_RESULTS_MULTI_ENGINE_DESIGN_20260423.md) -- [WSL 共享运行时重构](docs/WSL_RUNTIME_REFACTOR_DESIGN_20260422.md) -- [ISCE2 托管 D-InSAR 实施说明](docs/ISCE2_MANAGED_DINSAR_IMPLEMENTATION_20260424.md) -- [ISCE2 生产可靠性加固设计](docs/ISCE2_PRODUCTION_RELIABILITY_HARDENING_DESIGN_20260424.md) - -## 2026-04-25 运行态摘要 - -基于当前代码和现场数据库检查结果: - -- 数据库 schema 与 ORM 一致,无缺表、缺列、类型漂移、可空性漂移。 -- D-InSAR catalog 已登记 19 个产品。 -- 时序 InSAR catalog 当前为 0 个产品。 -- 产品包 schema 已统一为 `insar.product-package/v1`。 -- WSL 共享运行时健康,当前共享环境为 `insar_wsl_v1`。 - -详细结果见 [docs/DATABASE_SELF_MAINTENANCE_AUDIT_20260425.md](docs/DATABASE_SELF_MAINTENANCE_AUDIT_20260425.md)。 +`INIT.md` 是工作笔记,不是当前架构或部署事实的最高依据。 diff --git a/docs/CODEBASE_CLEANUP_AUDIT_20260425.md b/docs/CODEBASE_CLEANUP_AUDIT_20260425.md deleted file mode 100644 index c3b662a..0000000 --- a/docs/CODEBASE_CLEANUP_AUDIT_20260425.md +++ /dev/null @@ -1,148 +0,0 @@ -# Codebase Cleanup Audit - -Updated: 2026-04-25 - -## Scope - -This audit focuses on three cleanup questions: - -1. Which files are clearly transitional or unused? -2. Which documentation files are authoritative versus historical? -3. Are the reported "garbled comments" real source corruption or terminal/display issues? - -## Confirmed Cleanup Completed In This Wave - -The frontend had active implementations living under transitional filenames while older same-purpose files remained in place. - -Completed normalization: - -- Promoted active files from transitional names back to canonical names: - - `frontend/src/DinsarProductsPanel.jsx` - - `frontend/src/components/DinsarCatalogPanel.jsx` - - `frontend/src/panels/DinsarResultPanel.jsx` - - `frontend/src/components/ResultExportModal.jsx` - - `frontend/src/components/panels/DinsarResultRow.jsx` - - `frontend/src/LogManagementPanel.jsx` -- Removed superseded duplicate implementations that were no longer referenced. -- Updated imports so runtime entry points no longer depend on `.rewrite` or `.clean` suffixes. -- Updated `INIT.md` so it no longer describes the old temporary naming scheme as current reality. - -## High-Confidence Findings - -### 1. Transitional frontend files had become the real implementation - -Before cleanup, the live entry points imported: - -- `DinsarProductsPanel.rewrite.jsx` -- `DinsarResultPanel.rewrite.jsx` -- `LogManagementPanel.clean.jsx` - -while older canonical filenames still existed beside them. - -This is a maintenance hazard because: - -- file names no longer reflect runtime truth -- engineers can patch the wrong file -- stale files increase review and search noise - -### 2. Several "unused file" suspicions were correct, but not all of them - -Confirmed pattern: - -- some canonical frontend files were effectively dead -- some `.rewrite` or `.clean` files were not dead at all; they were the active implementation - -Conclusion: - -- `rewrite` / `clean` suffix is not enough to classify a file as removable -- reference tracing is required before deletion - -### 3. Most observed Chinese garbling is a tooling/display problem, not necessarily source corruption - -Key project files such as: - -- `README.md` -- `docs/DEPLOYMENT.md` -- `docs/CURRENT_STATUS_20260425.md` -- `backend/app/main.py` -- `backend/app/config.py` - -read correctly when opened as UTF-8. - -This indicates that a significant part of the reported garbling comes from PowerShell/default encoding behavior rather than broken source text. - -### 4. Some operational notes were stale even when the code was fine - -`INIT.md` still described the temporary `LogManagementPanel.clean.jsx` workflow after the codebase had already stabilized enough for normalization. - -That kind of drift is small, but it compounds quickly in a repo with many dated design notes. - -## Remaining Cleanup Candidates - -These items were not removed automatically in this wave, but they should be considered next. - -### A. Documentation governance - -Current situation: - -- `docs/INDEX.md` does a reasonable job separating current docs from historical docs -- the repository still contains many dated design, TODO, experiment, and archive documents - -Recommended next step: - -- keep `docs/INDEX.md` as the contract -- move any newly superseded design notes to `docs/archive/` -- avoid leaving outdated process notes at repo root unless they are still operational - -### B. Large-file refactors - -Large files remain a maintainability risk even when they are active: - -- `frontend/src/App.jsx` -- `backend/app/services/timeseries_service.py` -- `backend/app/models/orm.py` -- `backend/app/services/dinsar_production_service.py` - -Recommended next step: - -- split by responsibility, not by arbitrary line count -- keep public contracts stable while extracting helpers/modules - -### C. Historical compatibility layers - -There are still intentional legacy bridges in the backend, for example: - -- compatibility catalog/data bridges -- legacy manifest normalization paths -- legacy environment variables in WSL runtime definitions - -These should not be removed blindly. They need a separate compatibility retirement review driven by real production usage. - -## Recommendations - -### Phase 1: Done - -- remove dead duplicate frontend files -- normalize active transitional filenames -- correct stale operational notes - -### Phase 2: Safe repository hygiene - -- review root-level notes such as `INIT.md` for whether they still belong at repo root -- move superseded design/process notes to `docs/archive/` -- add a lightweight naming rule: no long-lived `.rewrite`, `.clean`, `.tmp`, `.bak` files in active UI paths - -### Phase 3: Controlled architecture cleanup - -- split oversized service files -- document which compatibility layers are still required by production data -- retire legacy code only after proving there is no runtime dependency - -## Practical Rule Going Forward - -Use this decision order for cleanup: - -1. Trace imports or runtime references. -2. Normalize active files back to canonical names. -3. Delete only the files that are both superseded and unreferenced. -4. Update the nearest authoritative document in the same change. diff --git a/docs/CURRENT_STATUS_20260425.md b/docs/CURRENT_STATUS_20260425.md deleted file mode 100644 index 224f113..0000000 --- a/docs/CURRENT_STATUS_20260425.md +++ /dev/null @@ -1,151 +0,0 @@ -# 当前状态快照 - -更新时间:2026-04-25 - -## 1. 项目形态 - -当前项目已经形成以下稳定形态: - -- 主界面仍然是地图工作区。 -- 顶级生产入口已经统一为“生产管理”。 -- “生产管理”同时包含: - - D-InSAR 运行 - - 时序 InSAR 运行 - - D-InSAR 产物 - - 时序 InSAR 产物 -- 时序入口的前端显示名已经统一为“时序 InSAR”。 -- 当前时序链路默认接入的是 SBAS 实现,而不是传统 PS-InSAR 单一路径。 - -## 2. 当前生产引擎 - -### D-InSAR - -- `sarscape` - 当前可用,已有 19 个发布产品进入 catalog。 - -- `isce2` - 已完成托管式接入,运行在 WSL 共享运行时中。 - -- `gamma / pyint` - 运行时接口与目录已经预留,后续可继续落生产设计。 - -### 时序 InSAR - -- 当前产品名称统一为“时序 InSAR”。 -- 当前默认执行路径是 SBAS。 -- 后续可在该顶级入口下继续扩展 `psinsar`、`sbas-insar` 等类型。 - -## 3. 结果目录现状 - -当前结果目录已经统一收口到 `D:\production_results`: - -```text -D:\production_results -├─ dinsar -├─ timeseries -└─ _quarantine -``` - -当前健康检查中的目录状态: - -- `D:\production_results\dinsar` - 存在,catalog 正常。 - -- `D:\production_results\timeseries` - 存在,catalog 正常。 - -- `D:\production_results\_quarantine` - 已作为统一隔离根目录纳入配置模型。 - -说明: - -- `backend\result_products` 旧目录已经不是当前文件系统事实来源。 -- 数据库 `result_products` 表仍然保留,是发布产品登记表,不要与旧目录混淆。 - -## 4. 当前启动自维护链路 - -后端启动时会依次执行: - -1. 数据库自维护 -2. SQLAlchemy 会话初始化 -3. 根目录登记同步 -4. manifest inventory 同步 -5. D-InSAR catalog 自举 -6. 时序 InSAR catalog 自举 -7. pairing cache 状态自举 -8. 启动健康检查 - -这说明当前系统已经不是“纯手工维护目录 + 手工修库”的模式,而是有稳定的启动自维护链路。 - -## 5. 2026-04-25 现场健康摘要 - -本次审计现场读取到的核心状态如下: - -- 数据库: - - `ok = true` - - `schema_ok = true` - - `postgis_ok = true` - - `required_table_count = 42` - -- D-InSAR catalog: - - `storage_root = D:\production_results\dinsar` - - `manifest_count = 19` - - `db_count = 19` - - `needs_rebuild = false` - -- 时序 InSAR catalog: - - `storage_root = D:\production_results\timeseries` - - `manifest_count = 0` - - `db_count = 0` - - `needs_rebuild = false` - -- 产品包: - - `total_count = 19` - - `canonical_schema = insar.product-package/v1` - - 缺失 manifest / publish dir / processor / runtime / native output 均为 0 - -- WSL 共享运行时: - - `shared_distro = Ubuntu-24.04` - - `shared_conda_env_name = insar_wsl_v1` - - `shared_python_path = /home/administrator/miniconda3/envs/insar_wsl_v1/bin/python` - - `required_runtime_count = 2` - - `healthy_runtime_count = 2` - -- 配对系统: - - `status = READY` - - `scene_count = 1500` - - `pair_count = 91737` - - `dirty_scene_count = 0` - -## 6. 现场源目录状态 - -当前健康检查确认以下源目录可访问: - -- `D:\LuTan1_Image_Pool` -- `D:\LT1_data_lsarorbit` -- `D:\DInSARResult` - -即当前 `source_roots` 为全绿状态,没有不可访问目录。 - -## 7. 当前数据库设计是否匹配 - -结论: - -- 当前 ORM 与现场数据库匹配。 -- 当前 catalog、结果包、WSL 运行时和 pairing 状态与现有架构一致。 -- 当前数据库自维护机制适合现在这批“新增字段、统一结果目录、增加运行时登记”的改动。 - -边界: - -- 它仍然只是“增量补齐型”自维护,不是全功能 migration 框架。 -- 如果后续要做字段改名、类型调整、约束重构,仍然需要显式迁移方案。 - -## 8. 当前最需要保持一致的事实 - -以下几件事已经是当前系统事实,后续文档和代码都应围绕它们展开: - -- 顶级生产入口是“生产管理”,不是“生产规划”里的临时子入口。 -- 时序 InSAR 是顶级产品族,当前默认接入 SBAS。 -- 结果发布根目录是 `RESULT_PUBLISH_ROOT`,不是历史散落目录。 -- WSL 运行时是共享模型,当前共享环境为 `insar_wsl_v1`。 -- 数据库自维护默认保守,不自动做破坏性重建。 diff --git a/docs/DATABASE_SELF_MAINTENANCE_AUDIT_20260425.md b/docs/DATABASE_SELF_MAINTENANCE_AUDIT_20260425.md deleted file mode 100644 index 714752d..0000000 --- a/docs/DATABASE_SELF_MAINTENANCE_AUDIT_20260425.md +++ /dev/null @@ -1,203 +0,0 @@ -# 数据库自维护审计 - -审计日期:2026-04-25 - -## 1. 审计目标 - -本次审计关注两件事: - -1. 当前数据库自维护机制,是否仍然符合现在的系统设计。 -2. 当前现场数据库,是否已经与现有 ORM / catalog / 运行时设计对齐。 - -## 2. 当前自维护机制实际做什么 - -代码入口位于 `backend/app/db_maintenance.py`,系统启动时由 `backend/app/main.py` 调用。 - -当前自维护能力包括: - -- 自动创建 `postgis` 扩展 -- 自动创建缺失表 -- 自动补齐缺失列 -- 自动执行 `backend/migrations/001` 到 `006` -- 自动引导管理员账号 -- 自动灌入灾害点数据 - -当前启动链路中的相关步骤: - -1. `ensure_database_ready(...)` -2. `database.init_db()` -3. 根目录登记同步 -4. manifest inventory 同步 -5. D-InSAR / 时序 catalog 自举 -6. pairing state 自举 -7. 健康检查 - -## 3. 当前机制不做什么 - -当前实现不是完整迁移框架,它不会自动处理: - -- 字段改名 -- 字段类型变更 -- 可空性从宽到严 -- 索引 / 约束漂移修补 -- 旧字段 / 旧表删除 - -破坏性重建只有在以下两个开关同时为 `true` 时才允许: - -```env -DB_SCHEMA_RESET_ON_MISMATCH=true -DB_SCHEMA_RESET_CONFIRM=true -``` - -默认情况下这是关闭的,符合当前内网生产环境“保守自维护”的要求。 - -## 4. 本次现场检查方法 - -本次检查直接用当前项目代码读取现场数据库并执行: - -- `inspect_database_structure(...)` -- `ensure_database_ready(settings.DATABASE_URL, bootstrap_admin=False, seed_hazard=False)` -- `get_health_status(include_external=False, include_details=True, refresh=True)` - -使用的是项目当前 `.env` 中配置的 Python 解释器和数据库连接。 - -## 5. 现场检查结果 - -### 5.1 schema 结构检查 - -结果: - -- `mismatch = false` -- `reason_count = 0` -- `required_table_count = 42` -- `missing_tables = []` -- `extra_tables = []` -- `missing_columns = {}` -- `type_mismatches = []` -- `nullable_mismatches = []` - -结论: - -- 现场数据库结构与当前 ORM 一致。 -- 当前新增的结果包、运行时、catalog 相关字段已经在数据库中落稳。 - -### 5.2 自维护执行结果 - -结果: - -- `schema_reset = false` -- `mismatch_detected = false` -- `added_columns = []` -- `bootstrap_initialized = false` - -启动时仍会执行以下 SQL 文件: - -- `001_st_intersection_agg.sql` -- `002_spatial_functions.sql` -- `003_pairing_enhancement.sql` -- `004_pairing_refactor.sql` -- `005_pairing_task_trace.sql` -- `006_result_pairing_trace.sql` - -结论: - -- 当前数据库已处于“无需修补”的稳定状态。 -- 启动自维护仍会重复执行迁移 SQL,因此这些 SQL 文件必须继续保持幂等。 - -### 5.3 健康检查结果 - -结果摘要: - -- `health.ok = true` -- `database.ok = true` -- `database.schema_ok = true` -- `database.postgis_ok = true` -- `dinsar_result_catalog.ok = true` -- `timeseries_result_catalog.ok = true` -- `dinsar_bridge.ok = true` -- `source_roots.ok = true` -- `product_packages.ok = true` -- `wsl_runtime.ok = true` -- `pairing_system.ok = true` - -关键现场值: - -- D-InSAR catalog: - - `storage_root = D:\production_results\dinsar` - - `manifest_count = 19` - - `db_count = 19` - -- 时序 InSAR catalog: - - `storage_root = D:\production_results\timeseries` - - `manifest_count = 0` - - `db_count = 0` - -- 产品包: - - `total_count = 19` - - `canonical_schema = insar.product-package/v1` - - 所有缺失项计数均为 0 - -- WSL 共享运行时: - - `shared_distro = Ubuntu-24.04` - - `shared_conda_env_name = insar_wsl_v1` - - `shared_python_path = /home/administrator/miniconda3/envs/insar_wsl_v1/bin/python` - - `required_runtime_count = 2` - - `healthy_runtime_count = 2` - -## 6. 结论 - -结论很明确: - -- 当前数据库自维护机制与当前系统状态相符。 -- 对于当前这轮改造引入的新增字段、catalog、结果包、WSL runtime 信息,它是足够的。 -- 当前现场数据库已经对齐当前 ORM 和结果目录设计。 - -这意味着: - -- 现在可以继续在当前 schema 基础上推进 D-InSAR / 时序 InSAR 生产。 -- 不需要为了“数据库跟不上代码”而先清空库或强制重建。 - -## 7. 残余风险 - -虽然当前是对齐的,但仍有三个明确边界: - -### 7.1 它不是 migration framework - -后续如果要做以下改动,不能只靠当前自维护: - -- 重命名字段 -- 修改字段类型 -- 增加更严格的非空约束 -- 重建索引或唯一约束 -- 删除旧结构 - -### 7.2 SQL 文件必须幂等 - -因为启动时会重复执行 `001` 到 `006`,任何新增 SQL 文件也必须遵守同样原则。 - -### 7.3 健康面板已成为设计约束的一部分 - -当前运维自检不只是“看数据库能不能连”,而是在验证: - -- catalog 是否正常 -- product package 是否完整 -- WSL 运行时是否齐全 -- pairing trace 是否一致 - -因此后续只要改目录模型、结果包模型、运行时模型,就必须同步维护健康检查逻辑。 - -## 8. 建议 - -当前建议如下: - -1. 保持 `DB_SCHEMA_RESET_ON_MISMATCH=false` 和 `DB_SCHEMA_RESET_CONFIRM=false`。 -2. 后续涉及 schema 破坏性调整时,单独编写受控迁移,不要指望启动自维护自动兜底。 -3. 新增数据库字段时,优先采用“可空 + 向后兼容 + 健康面板补校验”的方式推进。 -4. 每次结果目录、运行时 registry、catalog 设计变动后,都重新跑一次: - - schema 检查 - - `ensure_database_ready(...)` - - `GET /api/health` - -## 9. 审计结语 - -截至 2026-04-25,数据库自维护机制与当前系统状态是匹配的,且现场数据库处于健康状态。当前更大的风险已经不在“数据库结构漂移”,而在后续若继续做结果模型或运行时模型重构时,是否同步维护 catalog、健康检查和文档。 diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md index 3ac3538..d86a9e5 100644 --- a/docs/DEPLOYMENT.md +++ b/docs/DEPLOYMENT.md @@ -13,12 +13,13 @@ - D-InSAR 引擎: - `sarscape`,运行在 Windows + IDL/ENVI - `isce2`,运行在 WSL2 共享运行时 -- 时序 InSAR: - - 当前前端名称为“时序 InSAR” - - 当前默认接入为 SBAS 流程 +- SBAS-InSAR: + - 当前主线为 Gamma DIFF + IPTA SBAS + - 独立生产入口为 `/api/sbas-insar-production` + - 独立结果入口为 `/api/sbas-insar-products` - Gamma / PyINT: - - 运行时接口已预留 - - Gamma 本体采用固定安装目录,Python 胶水共享 WSL 环境 + - Gamma 本体采用固定安装目录 + - Python 胶水共享 WSL 环境 ## 2. 结果目录 @@ -42,7 +43,7 @@ RESULT_QUARANTINE_ROOT=D:\production_results\_quarantine D-InSAR 产物发布根目录。 - `TIMESERIES_PRODUCT_DIR` - 时序 InSAR 产物发布根目录。 + 时序类产物发布根目录;当前 SBAS 结果默认位于其下的 `sbas` 子目录。 - `RESULT_QUARANTINE_ROOT` 异常产物、待人工处理产物的隔离目录。 @@ -56,9 +57,8 @@ D:\production_results │ └─ runs │ └─ run_____ ├─ timeseries -│ └─ -│ └─ runs -│ └─ run_____ +│ └─ sbas +│ └─ └─ _quarantine ``` @@ -138,30 +138,38 @@ PYINT_RUNTIME_ID=gamma_pyint_runtime_v1 使用共享 python,runner 为 `deploy/wsl/runners/gamma_pyint_runner.py` Gamma 固定环境脚本为 `deploy/wsl/profiles/gamma_env.sh` -### 3.6 ISCE2 / 时序 InSAR +### 3.6 ISCE2 D-InSAR 与旧时序兼容 ```env -ISCE2_ENABLED=true +ISCE2_ENABLED=false ISCE2_WSL_DISTRO=Ubuntu-24.04 ISCE2_PYTHON=/home/administrator/miniconda3/envs/insar_wsl_v1/bin/python -TIMESERIES_ENABLED=true +TIMESERIES_ENABLED=false TIMESERIES_ENV_NAME=insar_wsl_v1 TIMESERIES_PYTHON=/home/administrator/miniconda3/envs/insar_wsl_v1/bin/python ``` 说明: -- 目前现场共享运行时已经对齐到 `insar_wsl_v1`。 -- 当前时序入口默认接入 SBAS 工作流,因此时序链路仍然依赖 ISCE2 / MintPy 实验脚本集合。 +- ISCE2 可作为 D-InSAR 引擎启用。 +- 旧 ISCE2/MintPy 时序生产链默认关闭,不再作为 SBAS 生产入口。 +- 如果必须做历史链路对比,需要显式开启 `TIMESERIES_ENABLED=true` 并提供完整旧脚本路径。 -### 3.7 Gamma / PyINT +### 3.7 Gamma / PyINT / SBAS ```env PYINT_ENABLED=true PYINT_WSL_DISTRO=Ubuntu-24.04 PYINT_WSL_PYTHON=/home/administrator/miniconda3/envs/insar_wsl_v1/bin/python PYINT_GAMMA_ENV_SCRIPT=D:\Code\Insar_management_system_v2\deploy\wsl\profiles\gamma_env.sh + +GAMMA_SBAS_ENABLED=true +GAMMA_SBAS_WSL_DISTRO=Ubuntu-24.04 +GAMMA_SBAS_PYTHON=/home/administrator/miniconda3/envs/insar_wsl_v1/bin/python +GAMMA_SBAS_ENV_SCRIPT=D:\Code\Insar_management_system_v2\deploy\wsl\profiles\gamma_env.sh +GAMMA_SBAS_SOURCE_ROOTS=D:\LuTan1_Image_Pool +GAMMA_SBAS_ORBIT_ROOTS=D:\orbit_pools\envi ``` 说明: @@ -199,8 +207,9 @@ python run_worker.py 4. `manifest_inventory_service.sync_manifest_roots()` 5. `result_catalog_service.bootstrap_catalog_on_startup_clean()` 6. `psinsar_catalog_service.bootstrap_catalog_on_startup_clean()` -7. `pairing_state_service.bootstrap_pairing_cache_state()` -8. `get_health_status(include_external=False)` +7. `sbas_insar_catalog_service.bootstrap_catalog_on_startup_clean()` +8. `pairing_state_service.bootstrap_pairing_cache_state()` +9. `get_health_status(include_external=False)` 这套链路要求: @@ -244,6 +253,7 @@ DB_SCHEMA_RESET_CONFIRM=true - `database` - `dinsar_result_catalog` - `timeseries_result_catalog` +- `sbas_insar_result_catalog` - `dinsar_bridge` - `source_roots` - `product_packages` @@ -295,14 +305,13 @@ VITE_TILE_SERVER_TOKEN=change_me 1. 打开前端并确认地图、生产管理和运维自检可正常进入。 2. 访问 `GET /api/health`,确认 database / catalog / product_packages / wsl_runtime 为 `ok`。 -3. 触发一次实际生产任务,确认结果能发布到 `DINSAR_PRODUCT_DIR` 或 `TIMESERIES_PRODUCT_DIR`,并被 catalog 收录。 +3. 触发一次实际生产任务,确认 D-InSAR 或 SBAS 结果能被对应 catalog 收录。 ## 11. 相关文档 - [../README.md](../README.md) -- [CURRENT_STATUS_20260425.md](CURRENT_STATUS_20260425.md) -- [DATABASE_SELF_MAINTENANCE_AUDIT_20260425.md](DATABASE_SELF_MAINTENANCE_AUDIT_20260425.md) - [PRODUCTION_RESULTS_MULTI_ENGINE_DESIGN_20260423.md](PRODUCTION_RESULTS_MULTI_ENGINE_DESIGN_20260423.md) +- [SBAS_INSAR_CURRENT_WORKFLOW.md](SBAS_INSAR_CURRENT_WORKFLOW.md) - [WSL_RUNTIME_REFACTOR_DESIGN_20260422.md](WSL_RUNTIME_REFACTOR_DESIGN_20260422.md) ## DEM Sidecar Migration Warning diff --git a/docs/DINSAR_PAIRING_DISTRIBUTION_LOGIC_20260508.md b/docs/DINSAR_PAIRING_DISTRIBUTION_LOGIC_20260508.md deleted file mode 100644 index 673e8de..0000000 --- a/docs/DINSAR_PAIRING_DISTRIBUTION_LOGIC_20260508.md +++ /dev/null @@ -1,585 +0,0 @@ -# D-InSAR 配对与分发逻辑梳理 - -更新时间:2026-05-09 - -本文按当前代码实现梳理 D-InSAR 从“雷达数据入库”到“配对规划”、“批次保存”、“数据分发”和“多引擎生产执行”的主链路。重点依据源码,而不是早期设计文档。 - -## 1. 总览 - -当前 D-InSAR 链路分为两层: - -1. 配对规划层:把 `radar_data` 中的影像先预计算成 `pairing_metric_cache` 候选边,再按用户阈值和策略筛选,最后固化为一次 `pairing_network_runs` 和若干 `pairing_network_edges`。 -2. 分发执行层:配对结果可保存为 `dinsar_task_batches/items`,再复制成 `Task_*/master`、`Task_*/slave` 生产目录;生产面板再以这个根目录提交到 SARscape、ISCE2 或 PyINT/Gamma 引擎,由 DB job queue 和 worker 执行。 - -核心入口: - -- 配对 API:[backend/app/routers/pairing.py](../backend/app/routers/pairing.py) -- 配对服务:[backend/app/services/spatial_service.py](../backend/app/services/spatial_service.py) -- 配对缓存:[backend/app/services/pairing_cache_service.py](../backend/app/services/pairing_cache_service.py) -- 批次 API:[backend/app/routers/task_batches.py](../backend/app/routers/task_batches.py) -- 数据分发 API:[backend/app/routers/tools.py](../backend/app/routers/tools.py) -- 数据复制执行:[backend/app/copier.py](../backend/app/copier.py) -- 生产提交 API:[backend/app/routers/dinsar_production.py](../backend/app/routers/dinsar_production.py) -- 生产运行状态:[backend/app/services/dinsar_production_service.py](../backend/app/services/dinsar_production_service.py) -- job 队列和 worker:[backend/app/services/job_queue_service.py](../backend/app/services/job_queue_service.py)、[backend/app/services/job_worker.py](../backend/app/services/job_worker.py) - -## 2. 数据入库与配对缓存失效 - -雷达数据扫描在 [backend/app/services/data_service.py](../backend/app/services/data_service.py) 中写入或更新 `radar_data`。每个 scene 使用 `unique_id` 做 upsert;如果发现新 scene 或补齐了轨道文件,会调用 `pairing_state_service.mark_scenes_dirty()` 或 `mark_global_dirty()`。 - -配对缓存状态由 [backend/app/services/pairing_state_service.py](../backend/app/services/pairing_state_service.py) 管理: - -- 全局状态表:`pairing_cache_state` -- 待重算 scene 表:`pairing_dirty_scenes` -- 当前指标版本:`2026.05.raw.v1` -- 当前 master/slave 定向规则:`date_then_scene_uid_v1` - -应用启动时会调用 `bootstrap_pairing_cache_state()`,但不会自动全量重建候选边。缓存如果是 `DIRTY`,配对仍可返回旧缓存结果并给 warning;如果是 `FAILED`、`UNINITIALIZED`、`ERROR`,或 scene 数大于 1 但候选边为 0,`/find-pairs` 会拒绝并提示先修复缓存。 - -## 3. 候选边缓存构建 - -候选边缓存由 [pairing_cache_service.py](../backend/app/services/pairing_cache_service.py) 写入 `pairing_metric_cache`。 - -全量重建逻辑: - -- 删除全部 `pairing_metric_cache` -- 从 `radar_data m JOIN radar_data s` 重新生成候选边 -- 只保留满足硬约束的 pair: - - `m.id <> s.id` - - 两景都有 `geom` - - `imaging_date` 是 8 位日期 - - 两景都有 `orbit_direction` 且方向一致 - - 两景都是可用于 InSAR 的原始复数源:`insar_source_ready = true` - - 如果两景都有 `look_direction`,要求视向一致 - - 几何相交 `ST_Intersects` - - 按 `date_then_scene_uid_v1` 只保留一个方向,避免 A-B 和 B-A 双向重复 - -写入的主要指标: - -- `time_baseline_days`:两景日期差的绝对值 -- `scene_center_distance_meters`:两景 footprint 质心的球面距离 -- `spatial_baseline_meters`:兼容旧 API 的历史字段;新缓存中暂存同一个 footprint 中心距,不能解释为 SAR 空间/垂直基线 -- `scene_overlap_ratio`:两景交集面积 / 两景较大 footprint 面积 -- `same_satellite` -- `same_satellite_family`:同一卫星族,例如 LT1A/LT1B 归为 `LT1` -- `same_look_direction` -- `same_imaging_mode` -- `same_polarization` -- `pair_uid = md5(master_scene_uid + '|' + slave_scene_uid)` - -增量重算逻辑: - -- 如果 dirty scene 数过多、占比过高、或缓存为空,会转全量重建 -- 否则删除涉及 dirty scene 的缓存边 -- 对每个 dirty scene 与其他 scene 重新计算边 -- resolved 对应 dirty rows - -阈值: - -- dirty scene 数量达到 64 触发全量重建 -- dirty scene 占 scene 总数比例达到 25% 触发全量重建 - -## 4. `/find-pairs` 配对查询 - -前端在 [frontend/src/hooks/usePairingLogic.js](../frontend/src/hooks/usePairingLogic.js) 中把配对参数、AOI 文件或行政区 GeoJSON 组装为 `FormData`,提交到 `POST /api/find-pairs`。 - -后端入口是 [pairing.py](../backend/app/routers/pairing.py): - -- 解析配对参数为 `PairingRequest` -- 解析 AOI:支持上传 Shapefile 或传入 GeoJSON -- 调用 `spatial_service.find_dinsar_pairs()` -- 返回 `PairingResponse`,包含 pairs、warnings、`network_run_id`、`policy_version`、候选数和入选边数 - -`PairingRequest` 在 [backend/app/models/schemas.py](../backend/app/models/schemas.py) 中定义,主要参数包括: - -- `time_baseline_min/max` -- `overlap_threshold` -- `spatial_baseline_max_meters` -- `coverage_diversity_penalty` -- `require_same_imaging_mode` -- `require_same_polarization` -- `aoi_overlap_threshold` -- master/slave 日期范围 -- `strategy`: `all | sbas | sequential | star` -- `num_connections` -- `reference_image_id` -- `allowed_satellites` -- `cross_satellite_pairing` -- `start_date` 兼容旧参数 - -## 5. 候选池过滤条件 - -`spatial_service._query_pairing_metric_cache()` 只查询缓存表,不再实时两两计算。基础过滤条件: - -- `metric_version == 2026.05.raw.v1` -- `status == READY` -- `time_baseline_days` 在请求范围内 -- `scene_center_distance_meters <= spatial_baseline_max_meters` -- `scene_overlap_ratio >= overlap_threshold` -- `same_look_direction = true` -- 如果 `require_orbit_data = true`,master 和 slave 都要有精轨 -- 默认要求同卫星族;除非 `cross_satellite_pairing = true` -- 默认要求成像模式一致、极化一致 -- 如果 `allowed_satellites` 不为空,master/slave 的卫星名或卫星族都必须在列表内 -- 如果传入 master/slave 日期范围,分别约束 `master_imaging_date` 和 `slave_imaging_date` -- 如果有 AOI,master/slave footprint 都要与 AOI 相交 -- 如果 `aoi_overlap_threshold` 有值,master/slave 各自覆盖 AOI 的比例都要达标 - -排序默认按: - -1. master 日期升序 -2. slave 日期升序 -3. overlap 降序 -4. pair_uid 升序 - -## 6. 配对策略 - -策略选择在 `spatial_service._apply_strategy()`。 - -### 6.1 all - -`all` 策略不再做网络抽稀,直接返回过滤后的全部候选边。每条边的: - -- `selection_reason = all_candidate` -- `selection_score` 综合时间基线、footprint 中心距、重叠率、源数据可用性和精轨状态 - -### 6.2 sequential - -`sequential` 策略先从候选池提取 scene,按稳定时间键排序: - -- 优先 `acquisition_time_utc` -- 否则 `imaging_date` -- 再按 scene_uid 和 id 打平同日多景 - -然后每个 scene 向后寻找最多 `num_connections` 个有候选边的后继 scene。不存在于候选池的边不会被补造。 - -输出边: - -- `selection_reason = sequential_neighbor` -- `selection_score` 综合时间基线、footprint 中心距、重叠率、源数据可用性和精轨状态 - -### 6.3 star - -`star` 策略要求参考影像固定作为 master。 - -如果用户未指定 `reference_image_id`,系统会在时间序列中找靠近中位位置、且能作为 master 的 scene 自动作为参考影像。注意当前实现不会把 slave 侧边反转为 master 侧边;如果参考影像在候选边中只能出现在 slave 侧,这些边会被跳过并给 warning。 - -输出边: - -- `selection_reason = star_reference_master` -- `is_reference_edge = true` -- `reference_image_id` 写入 edge meta - -### 6.4 sbas - -`sbas` 策略用于构造小基线网络,流程是: - -1. 按时间顺序先选相邻 scene 的候选边,形成时间骨架。 -2. 如果网络有多个连通分量,优先选能连接分量的候选边。 -3. 继续补低度数节点,直到达到目标连接数或达到最大边数。 -4. 如果无法形成完整连通图,或存在 0 度/低度数节点,返回 warning。 - -关键参数: - -- `min_degree = min(max(1, num_connections), scene_count - 1)` -- `max_degree = min(max(min_degree + 2, 3), scene_count - 1)` -- `max_edges = min(candidate_count, max(scene_count - 1, scene_count * min_degree))` - -候选边评分: - -```text -score = - 0.30 * time_score -+ 0.15 * center_distance_score -+ 0.30 * overlap_score -+ 0.10 * aoi_gain -+ 0.10 * source_ready_score -+ 0.05 * orbit_score -- coverage_diversity_penalty * redundancy_penalty -``` - -其中 `aoi_gain` 和 `redundancy_penalty` 基于 master/slave 交集几何计算;如果有 AOI,会先把交集裁到 AOI 范围。 - -## 7. 网络运行留痕 - -每次 `/find-pairs` 都会创建一条 `pairing_network_runs`: - -- `network_run_id = pnr_` -- `strategy` -- `policy_version = 2026.05.raw-source.v1` -- `request_hash` -- 请求参数 JSON -- AOI hash 和 summary -- 候选边数量、入选边数量、warning 数量 - -每条入选边写入 `pairing_network_edges`: - -- 指向 `pairing_metric_cache` -- `edge_rank` -- `selection_reason` -- `selection_score` -- `selection_meta_json` -- `is_reference_edge` - -之后 `RadarPair` 响应会携带: - -- `pair_key` -- `pair_uid` -- `metric_cache_ref_id` -- `network_run_id` -- `network_edge_id` -- `policy_version` -- `selection_strategy` -- `selection_score` -- `selection_reason` -- `scene_center_distance_meters` -- `task_name/task_alias` - -`task_alias` 由 [dinsar_naming.py](../backend/app/services/dinsar_naming.py) 生成,格式是 `Task_YYYYMMDD_YYYYMMDD`;同名时追加 `_1`、`_2` 保证唯一。 - -## 8. 批次保存 - -前端找到 pairs 后,用户勾选结果并调用 `createDinsarBatch()`,提交到 `POST /api/task-batches/dinsar`。 - -后端 [task_batches.py](../backend/app/routers/task_batches.py) 会创建: - -- `dinsar_task_batches` -- `dinsar_task_items` - -每条 item 会保存: - -- `task_name/task_alias` -- `pair_key` -- `scene_pair_uid` -- `network_run_id` -- `network_edge_id` -- `policy_version` -- `selection_strategy` -- master/slave 文件路径 -- master/slave 卫星、日期、成像模式、极化 -- 时间基线、footprint 中心距 -- 人工审核状态,默认 `PENDING` - -前端批次面板可把 item 状态改成: - -- `PENDING` -- `IN_PROGRESS` -- `COMPLETED` -- `FAILED` - -数据分发默认只复制 `COMPLETED` 状态的条目。 - -## 9. 数据分发到 Task 目录 - -数据分发入口是 `POST /api/tools/copy-dinsar-pairs`,代码在 [tools.py](../backend/app/routers/tools.py)。 - -请求参数: - -- `batch_id` -- `dest_dir` -- `copy_statuses`,为空时默认 `["COMPLETED"]` -- `include_orbit_files`,默认 `false`;为 `true` 时把 master/slave 精轨复制到 Task 内的 `orbit/` -- `package_mode`,支持 `task_folder`、`task_zip`、`source_bundle` -- `export_zip`,兼容旧参数;为 `true` 且 `package_mode=task_folder` 时等价于 `task_zip` -- `skip_existing`,默认 `true` -- `max_items`,每次最多处理的新 Task 或新 pair 数量;为空或 0 表示不限制 - -后端动作: - -1. 校验目标路径。 -2. 创建 `SystemTask`,类型为 `COPY_DATA`。 -3. 创建 `SystemJob`,job_type 也是 `COPY_DATA`。 -4. worker 领取 job 后进入 `job_handlers._handle_copy_data()`。 -5. `_handle_copy_data()` 根据 `batch_id` 查询 `dinsar_task_items`,只取 `copy_statuses` 命中的条目。 -6. 根据 `package_mode` 调用 [backend/app/copier.py](../backend/app/copier.py) 的 `run_dinsar_copy_items()` 或 `run_dinsar_source_bundle_items()`。 - -### 9.1 Task 文件夹 / ZIP 模式 - -`run_dinsar_copy_items()` 对每个 item 执行: - -- 文件夹模式目标目录:`//` -- zip 模式目标文件:`/.zip` -- master 目录:`/master` -- slave 目录:`/slave` -- 如果启用 `include_orbit_files`,从 `radar_data.orbit_file_path` 找 master/slave 精轨并复制到 `/orbit/` -- 直接复制配对时保存的原始产品目录;D-InSAR 分发不再优先使用 `envi_import/` -- 先复制到临时目录,完成后再替换为最终 Task 目录或 ZIP,避免留下半成品 -- 写入 `/.dinsar_pair.json` -- `skip_existing=true` 时,文件夹模式检查 `/master` 和 `/slave` 非空即跳过;ZIP 模式只检查 `.zip` 存在且大小大于 0,不打开 ZIP 做深度校验 -- `max_items` 限制本次新复制数量;已跳过的既有 Task 不消耗本次额度 - -`.dinsar_pair.json` 是后续生产追踪的关键 sidecar,包含: - -- `pair_key` -- `task_name/task_alias` -- master/slave 原始路径和元数据 -- `time_baseline_days` -- `spatial_baseline_meters` -- `scene_center_distance_meters` -- `package_format` -- `include_orbit_files` -- `orbit_files` -- `scene_pair_uid/pair_uid` -- `network_run_id` -- `network_edge_id` -- `policy_version` -- `selection_strategy` -- `copied_at` - -当前实现不再合并写入已有 Task 目录。目标 Task 已完整存在时跳过;目标同名目录存在但不完整时,为避免误覆盖,会报错并要求人工处理。 - -### 9.2 去重源数据包模式 - -`package_mode=source_bundle` 时,分发不生成每个 `Task_*`,而是在同一个目标目录内维护: - -```text -/ - data/ - orbit/ - pairs.json - manifest.json -``` - -规则: - -- `data/` 只复制唯一源影像目录或文件,命名为 `scene__`。 -- `orbit/` 只复制唯一精密轨道文件,命名为 `orbit__`。 -- `pairs.json` 记录每个 pair 的 master/slave 数据相对路径、轨道相对路径、pair 元数据和 `identity_key`。 -- `manifest.json` 记录 package 统计信息、场景清单、轨道清单和本次追加统计。 -- 同一目标目录再次分发时,系统先读取已有 `pairs.json/manifest.json`,根据 `identity_key`、`scene_pair_uid/pair_uid`、`pair_key`、`network_run_id + network_edge_id`、源路径或 bundle 相对路径识别已导出的 pair。 -- `max_items` 在跳过已导出 pair 后生效。因此 500 个 pair 第一次限制 100,第二次同一目录限制 100,会追加下一批未导出的 100 个 pair。 -- `data/` / `orbit/` 仍按文件存在性跳过重复复制;pair 级续跑以 `pairs.json` 为准。 -- `pairs.json` 和 `manifest.json` 写入时先写临时文件,再原子替换。 - -这个模式面向外部分发和后续离线还原,不直接作为本系统生产输入。反向还原工具任务书见 [DINSAR_SOURCE_BUNDLE_REVERSE_TOOL_TASK_20260511.md](DINSAR_SOURCE_BUNDLE_REVERSE_TOOL_TASK_20260511.md)。 - -## 10. 生产提交与运行分发 - -生产入口是 `POST /api/dinsar-production/run`,前端在 [frontend/src/DinsarProductionPanel.jsx](../frontend/src/DinsarProductionPanel.jsx) 手动输入“根目录或单个任务目录”并选择引擎/模板。 - -支持引擎来自 [backend/app/dinsar_engines/registry.py](../backend/app/dinsar_engines/registry.py): - -- `sarscape` -- `isce2` -- `pyint` -- `landsar`,目前预留,不进入 D-InSAR queued production 主链路 - -提交流程: - -1. 校验 engine 是否注册且可用。 -2. 校验 profile 是否属于该 engine。 -3. 对 ISCE2/PyINT 调用 engine 的 `validate_root_dir()` 和 `normalize_extra()`。 -4. PyINT 会额外做输入资产预检。 -5. 当前 SARscape、ISCE2、PyINT 都走 managed production run。 -6. 调用 `dinsar_production_service.create_run()`。 - -`create_run()` 做的事情: - -- 根据引擎映射 task_type: - - SARscape -> `IDL_RUN_DINSAR` - - ISCE2 -> `ISCE2_RUN` - - PyINT/Gamma -> `PYINT_RUN` -- 扫描 root 下的 `Task_*` 目录,或把 root 本身当单个 Task 目录 -- 从 `.dinsar_pair.json` 解析 pair identity;如果没有 sidecar,则按目录名和路径生成 fallback -- 根据 `rerun_mode` 跳过已有 current pointer 的完成项 -- 创建 `SystemTask` -- 创建 `dinsar_production_runs` -- 创建 `dinsar_production_run_items` -- 创建一个 workflow run,只有一个 step:`execute_items` -- workflow step 入队为 `SystemJob` - -注意:生产面板目前不直接从 `dinsar_task_batches` 选择批次。实际串联方式是:先在“分发”面板把批次复制到生产根目录,再在“生产”面板提交这个根目录。 - -## 11. worker 与执行控制 - -后台 worker 在 [job_worker.py](../backend/app/services/job_worker.py): - -- 周期性 `claim_next_job()` -- DB 查询使用 `FOR UPDATE SKIP LOCKED` -- 按 `priority DESC, id ASC` 领取 `READY/RETRY` job -- 支持 worker heartbeat -- 支持 stale RUNNING job 恢复为 RETRY 或 FAILED -- `run_worker_loop()` 参数支持 job 级并发,但默认并发为 1 - -`SystemTask` 在 [task_service.py](../backend/app/services/task_service.py) 管理: - -- 创建任务时会检查同一 `task_type` 是否已有 `PENDING/RUNNING` -- PostgreSQL 下使用 advisory lock 防止并发创建同类任务 -- 因此同一类生产任务天然串行提交 - -workflow 在 [workflow_service.py](../backend/app/services/workflow_service.py): - -- 创建 workflow run 和 steps -- 没有依赖的 step 立即入队 -- job 完成后 mark step completed -- step 全部终态后 workflow run 完成 - -## 12. 各引擎生产控制器 - -job handler 在 [job_handlers.py](../backend/app/services/job_handlers.py)。 - -### 12.1 SARscape - -`_handle_idl_run_dinsar()` 如果 payload 有 `production_run_id`,会进入 `_run_dinsar_production_controller()`。 - -执行特点: - -- 使用 `engine_lock_service.acquire("envi_taskengine")`,保证 ENVI/SARscape taskengine 串行 -- 对 run item 逐个执行 -- 每个 item 创建一个 `DinsarProductionExecution` -- 调用 `build_envi_runner_command()` 启动 runner -- 运行结束后规范化输出目录 -- 写 `execution_manifest.json` -- 写 `current/__.json` -- 标记 item completed/failed/cancelled -- 成功输出目录会进入 `result_catalog_service.publish_from_sources()` - -### 12.2 ISCE2 与 PyINT/Gamma - -`_handle_isce2_run()` 和 `_handle_pyint_run()` 在 managed 模式下都进入 `_run_wsl_dinsar_production_controller()`。 - -执行特点: - -- 使用 `engine_lock_service.acquire(f"wsl_dinsar_{engine_code}")` -- 每个 item 构造独立 managed run 目录: - - run dir - - native dir - - workflow dir - - export dir - - orbit output dir -- 构造 `RunRequest` 调用 engine 的 `run()` -- engine 返回 `primary_file`、`source_files`、`native_output_dir` -- 校验 primary output 存在 -- 写 `execution_manifest.json` -- 写 current pointer -- 标记 item 状态 -- 发布成功包,并对结果 catalog 做 rebuild - -一个 production run 内部 item 是串行执行的。多个 worker 可以领取不同 job,但同类任务创建限制和 engine lock 会进一步限制实际并发。 - -## 13. 结果发布与追踪 - -生产完成后会生成标准包结构,并由 result catalog 接管。`execution_manifest.json` 中保留: - -- `run_id` -- `task_id` -- `engine_code` -- `profile_code` -- `runtime_id` -- `task_name/task_alias` -- `pair_key` -- `pair_uid` -- `network_run_id` -- `network_edge_id` -- `policy_version` -- `selection_strategy` -- `source_task_dir` -- `results_root_dir` -- `publish_root_dir` -- `primary_file` -- `source_files` -- `metrics` - -catalog 注册逻辑在 [backend/app/services/result_catalog_service.py](../backend/app/services/result_catalog_service.py) 中会继续把 pairing trace 字段写到结果产品,便于从结果反查配对网络。 - -## 14. 关键表关系 - -配对规划: - -- `radar_data` -- `pairing_cache_state` -- `pairing_dirty_scenes` -- `pairing_metric_cache` -- `pairing_network_runs` -- `pairing_network_edges` - -人工批次: - -- `dinsar_task_batches` -- `dinsar_task_items` - -后台任务: - -- `system_tasks` -- `task_logs` -- `system_jobs` -- `system_worker_heartbeats` -- `workflow_runs` -- `workflow_steps` - -生产执行: - -- `dinsar_production_runs` -- `dinsar_production_run_items` -- `dinsar_production_executions` - -## 15. 常用 API 链路 - -配对健康和修复: - -- `GET /api/pairing/health` -- `POST /api/pairing/rebuild-cache` -- `POST /api/pairing/reconcile-dirty?force_full=false` - -配对规划: - -- `POST /api/find-pairs` -- `GET /api/pairing/networks/{network_run_id}` - -批次: - -- `POST /api/task-batches/dinsar` -- `GET /api/task-batches/dinsar` -- `GET /api/task-batches/dinsar/{batch_id}/items` -- `PATCH /api/task-batches/dinsar/items/{item_id}` -- `PATCH /api/task-batches/dinsar/{batch_id}/complete-all` - -数据分发: - -- `POST /api/tools/copy-dinsar-pairs` -- `GET /api/tools/copy-status/{task_id}` - -生产: - -- `GET /api/dinsar-production/engines` -- `POST /api/dinsar-production/engines/pyint/preview-input-assets` -- `POST /api/dinsar-production/run` -- `GET /api/dinsar-production/runs` - -## 16. 当前实现边界 - -1. 配对查询完全依赖 `pairing_metric_cache`。缓存未初始化、失败、或 scene 足够但 pair 为 0 时不会降级实时计算。 -2. `scene_center_distance_meters` 是 footprint 质心距离;`spatial_baseline_meters` 仅为旧 API 兼容字段,不是 SAR 几何中的垂直基线。 -3. master/slave 方向在缓存层已经固定为“日期优先、scene_uid 次之”。`star` 策略不会把参考影像位于 slave 的边翻转。 -4. `aoi_overlap_threshold` 约束的是每一景对 AOI 的覆盖比例,不是 pair 交集对 AOI 的覆盖比例。 -5. 数据分发默认只复制 `COMPLETED` 状态 item;如果用户没有在批次面板审核或一键完成,分发可能没有条目。 -6. 数据分发使用 `dirs_exist_ok=True` 合并复制,不会自动清理目标旧内容。 -7. 生产提交和批次保存之间没有数据库级直接引用;生产侧通过 `Task_*` 目录和 `.dinsar_pair.json` sidecar 重新恢复 pair trace。 -8. 每个 production run 内部 item 串行执行;job worker 可并发,但 task_type 冲突检查和 engine lock 会限制同类引擎并发。 -9. `landsar` 已注册为 engine,但当前 `/dinsar-production/run` 仅对 SARscape、ISCE2、PyINT 建立 queued production 主链路。 - -## 17. 推荐排查路径 - -配对为空: - -1. 查 `GET /api/pairing/health` -2. 看 `pair_count`、`dirty_scene_count`、`status` -3. 必要时执行 `POST /api/pairing/reconcile-dirty` 或 `POST /api/pairing/rebuild-cache` -4. 放宽 `time_baseline_max`、`spatial_baseline_max_meters`、`overlap_threshold` -5. 检查 `insar_source_ready`、`require_orbit_data`、同卫星族、同视向、同模式、同极化约束 - -分发为空: - -1. 查 batch item 是否存在 -2. 查 item 状态是否命中 `copy_statuses`,默认只取 `COMPLETED` -3. 查 master/slave 源路径是否存在 -4. 查目标目录是否已有旧文件影响判断 - -生产未执行: - -1. 查 `system_tasks` 状态和 task logs -2. 查 `system_jobs` 是否 READY/RUNNING/FAILED -3. 查 worker heartbeat -4. 查 engine lock 是否被长任务持有 -5. 查生产根目录是否包含有效 `Task_*/master`、`Task_*/slave` -6. 对 PyINT 先跑输入资产预检 diff --git a/docs/DINSAR_SOURCE_BUNDLE_REVERSE_TOOL_TASK_20260511.md b/docs/DINSAR_SOURCE_BUNDLE_REVERSE_TOOL_TASK_20260511.md deleted file mode 100644 index 96cf9b0..0000000 --- a/docs/DINSAR_SOURCE_BUNDLE_REVERSE_TOOL_TASK_20260511.md +++ /dev/null @@ -1,168 +0,0 @@ -# D-InSAR 去重源数据包反向还原工具任务书 - -日期:2026-05-11 - -## 背景 - -本系统新增“去重源数据包”分发模式。该模式不直接生成每个干涉对的 `Task_*` 目录,而是只分发唯一源影像、唯一精密轨道文件和配对关系文件,减少外部分发时的重复复制量。 - -反向还原工具由任务接收方本地运行,将去重源数据包还原为传统 D-InSAR `Task_*` 目录结构。 - -## 输入目录结构 - -```text -BundleRoot/ - data/ - scene__/ - ... - orbit/ - orbit__.txt - ... - pairs.json - manifest.json -``` - -`orbit/` 可能不存在,或 `pairs.json` 内某些配对的轨道字段为空。 - -## 输出目录结构 - -```text -OutputRoot/ - Task_YYYYMMDD_YYYYMMDD/ - master/ - - slave/ - - orbit/ - - .dinsar_pair.json -``` - -输出目录名称优先使用 `pairs.json` 内的 `task_alias`,若为空则使用 `task_name`,再为空则使用 `pair_id`。 - -## pairs.json 关键字段 - -```json -{ - "schema": "dinsar_source_bundle_pairs.v1", - "exported_at": "2026-05-11T00:00:00Z", - "pairs": [ - { - "pair_id": "pair_0001", - "identity_key": "uid:", - "task_name": "Task_20250101_20250113", - "task_alias": "Task_20250101_20250113", - "master_source_path": "D:/Source/master", - "slave_source_path": "D:/Source/slave", - "master_scene_id": "scene_0001", - "slave_scene_id": "scene_0002", - "master_data": "data/scene_xxx_master", - "slave_data": "data/scene_yyy_slave", - "master_orbit_id": "orbit_0001", - "slave_orbit_id": "orbit_0002", - "master_orbit_source_path": "D:/Orbit/master.EOF", - "slave_orbit_source_path": "D:/Orbit/slave.EOF", - "master_orbit": "orbit/orbit_xxx.txt", - "slave_orbit": "orbit/orbit_yyy.txt", - "master_imaging_date": "20250101", - "slave_imaging_date": "20250113", - "time_baseline_days": 12 - } - ] -} -``` - -## 本系统分发续跑规则 - -去重源数据包支持向同一个 `BundleRoot` 多次分发: - -- 每次启动时先读取目标目录内已有的 `pairs.json` 和 `manifest.json`。 -- 已导出的 pair 通过 `identity_key`、`scene_pair_uid/pair_uid`、`pair_key`、`network_run_id + network_edge_id`、`master/slave_source_path` 或 `master_data + slave_data` 识别。 -- 开启“每次最多追加新配对”时,系统会先跳过已导出的 pair,再从剩余 pair 中取下一批追加;例如 500 个 pair 第一次限制 100,第二次同一目录仍限制 100 时,会追加第 101-200 个未导出的 pair。 -- `data/` 和 `orbit/` 按源路径哈希命名,已有文件或目录在 `skip_existing` 开启时不会重复复制。 -- `pairs.json` 和 `manifest.json` 采用临时文件写入后原子替换,避免中途失败留下半写 JSON。 - -注意:如果用户手动删除了 `pairs.json`,系统无法再根据记录判断哪些 pair 已经分发,只能根据重新生成的 `data/` 路径做源数据级去重,pair 级续跑能力会丢失。 - -## 还原规则 - -1. 读取 `pairs.json`。 -2. 对每个 pair 创建目标 `Task` 目录。 -3. 将 `master_data` 指向的数据复制到 `Task/master/`。 -4. 将 `slave_data` 指向的数据复制到 `Task/slave/`。 -5. 如 `master_orbit` / `slave_orbit` 存在,将轨道文件复制到 `Task/orbit/`。 -6. 生成 `.dinsar_pair.json`,至少保留: - - `pair_id` - - `identity_key` - - `task_name` - - `task_alias` - - `master_scene_id` - - `slave_scene_id` - - `master_data` - - `slave_data` - - `master_orbit_id` - - `slave_orbit_id` - - `master_orbit` - - `slave_orbit` - - `master_imaging_date` - - `slave_imaging_date` - - `time_baseline_days` - - `restored_at` -7. 每个 Task 应采用临时目录还原,全部成功后再重命名为最终目录,避免半成品。 - -## 覆盖策略 - -工具应提供参数: - -- `--skip-existing`:默认开启。若目标 `Task/master` 和 `Task/slave` 均存在且非空,则跳过。 -- `--overwrite`:删除并重建已存在的目标 Task。 -- `--limit N`:最多还原 N 个 pair,便于分批执行。 -- `--dry-run`:只打印计划,不复制。 - -`--skip-existing` 与 `--overwrite` 同时出现时应报错。 - -## 校验要求 - -启动前: - -- 检查 `pairs.json` 是否存在且可解析。 -- 检查 `data/` 是否存在。 -- 检查每个 pair 的 `master_data` / `slave_data` 是否存在。 -- 轨道缺失不应阻断还原,但要记录 warning。 - -还原后: - -- `Task/master/` 非空。 -- `Task/slave/` 非空。 -- `.dinsar_pair.json` 存在。 - -## 日志与报告 - -工具结束后输出 `restore_report.json`: - -```json -{ - "started_at": "...", - "finished_at": "...", - "input_root": "...", - "output_root": "...", - "total_pairs": 20, - "restored": 18, - "skipped": 2, - "failed": 0, - "warnings": [] -} -``` - -同时建议输出人类可读日志 `restore.log`。 - -## 建议实现 - -建议使用 Python 3.10+: - -- `argparse` 处理命令行参数。 -- `pathlib.Path` 处理路径。 -- `shutil.copytree(..., dirs_exist_ok=True)` / `shutil.copy2()` 处理复制。 -- Windows 下注意长路径和权限异常。 - -该工具不需要连接本系统数据库,也不需要调用本系统 API。 diff --git a/docs/DOCUMENTATION_GOVERNANCE.md b/docs/DOCUMENTATION_GOVERNANCE.md index c8f4044..678d704 100644 --- a/docs/DOCUMENTATION_GOVERNANCE.md +++ b/docs/DOCUMENTATION_GOVERNANCE.md @@ -1,6 +1,6 @@ # 文档治理约定 -最后更新:2026-04-25 +最后更新:2026-05-28 ## 1. 目标 @@ -41,8 +41,8 @@ - `*_EXPERIMENT_*` - 临时工作笔记 -6. `docs/archive/` - 默认只作为历史参考,不作为现行事实来源。 +6. 已删除历史材料 + 旧 archive、实验记录和阶段计划已从当前文档树移除;需要追溯时使用 Git 历史。 ## 3. 文档分类 @@ -68,7 +68,7 @@ ### 3.3 历史文档 -满足以下任一条件时,应优先考虑归档到 `docs/archive/`: +满足以下任一条件时,应优先考虑删除;确需保留时才放入专门的历史目录: - 已被更新文档明确取代 - 描述的是已结束阶段 @@ -91,20 +91,9 @@ `docs/` 根目录用于放当前仍有导航价值的正式文档。 -### 4.3 `docs/archive/` +### 4.3 历史材料 -`docs/archive/` 用于放: - -- 已退役设计 -- 已完成修复的过程文档 -- 旧版方案 -- 只保留追溯价值的材料 - -当归档材料数量继续增长时,应维护: - -- `docs/archive/INDEX.md` - -用于提供历史文档的结构化入口。 +默认不再长期堆积 `docs/archive/`。被当前实现取代的设计、计划、TODO 和实验记录应删除,让 Git 历史承担追溯职责。只有外部审计、交付或合规确实要求保留的历史材料,才单独建立历史目录并维护索引。 ## 5. 命名规则 @@ -148,7 +137,7 @@ - 新的正式文档进入 `docs/` 根目录时,必须决定是否加入 `docs/INDEX.md` - 文档失效时,必须从“当前有效”区移除 -- 被取代文档如仍需保留,应移动到 `docs/archive/` +- 被取代文档默认删除;确需保留时必须说明保留原因并同步索引 ## 7. 语言与编码规则 @@ -190,6 +179,6 @@ 后续文档清理建议按以下顺序推进: 1. 保持 `README.md` 与 `docs/INDEX.md` 为主导航入口 -2. 将明确失效的阶段性文档继续迁入 `docs/archive/` +2. 删除明确失效的阶段性文档 3. 收紧 `docs/` 根目录,只保留当前仍需导航的文档 4. 逐步减少无明确角色的过程型文件 diff --git a/docs/FLOOD_MODULE_REFACTOR_PLAN_20260514.md b/docs/FLOOD_MODULE_REFACTOR_PLAN_20260514.md deleted file mode 100644 index 4cc96df..0000000 --- a/docs/FLOOD_MODULE_REFACTOR_PLAN_20260514.md +++ /dev/null @@ -1,693 +0,0 @@ -# 洪涝监测模块整改实施方案 - -> 日期:2026-05-14 -> 目标:将当前分散的水体监测、洪涝检测、GF3 处理和结果管理,收敛为可部署、可维护、可扩展的洪涝灾害分析流水线。 - -## 1. 总体原则 - -1. 新业务统一走 `/flood/*`。 -2. “水体监测”不再作为独立业务模块出现,只作为“洪涝灾害分析 -> 水体提取”步骤存在。 -3. `/water/*` 暂时保留兼容窗口,但标记 deprecated,不再新增功能。 -4. 数据库优先新增表和回填数据,避免直接破坏旧表。 -5. 每个阶段独立提交,保证任一阶段都能构建、部署和回滚。 - -## 2. 目标流水线 - -```text -场景准备 Scene - -> 水体提取 WaterExtraction - -> 洪涝检测 FloodDetection - -> 套合分析 FloodOverlay - -> 洪涝产品 FloodProduct / Report -``` - -其中水体提取是洪涝分析的前置步骤,不再和洪涝灾害分析并列成两个业务入口。 - -## 3. 阶段 1:收敛 API 契约 - -目标:先消灭“前端调用不存在接口”的问题,避免部署后出现静默 404。 - -修改范围: - -```text -frontend/src/api/flood.js -backend/app/routers/flood.py -docs/FLOOD_MODULE_REFACTOR_PLAN_20260514.md -``` - -处理方式: - -- `frontend/src/api/flood.js` 只暴露后端当前真实支持的接口。 -- 暂时移除或注释未实现接口,例如 `/flood/sources`、`/flood/ready-products`、`/flood/pairs` 保存删除、`/flood/reports`、`/flood/results`。 -- 后端 `/flood` 路由继续保留现有功能,但接口命名统一为 `preprocess`、`scenes`、`water-extractions`、`pairs/search`、`detections`、`detections/{id}/preview/{layer}`。 - -验收标准: - -```text -npm run build 通过 -前端 flood API 文件中没有明显会 404 的已导出函数 -/flood 主流程现有功能不退化 -``` - -## 4. 阶段 2:新增数据模型 - -目标:建立洪涝流水线需要的数据承载,不直接破坏旧表。 - -新增模型: - -```text -WaterExtractionORM -> water_extractions -FloodOverlayORM -> flood_overlays -FloodProductORM -> flood_products -``` - -关键取舍: - -- 保留旧 `WaterDetectionORM -> water_detections`。 -- 新表 `water_extractions` 从旧表回填。 -- 后续新任务写入 `water_extractions`。 -- 旧接口读旧表或兼容映射,等稳定后再清理。 - -建议字段: - -```text -water_extractions: - id - scene_id - processor - task_id - input_path - output_path - preview_path - vector_path - water_area_km2 - water_pixel_count - threshold_value - metadata_json - status - error_msg - created_at - updated_at - -flood_overlays: - id - detection_id - flood_vector_path - hazard_points_hit - hazard_points_near - hazard_points_total - dinsar_products_intersecting - affected_area_km2 - summary_json - created_at - -flood_products: - id - product_id - detection_id - overlay_id - display_name - status - publish_dir - manifest_path - summary_json - created_at -``` - -验收标准: - -```text -alembic upgrade head 成功 -旧 water_detections 数据可迁移到 water_extractions -模型 import 正常 -``` - -## 5. 阶段 3:抽离 Service - -目标:让 `/flood` 成为真正的业务路由,而不是代理 `water.py` 的壳。 - -新增服务: - -```text -backend/app/services/flood_analysis_service.py -backend/app/services/water_extraction_service.py -backend/app/services/flood_product_service.py -backend/app/services/flood_overlay_service.py -``` - -职责划分: - -```text -flood_analysis_service.py - 场景列表、预处理提交、配对搜索、洪涝检测提交、检测列表 - -water_extraction_service.py - Otsu 水体提取、ENVI/SARscape 水体提取 - -flood_product_service.py - 产品列表、manifest、产品包生成 - -flood_overlay_service.py - 分类栅格矢量化、灾害点/DInSAR/AOI 套合 -``` - -完成后: - -- `backend/app/routers/flood.py` 不再 `import water as water_compat`。 -- `backend/app/routers/water.py` 标记 deprecated,后续可反向调用新 service。 -- `water_detect_service.py` 逐步迁移到 `water_extraction_service.py`。 - -验收标准: - -```text -flood.py 不再 import water.py -/water/* 旧接口仍可用 -/flood/* 主接口可用 -``` - -## 6. 阶段 4:补齐产品端点 - -目标:让“结果与任务”视图有真实后端数据。 - -主接口建议使用 `products`,避免和 DInSAR result/product 概念混淆: - -```text -POST /flood/detections/{id}/products -GET /flood/products -GET /flood/products/{id} -GET /flood/products/{id}/manifest -``` - -兼容别名可选: - -```text -GET /flood/results -GET /flood/results/{id} -GET /flood/results/{id}/manifest -``` - -验收标准: - -```text -前端结果视图能展示真实产品 -manifest 能返回 JSON -没有空接口或静默失败 -``` - -## 7. 阶段 5:实现套合分析 - -目标:把洪涝模块从检测工具提升为灾害分析模块。 - -新增接口: - -```text -POST /flood/detections/{id}/overlay -GET /flood/detections/{id}/impact -``` - -处理逻辑: - -```text -1. 读取 flood_detections.classified_path -2. 提取 class=2 洪涝区域 -3. 栅格转矢量 -4. 写 flood_overlays.flood_vector_path -5. 查询灾害点命中 -6. 查询近邻风险点 -7. 查询相交 DInSAR 产品 -8. 查询 AI 诊断摘要 -9. 写 summary_json -``` - -注意: - -- AOI、PostGIS、DInSAR 产品几何不完整时,不让整个任务失败。 -- 返回部分结果,并在 `summary_json.warnings` 中说明缺失项。 - -验收标准: - -```text -POST overlay 能生成 flood_overlays 记录 -GET impact 返回结构化 JSON -没有数据时返回空数组而不是 500 -``` - -## 8. 阶段 6:前端重整 - -目标:只保留一个洪涝灾害分析工作台。 - -新增目录: - -```text -frontend/src/components/flood/ -``` - -建议组件: - -```text -FloodStatusBadge.jsx -FloodButton.jsx -FloodSceneRow.jsx -FloodWaterExtractionRow.jsx -FloodDetectionRow.jsx -FloodOverlayPanel.jsx -FloodProductPanel.jsx -``` - -界面改成四站式: - -```text -1. 场景准备 -2. 水体提取 -3. 洪涝检测 -4. 套合与产品 -``` - -处理方式: - -- `WaterMonitorPanel.jsx` 加弃用提示。 -- 左侧导航只引导用户进入洪涝灾害分析。 -- `api/water.js` 和 `api/gf3.js` 标记 deprecated。 -- 新功能只接入 `api/flood.js`。 - -验收标准: - -```text -左侧导航只引导用户进入洪涝灾害分析 -水体提取不再作为独立业务重复出现 -套合分析和产品视图接真实接口 -npm run build 通过 -``` - -## 9. 阶段 7:报告生成壳 - -目标:先形成 Markdown 报告能力,再扩展 PDF。 - -新增接口: - -```text -POST /flood/reports -GET /flood/reports/{id} -``` - -第一版报告包含: - -```text -灾前/灾后场景信息 -洪涝面积 -灾害点命中 -DInSAR 产品关联 -AI 诊断摘要 -套合统计 -``` - -## 10. 推荐提交顺序 - -```text -1. refactor flood api contract -2. add flood pipeline orm models -3. extract flood analysis services -4. add flood product endpoints -5. implement flood overlay impact analysis -6. consolidate flood frontend workspace -7. scaffold flood report generation -``` - -核心思路:先稳住接口和数据,再拆服务,再补业务能力,最后整理 UI。这样 Git 中的代码、迁移、前端调用和部署环境是闭合的,另一台服务器拉取后不会出现关键页面依赖未实现接口的问题。 - -## 11. 2026-05-15 补充设计:水体提取、灾害配对与套合展示 - -本节根据最新审阅意见补充。结论:洪涝工作台不应该继续以“手动选日期范围 + 查配对”为核心,而应该改成“灾害事件驱动”: - -```text -灾害时间 + 灾害位置 - -> 过滤可用 SAR 场景匹配池 - -> 推荐灾前/灾后配对 - -> 执行洪涝检测 - -> 套合分析 - -> 结果展示与产品导出 -``` - -### 11.1 水体提取视图复用管理页能力 - -当前管理页面已经具备三类能力,洪涝模块应复用,而不是重新做一套弱化版: - -1. 雷达数据查询能力:`/radar-data/search` 已支持成像日期、成像模式、极化方式、产品级别、行政区 AOI、上传 AOI 文件。 -2. 地图能力:`App.jsx` 中已经有源影像 footprint 上图、源影像预览缓存 `radar-data/{id}/thumb`、地图定位和图层开关逻辑。 -3. 行政区 AOI 能力:`/aoi/regions/children` 与 `/aoi/regions/{treeId}/geometry` 已可用于按省/市范围查询和定位。 - -因此水体提取界面调整为: - -```text -左侧:场景匹配池 - - 灾害位置 / AOI - - 成像日期范围 - - 卫星、模式、极化、产品级别 - - 只显示有 footprint 的数据 - - 可显示源影像预览图 - - 显示成像时间、极化方式、成像模式、产品级别 - -右侧:已完成地理编码场景 / 水体提取结果 - - 场景 footprint 上图 - - 水体提取掩膜上图 - - 与源影像预览可叠加对比 -``` - -前端复用建议: - -```text -复用 buildRadarSearchFormData / normalizeRadarSearchCriteria -复用 UnifiedDatePicker -复用 RadarDataRow 的预览状态表达 -复用 App.jsx 中 updateRadarPreviewVisibility 的源影像预览图层逻辑 -复用行政区 AOI 选择和地图定位逻辑 -``` - -需要新增的洪涝专用组件: - -```text -FloodSourceSearchPanel.jsx - 封装灾害位置、日期、极化、模式、产品级别过滤。 - -FloodSourceSceneRow.jsx - 显示源影像:成像时间、极化方式、模式、产品级别、预览状态、上图按钮。 - -FloodWaterExtractionRow.jsx - 显示水体提取结果:面积、状态、输入场景、上图按钮、错误信息。 -``` - -地图图层约定: - -```text -source_preview:{radar_data_id} 源影像预览 -source_footprint:{radar_data_id} 源影像覆盖范围 -scene_footprint:{scene_id} 地理编码场景范围 -water_mask:{water_extraction_id} 水体提取掩膜 -``` - -### 11.2 洪涝检测配对改为灾害事件驱动 - -当前 `/flood/pairs/search` 只接收灾前/灾后日期区间和 overlap 阈值,实际使用体验不够:用户通常知道的是“灾害发生时间”和“灾害位置”,不是一开始就知道灾前灾后影像窗口。 - -新的配对入口应改为: - -```text -灾害名称 disaster_name 可选 -灾害时间 disaster_date 必填 -灾害位置 disaster_aoi 必填,来自行政区 / 地图框选 / 上传 SHP / GeoJSON -灾前窗口 pre_window_days 默认 30 天 -灾后窗口 post_window_days 默认 30 天 -最小 AOI 覆盖率 min_aoi_coverage_ratio 默认 0.3 -最小两景重叠率 min_pair_overlap_ratio 默认 0.5 -是否要求同极化 require_same_polarization 默认 true -是否要求同成像模式 require_same_imaging_mode 默认 false -卫星过滤 satellites 可选 -极化过滤 polarization 可选 -``` - -前端交互: - -```text -1. 用户选择灾害日期 -2. 用户选择灾害位置 - - 行政区:省 / 市 - - 地图框选:后续实现 - - 上传 SHP/GeoJSON:沿用现有 AOI 解析 -3. 系统自动推导: - - 灾前窗口:disaster_date - pre_window_days 至 disaster_date - 1 - - 灾后窗口:disaster_date 至 disaster_date + post_window_days -4. 后端返回: - - pre_pool - - post_pool - - candidate_pairs - - warnings -5. 用户在候选配对中选择一组提交洪涝检测 -``` - -推荐新增接口: - -```text -POST /flood/disaster-pairs/search -``` - -请求格式: - -```json -{ - "disaster_name": "汶川洪涝", - "disaster_date": "20260715", - "aoi_geojson": {}, - "region_tree_id": "510000", - "pre_window_days": 30, - "post_window_days": 30, - "min_aoi_coverage_ratio": 0.3, - "min_pair_overlap_ratio": 0.5, - "require_same_polarization": true, - "require_same_imaging_mode": false, - "satellites": ["LT-1", "GF3"], - "polarization": "VV" -} -``` - -返回格式: - -```json -{ - "disaster": { - "name": "汶川洪涝", - "date": "20260715", - "pre_range": ["20260615", "20260714"], - "post_range": ["20260715", "20260814"] - }, - "aoi": { - "source": "region", - "name": "汶川县", - "aoi_geojson": {} - }, - "pre_pool": [], - "post_pool": [], - "candidate_pairs": [ - { - "pre": { - "scene_id": 1, - "radar_data_id": 10, - "imaging_date": "20260701", - "satellite": "LT-1", - "polarization": "VV", - "imaging_mode": "SM", - "aoi_coverage_ratio": 0.82, - "coverage_polygon": [] - }, - "post": { - "scene_id": 2, - "radar_data_id": 11, - "imaging_date": "20260718", - "satellite": "LT-1", - "polarization": "VV", - "imaging_mode": "SM", - "aoi_coverage_ratio": 0.79, - "coverage_polygon": [] - }, - "pair_overlap_ratio": 0.91, - "pre_delta_days": 14, - "post_delta_days": 3, - "score": 0.86, - "warnings": [] - } - ], - "warnings": [] -} -``` - -排序建议: - -```text -score = - pair_overlap_ratio * 0.35 - + min(pre_aoi_coverage, post_aoi_coverage) * 0.30 - + time_score * 0.20 - + same_polarization_bonus * 0.10 - + same_mode_bonus * 0.05 -``` - -后端实现建议: - -```text -1. 复用 radar search 的 AOI 解析能力,避免重复解析行政区和 SHP。 -2. 查询 sar_scene_geo.status=DONE 的场景,并 join radar_data。 -3. 按 disaster_date 自动构造灾前/灾后窗口。 -4. 用 PostGIS 计算单景 AOI 覆盖率。 -5. 用 footprint 相交面积计算 pair_overlap_ratio。 -6. 返回匹配池和候选配对,而不是只返回最终 pairs。 -``` - -兼容处理: - -```text -旧接口 POST /flood/pairs/search 保留,但只作为手动日期模式。 -新工作台默认使用 POST /flood/disaster-pairs/search。 -``` - -### 11.3 套合分析结果展示逻辑 - -套合分析不能只是一个“运行按钮”。它应展示“为什么这个洪涝结果重要”: - -```text -洪涝范围 -灾害点命中 -近洪涝风险点 -DInSAR 产品关联 -行政区影响统计 -结果图层 -产品/报告入口 -``` - -前端 `FloodOverlayPanel.jsx` 设计: - -```text -顶部摘要: - - 洪涝面积 - - 命中灾害点数量 - - 近洪涝风险点数量 - - 关联 DInSAR 产品数量 - - warnings 数量 - -中部地图控制: - - 洪涝分类图 - - 洪涝矢量范围 - - 命中灾害点 - - 近洪涝风险点 - - DInSAR 产品 footprint - -下部结果表: - - 灾害点列表:名称、类型、行政区、距离、是否命中 - - DInSAR 产品列表:product_id、engine、形变量、AI 风险等级、预览/详情 - - AOI 统计:行政区、洪涝面积、占比 -``` - -后端 `GET /flood/detections/{id}/impact` 应保证即使没有运行套合,也返回稳定结构: - -```json -{ - "detection_id": 1, - "flood_area_km2": 12.5, - "hazard_points": { - "inside_flood": [], - "near_flood": [], - "total_in_scene": 0 - }, - "dinsar_products": [], - "affected_aois": [], - "map_layers": { - "classified_preview": true, - "flood_vector_path": null, - "hazard_points": true, - "dinsar_footprints": true - }, - "warnings": [] -} -``` - -需要新增或完善的地图层约定: - -```text -flood_classified:{detection_id} 洪涝分类栅格预览 -flood_vector:{overlay_id} 洪涝矢量面 -hazard_inside:{overlay_id} 洪涝范围内灾害点 -hazard_near:{overlay_id} 近洪涝风险点 -dinsar_intersect:{overlay_id} 相交 DInSAR 产品 footprint -``` - -### 11.4 前端整改顺序调整 - -基于以上补充,前端整改顺序调整为: - -```text -1. 抽 FloodSourceSearchPanel,复用管理页雷达搜索/AOI 查询。 -2. 水体提取页增加源影像预览图、成像时间、极化方式、模式、产品级别。 -3. 洪涝检测页改为灾害事件输入:灾害时间 + 灾害位置。 -4. 新增 /flood/disaster-pairs/search 后接入候选池和推荐配对。 -5. 套合分析页接入 /flood/detections/{id}/overlay 和 /impact。 -6. 结果与任务页从 flood_detections 过渡到 flood_products。 -``` - -验收标准补充: - -```text -水体提取: - - 能按行政区/上传 AOI 查询源影像 - - 能显示源影像预览图 - - 列表中显示成像日期、极化方式、成像模式、产品级别 - - 水体提取结果可叠加到地图 - -洪涝检测: - - 用户只需输入灾害时间和灾害位置即可获得匹配池 - - 返回 pre_pool/post_pool/candidate_pairs - - 推荐配对可在地图上预览灾前/灾后 footprint - - 配对结果显示时间差、AOI 覆盖率、两景重叠率、极化一致性 - -套合分析: - - 运行后能看到摘要指标 - - 能看到灾害点命中和近邻风险点列表 - - 能看到关联 DInSAR 产品列表 - - warnings 可见,不静默失败 -``` - -## 12. 2026-05-15 第一轮落地记录 - -本轮先闭合三条可直接使用的业务链路,不把报告生成和完整产品包导出放进同一次改动。 - -### 12.1 已落地 - -后端: -```text -1. 新增 POST /flood/disaster-pairs/search。 - 输入 disaster_date + region_tree_id/aoi_geojson + 灾前灾后窗口。 - 输出 pre_pool、post_pool、candidate_pairs、summary、warnings。 - -2. 水体提取列表补充源影像元数据。 - /flood/scenes 与 /flood/water-extractions 现在返回 imaging_date、polarization、 - imaging_mode、product_level、coverage_polygon、min/max lon/lat。 - -3. 套合分析接口保持并接入前端。 - POST /flood/detections/{id}/overlay - GET /flood/detections/{id}/impact -``` - -前端: -```text -1. 洪涝灾害分析 / 水体提取页: - - 复用 AOI 行政区索引。 - - 支持按行政区筛选雷达源影像。 - - 雷达结果行增加“覆盖”和“源影像”上图。 - - 场景/水体结果行显示成像日期、极化方式、模式、产品级别。 - -2. 洪涝检测页: - - 从手工填写灾前/灾后日期,改为输入灾害名称、灾害发生日期、灾害位置。 - - 自动根据窗口期生成灾前池和灾后池。 - - 候选配对展示评分、AOI 覆盖率、两景重叠率、灾前/灾后时间差、极化方式。 - -3. 套合分析页: - - 可运行 overlay。 - - 可刷新并展示 impact。 - - 展示洪涝面积、命中灾害点、近洪涝风险点、关联 DInSAR 产品和 warnings。 -``` - -### 12.2 当前保留的兼容点 - -```text -1. GET/POST /water/* 暂未删除,旧页面仍可过渡使用。 -2. 旧 POST /flood/pairs/search 保留为手工日期兼容接口,但新工作台默认使用 /flood/disaster-pairs/search。 -3. 水体提取任务底层仍写 water_detections;water_extractions 新表和正式迁移已建模,但任务处理器尚未完全切换。 -4. AOI 只先复用行政区查询;上传 SHP/GeoJSON 到洪涝工作台可作为下一步补充。 -``` - -### 12.3 下一轮建议 - -```text -1. 把水体提取 job handler 从 WaterDetectionORM 切到 WaterExtractionORM。 -2. 给候选配对增加地图 footprint 预览按钮,辅助人工确认灾前/灾后覆盖。 -3. 把 overlay 生成的 flood_vector_path 也作为地图矢量层显示。 -4. 套合分析补行政区受影响面积统计。 -5. 结果与任务页开始接 flood_products,而不是只列 flood_detections。 -``` diff --git a/docs/FLOOD_MODULE_REFACTOR_PROGRESS_20260515.md b/docs/FLOOD_MODULE_REFACTOR_PROGRESS_20260515.md deleted file mode 100644 index d047fb8..0000000 --- a/docs/FLOOD_MODULE_REFACTOR_PROGRESS_20260515.md +++ /dev/null @@ -1,128 +0,0 @@ -# 洪涝模块整改进展记录 - -## 2026-05-15 第二轮:水体提取表切换 - -本轮目标是把洪涝工作台里的水体提取正式从旧 `water_detections` -迁到新 `water_extractions`,同时保留旧 `/water/*` 兼容窗口。 - -### 已完成 - -```text -1. 新增 backend/app/services/water_extraction_service.py。 - - run_otsu_water_extraction 复用旧 Otsu 实现。 - - 对外使用 extraction/processor/threshold_value 命名。 - - run_envi_water_extraction 暂留占位,后续接 ENVI/SARscape。 - -2. /flood/water-extractions 新提交任务开始写 water_extractions。 - - 任务 payload 使用 extraction_id。 - - WaterExtractionORM.processor 默认 otsu。 - - task_id、threshold_value、metadata_json 随任务更新。 - -3. WATER_DETECT job handler 支持双轨兼容。 - - 新 payload: extraction_id -> 写 water_extractions。 - - 旧 payload: detection_id -> 继续写 water_detections。 - - 如果旧 detection_id 已经被 alembic 0003 回填到 water_extractions, - handler 会同步更新同 ID 的新表记录,避免迁移期状态卡住。 - -4. /flood/water-extractions 列表改读 water_extractions。 - - 旧数据依赖 alembic 0003 从 water_detections 回填。 - - preview 优先读 WaterExtractionORM,找不到时回退 WaterDetectionORM。 -``` - -### 仍保留 - -```text -1. 旧 /water/detect 仍创建 water_detections。 - 这是兼容窗口内的刻意保留,不再作为洪涝工作台主链路。 - -2. ENVI/SARscape 精密水体提取还没有接入任务队列。 - -3. 如果部署环境已有旧 PENDING water_detections 任务, - 需要先执行 alembic 0003,再启动 worker。 -``` - -### 下一步 - -```text -1. 给候选洪涝配对增加灾前/灾后 footprint 地图预览。 -2. 把 flood_vector_path 作为矢量图层上图。 -3. 套合分析补行政区受影响面积统计。 -4. 结果与任务页接 flood_products。 -``` - -## 2026-05-15 第三轮:地图闭环与产品页 - -### 已完成 - -```text -1. 候选洪涝配对支持地图预览。 - - 灾前 footprint 蓝色。 - - 灾后 footprint 绿色。 - - 配对行增加“预览覆盖”。 - -2. 套合结果支持洪涝矢量上图。 - - GET /flood/detections/{id}/impact 返回 overlay_id、flood_vector_path、flood_vector_geojson。 - - 前端套合分析页增加“加载洪涝矢量”。 - -3. 套合分析补行政区影响统计。 - - overlay 运行时读取 AOI 行政区边界索引。 - - 返回 affected_aois,包含 tree_id、name、level、flood_area_km2。 - - 前端展示影响行政区数量和前 5 个行政区。 - -4. 结果与任务页接 flood_products。 - - 洪涝结果行增加“生成产品”。 - - 结果页增加“产品”tab。 - - 产品行展示 product_id、洪涝面积、影响面积、生成时间。 - - Manifest 按钮读取 /flood/products/{id}/manifest。 -``` - -### 当前可集中测试的链路 - -```text -1. 水体提取: - 入库影像查询 -> 行政区筛选 -> 源影像上图 -> 提交预处理 -> 提交水体提取 -> 水体结果上图。 - -2. 洪涝检测: - 输入灾害时间 + 灾害位置 -> 推荐配对 -> 预览覆盖 -> 提交洪涝检测 -> 分类图上图。 - -3. 套合分析: - 选择 DONE 洪涝结果 -> 运行套合分析 -> 加载洪涝矢量 -> 查看灾害点、DInSAR、行政区统计。 - -4. 产品: - DONE 洪涝结果 -> 生成产品 -> 产品 tab 查看 -> 读取 manifest。 -``` - -## 2026-05-15 第四轮:GeoTIFF 化技术决策 - -本轮根据新的产品判断调整洪涝模块技术路线:不再把 ENVI/SARscape -作为洪涝算法主线,后续水体提取、洪涝检测、套合分析全部运行在标准 -GeoTIFF 层。原始 SAR 到 GeoTIFF 的部分做成可插拔前处理器,优先评估 -GAMMA,GAMMA 覆盖不了的传感器使用 GF3_GDAL、SNAP、ISCE 或 external -处理器兜底。 - -### 已完成 - -```text -1. 新增 docs/FLOOD_GEOTIFF_GAMMA_PREPROCESS_DESIGN_20260515.md。 - - 总结当前洪涝模块仍绑定 ENVI/SARscape 的真实状态。 - - 梳理当前 GAMMA/PyINT 在项目中的能力和边界。 - - 明确 GAMMA 适合作为 TIF 前处理候选,但不能把 D-InSAR pair profile - 直接复用为洪涝单景预处理。 - - 定义 analysis_ready.tif / SarAnalysisScene 数据契约。 - - 给出 Python GeoTIFF 洪涝检测和 GAMMA 单景前处理分阶段方案。 - -2. 更新 docs/INDEX.md。 - - 将 GeoTIFF 化设计列入当前执行中的核心设计。 - - 标明 2026-05-14 旧洪涝设计中的 ENVI/SARscape 主线表述已被新设计取代。 -``` - -### 当前判断 - -```text -1. 洪涝算法应先从 SARscape 黑盒迁出,改成 pre/post GeoTIFF 上的 Python 算法。 -2. GAMMA 适合优先做 LT-1/Sentinel-1 的 analysis_ready.tif 生产器。 -3. 当前 GAMMA 接入已有 geocode/data2geotiff 能力,但缺少独立单景预处理服务。 -4. GF3 已有 Python/GDAL L1A->L2 路线,不应强行改走 GAMMA。 -5. 另一台服务器部署时,Git 能提供平台代码和算法;GAMMA 本体、DEM、轨道池、 - conda/WSL 运行时仍是外部部署前提。 -``` diff --git a/docs/FRONTEND_NAVIGATION_ARCHITECTURE.md b/docs/FRONTEND_NAVIGATION_ARCHITECTURE.md index 6139f97..1cdd201 100644 --- a/docs/FRONTEND_NAVIGATION_ARCHITECTURE.md +++ b/docs/FRONTEND_NAVIGATION_ARCHITECTURE.md @@ -69,6 +69,7 @@ This is a workspace group, not a multi-tab planning tree. └─ 生产管理 (`production_management`) ├─ D-InSAR运行 (`dinsar_runs`) ├─ SBAS-InSAR Production (`sbas_insar_production`) + ├─ SBAS-InSAR结果 (`sbas_insar_products`) └─ D-InSAR产物 (`dinsar_products`) ``` diff --git a/docs/GAMMA_IPTA_LT1_SBAS_TRIAL_RUNBOOK_20260518.md b/docs/GAMMA_IPTA_LT1_SBAS_TRIAL_RUNBOOK_20260518.md deleted file mode 100644 index 3dca496..0000000 --- a/docs/GAMMA_IPTA_LT1_SBAS_TRIAL_RUNBOOK_20260518.md +++ /dev/null @@ -1,551 +0,0 @@ -# Gamma IPTA LT1 SBAS Trial Runbook - -Date: 2026-05-18 - -## Goal - -Validate whether the current local LT1 data pool and WSL Gamma installation can produce one usable SBAS/IPTA trial result with the official Gamma toolchain. - -This is not yet a production integration design. The immediate goal is to run one conservative stack, inspect failures and product quality, then decide what should be productized in the system. - -Actual trial root used in this repository: - -```text -D:\Code\Insar_management_system_v2\backend\runtime\gamma_ipta_trials\lt1b_r114_e1312_n438_20240516_20251002 -``` - -## Current Environment - -- WSL distro configured by the project: `Ubuntu-24.04`. -- Gamma install found in WSL: `/usr/local/GAMMA_SOFTWARE-20240627`. -- Installed package name present on disk: `GAMMA_SOFTWARE-20240627_MSP_ISP_DIFF_IPTA.linux64_ubuntu2404.tar.gz`. -- Installed Gamma modules include `MSP`, `ISP`, `DIFF`, `DISP`, and `IPTA`. -- `GEO` is not a separate directory in this install, but DIFF contains the relevant geocoding tools, including `gc_map`, `geocode`, and `geocode_back`. -- Project Gamma environment script: `deploy/wsl/profiles/gamma_env.sh`. -- Project WSL Python: `/home/administrator/miniconda3/envs/insar_wsl_v1/bin/python`. - -Important command checks already performed: - -- `IPTA/bin/ts_rate` runs and prints usage. No license-denied error observed. -- `IPTA/bin/multi_def_pt` runs and prints usage. No license-denied error observed. -- `ISP/bin/par_LT1_SLC` exists and prints LT1 SLC conversion usage. -- `ISP/scripts/LT1_precision_orbit.py` runs with the project conda Python and prints usage. - -## Data Pool Findings - -Configured LT1 source pool: - -- `D:\LuTan1_Image_Pool` - -Configured LT1 precise orbit pool: - -- `D:\orbit_pools\envi\LT1A` -- `D:\orbit_pools\envi\LT1B` - -High-level inventory: - -- LT1 scene directories found: `1500`. -- LT1A orbit TXT files found: `896`, spanning `20230508` to `20251219`. -- LT1B orbit TXT files found: `858`, spanning `20230510` to `20251219`. - -Metadata caveat: - -- `*.meta.xml` and `*_Check.xml` are not safe to parse as whole XML documents in PowerShell because many files contain malformed Chinese text near the tail, for example bad `usePreciseOrbit` closing text. -- The needed `productInfo` block is structurally valid. For stack discovery, parse only `...`. -- Gamma `par_LT1_SLC` should still use the original `.meta.xml`; do not rewrite source metadata unless a Gamma run proves the malformed tail is a blocker. - -## Candidate Stack - -First trial should use a narrow single-center stack, not the broad system time-series grouping. - -Recommended trial stack: - -- Satellite: `LT1B` -- Relative orbit: `114` -- Direction: `DESCENDING` -- Imaging mode: `STRIP1` -- Polarization: `HH` -- Approximate center: `E131.2 / N43.8` -- Scene count at this center: `7` -- Scenes with precise orbit TXT currently present: `5` - -Use the 5 scenes with available precise orbit first: - -| Date | Orbit TXT | Scene | -| --- | --- | --- | -| 20240516 | yes | `LT1B_MONO_SYC_STRIP1_012047_E131.2_N43.8_20240516_SLC_HH_S2A_0000399289` | -| 20240711 | yes | `LT1B_MONO_SYC_STRIP1_012880_E131.2_N43.8_20240711_SLC_HH_S2A_0000450956` | -| 20240905 | yes | `LT1B_MONO_SYC_STRIP1_013713_E131.2_N43.8_20240905_SLC_HH_S2A_0000501650` | -| 20250417 | yes | `LT1B_MONO_SYC_STRIP1_017045_E131.2_N43.8_20250417_SLC_HH_S2A_0000713375` | -| 20251002 | yes | `LT1B_MONO_SYC_STRIP1_019544_E131.2_N43.8_20251002_SLC_HH_S2A_0000891257` | - -Do not include these two in the first run unless the missing orbits are added: - -| Date | Orbit TXT | Scene | -| --- | --- | --- | -| 20250612 | no | `LT1B_MONO_SYC_STRIP1_017878_E131.2_N43.8_20250612_SLC_HH_S2A_0000772122` | -| 20250807 | no | `LT1B_MONO_SYC_STRIP1_018711_E131.2_N43.8_20250807_SLC_HH_S2A_0000831367` | - -Common bounding box across the broader 13-scene `LT1B relOrbit 114 / E131-N44` candidate: - -- lon: `130.8615 .. 131.1638` -- lat: `43.7127 .. 44.0987` - -For the narrow `E131.2/N43.8` trial stack, overlap is visually/metadata-wise much tighter: - -- Each scene center is around `131.20E, 43.79N`. -- Each scene bbox is roughly `130.81..131.62E`, `43.48..44.10N`. - -Secondary candidate if the first stack fails: - -- `LT1B relOrbit 114 DESCENDING STRIP1 HH`, center `E130.8/N43.9`. -- 5 dates, 4 with orbit: `20250425`, `20250620`, `20250815`, `20251010`. -- One scene is `MONO_MH1` while the others are `MONO_SYC`; keep it as secondary, not first choice. - -## Current System Pairing Limitations - -The current time-series stack selection is useful for broad discovery but is too coarse for Gamma IPTA production. - -Observed code behavior: - -- LT1A/LT1B are normalized into the same satellite family `LT1`. -- The compatibility key only uses direction, satellite family, imaging mode, and polarization. -- Relative orbit, absolute track family, scene center/strip identity, receiving station, and detailed LT1 product variant are not hard grouping keys. -- The stable-stack selector tries to recover by common AOI overlap and pairwise network connectivity. -- The SBAS network selector uses time-baseline, center-distance and overlap thresholds, but does not use Gamma-derived perpendicular baseline at planning time. -- Time-series processors currently accepted by the service are only `isce2_stack_mintpy` and `sarscape_sbas`; there is no `gamma_ipta_sbas` processor code yet. - -For the Gamma IPTA trial, do not rely on the current automatic PS stack plan as the source of truth. Use a manually audited stack manifest first. - -## Trial Run Checklist - -### 1. Create Isolated Work Directory - -Actual root: - -```text -D:\Code\Insar_management_system_v2\backend\runtime\gamma_ipta_trials\lt1b_r114_e1312_n438_20240516_20251002 -``` - -Keep these subdirectories: - -```text -input\scenes -input\orbits -gamma\slc -gamma\mli -gamma\diff -gamma\ipta -logs -publish -``` - -For the first trial, prefer symlinks or a manifest that points to source scenes. Avoid duplicating large TIFF files unless Gamma scripts require local flat layout. - -### 2. Build Scene Manifest - -For each selected scene, record: - -- scene directory -- `.tiff` -- `.meta.xml` -- precise orbit TXT -- date -- relative orbit -- direction -- mode -- polarization -- center lon/lat -- bbox - -This manifest becomes the hand-audited truth for the trial. - -### 3. Convert LT1 Products To Gamma SLC - -For each selected scene: - -```bash -source /mnt/d/Code/Insar_management_system_v2/deploy/wsl/profiles/gamma_env.sh -par_LT1_SLC .slc.par .slc -``` - -Then apply precise orbit: - -```bash -/home/administrator/miniconda3/envs/insar_wsl_v1/bin/python \ - /usr/local/GAMMA_SOFTWARE-20240627/ISP/scripts/LT1_precision_orbit.py \ - .slc.par -``` - -### 4. Build SLC/MLI Tables - -Create Gamma tables for the stack: - -```text -SLC_tab -RMLI_tab -``` - -Use multilook settings conservative enough for a first run. The goal is robustness and fast feedback, not final product resolution. - -### 5. Baseline And Pair Network - -Use Gamma baseline tools first, not the system's center-distance approximation: - -- `base_init` -- `base_perp` -- IPTA baseline tools such as `base_orbit_pt`, `base_par_pt`, `base_ls_pt` as needed by the official IPTA path. - -For the first 5-scene stack, start with a simple connected small-baseline network: - -- adjacent pairs by time -- add one or two skip pairs only if coherence and baseline look acceptable - -Expected adjacent temporal intervals: - -- `20240516 -> 20240711`: 56 days -- `20240711 -> 20240905`: 56 days -- `20240905 -> 20250417`: 224 days -- `20250417 -> 20251002`: 168 days - -The large seasonal gaps are acceptable for a trial only if perpendicular baseline and coherence are reasonable. If they are poor, switch to a denser 2025-only or 2024-only local test, even with fewer dates. - -### 6. Differential Interferograms - -Use official Gamma DIFF commands/scripts for: - -- coregistration -- interferogram generation -- simulated topographic phase -- differential phase -- filtering -- coherence -- unwrapping if needed by the chosen IPTA path - -Do not implement custom SBAS inversion in the management system. - -### 7. IPTA Processing - -Use Gamma IPTA commands for point/stack time-series processing. Confirm exact command sequence against the installed: - -```text -/usr/local/GAMMA_SOFTWARE-20240627/IPTA/html/IPTA_users_guide.pdf -``` - -Commands observed in the local IPTA module include: - -- `multi_def_pt` -- `ts_rate` -- `ts_rate_pt` -- `base_ls_pt` -- `base_par_pt` -- `ph_base_pt` -- `atm_mod_pt` -- `pt2geo` -- `dis_ipta` - -### 8. Review Outputs - -Minimum acceptance checks for the first run: - -- every selected scene converts to SLC -- precise orbit update succeeds for every SLC -- all intended pairs generate interferograms -- coherence is not uniformly poor -- unwrapping or IPTA point solution is not globally unstable -- one geocoded velocity or displacement-rate raster/vector product can be inspected -- logs and command manifests are complete enough to reproduce the run - -## Trial Progress On 2026-05-18 - -### Files Created For This Trial - -- `backend/runtime/gamma_ipta_trials/lt1b_r114_e1312_n438_20240516_20251002/input/scene_manifest.json` -- `backend/runtime/gamma_ipta_trials/lt1b_r114_e1312_n438_20240516_20251002/scripts/01_prepare_slc.sh` -- `backend/runtime/gamma_ipta_trials/lt1b_r114_e1312_n438_20240516_20251002/scripts/02_mli_and_baseline.sh` -- `backend/runtime/gamma_ipta_trials/lt1b_r114_e1312_n438_20240516_20251002/scripts/03_coreg_one_pair.sh` -- `backend/runtime/gamma_ipta_trials/lt1b_r114_e1312_n438_20240516_20251002/scripts/04_cc_stats.py` -- `backend/runtime/gamma_ipta_trials/lt1b_r114_e1312_n438_20240516_20251002/scripts/05_coreg_adjacent_pairs.sh` -- `backend/runtime/gamma_ipta_trials/lt1b_r114_e1312_n438_20240516_20251002/scripts/06_prepare_rdc_dem.sh` -- `backend/runtime/gamma_ipta_trials/lt1b_r114_e1312_n438_20240516_20251002/scripts/07_coreg_common_ref_stack.sh` -- `backend/runtime/gamma_ipta_trials/lt1b_r114_e1312_n438_20240516_20251002/scripts/08_diff_unwrap_common_ref.sh` -- `backend/runtime/gamma_ipta_trials/lt1b_r114_e1312_n438_20240516_20251002/scripts/09_mb_ts_rate.sh` -- `backend/runtime/gamma_ipta_trials/lt1b_r114_e1312_n438_20240516_20251002/scripts/10_float_stats.py` -- `backend/runtime/gamma_ipta_trials/lt1b_r114_e1312_n438_20240516_20251002/scripts/11_make_timeseries_previews.sh` -- `backend/runtime/gamma_ipta_trials/lt1b_r114_e1312_n438_20240516_20251002/scripts/12_geocode_export_timeseries.sh` -- `backend/runtime/gamma_ipta_trials/lt1b_r114_e1312_n438_20240516_20251002/scripts/13_phase_to_los.py` -- `backend/runtime/gamma_ipta_trials/lt1b_r114_e1312_n438_20240516_20251002/scripts/14_build_trial_summary.py` -- `backend/runtime/gamma_ipta_trials/lt1b_r114_e1312_n438_20240516_20251002/scripts/15_make_los_velocity_maps.sh` -- `backend/runtime/gamma_ipta_trials/lt1b_r114_e1312_n438_20240516_20251002/scripts/16_plot_monitor_point_timeseries.py` -- `backend/runtime/gamma_ipta_trials/lt1b_r114_e1312_n438_20240516_20251002/scripts/17_make_geocoded_web_previews.sh` - -### Completed - -- Created a hand-audited 5-scene LT1B manifest for `relOrbit 114 / DESCENDING / STRIP1 / HH / E131.2 N43.8`. -- Converted all 5 LT1 TIFF products with Gamma `par_LT1_SLC`. -- Applied precise orbit updates with Gamma `LT1_precision_orbit.py`. -- Built `gamma/slc/SLC_tab`. -- Built 8x8 multilooked MLI products with Gamma `multi_look`. -- Built `gamma/mli/RMLI_tab`. -- Generated all-pair and adjacent-pair baseline tables with Gamma `base_calc`. -- Ran adjacent-pair coregistration for all 4 selected adjacent pairs using Gamma `SLC_coreg.py`. -- Generated 4 interferograms and coherence products using Gamma `create_offset`, `SLC_intf`, and `cc_wave`. -- Generated browse previews using Gamma `rasmph` and `raspwr`. -- Computed coherence statistics for each adjacent pair. -- Reused the existing Copernicus30 DEM cache covering `E131.2/N43.8`. -- Generated a reference-geometry RDC DEM for `20240905` using Gamma `gc_map1`, `geocode`, and `gc_map_fine`. -- Built a common-reference RSLC/RMLI stack in `20240905` geometry. -- Generated common-reference differential interferograms with Gamma `phase_sim_orb` and `SLC_diff_intf`. -- Filtered common-reference differential interferograms with Gamma `adf`. -- Unwrapped common-reference differential interferograms with Gamma `mcf`. -- Ran Gamma IPTA `mb` to solve the image-based phase time-series. -- Ran Gamma IPTA `ts_rate` to estimate a linear phase-rate map. -- Generated Gamma preview rasters for `ts_rate`, `sigma_rate`, and `hgt_correction`. -- Exported phase-rate, sigma, height-correction, and LOS-rate rasters to EPSG:4326 GeoTIFF. -- Generated explicit LOS velocity maps in `mm/year` for both away-from-radar-positive and toward-radar-positive conventions. -- Generated one example monitoring-point LOS displacement curve as PNG, CSV, and metadata JSON. -- Generated north-up WGS84 web preview PNGs from the geocoded LOS GeoTIFF products. - -### Key Outputs - -- SLC stack: `gamma/slc/SLC_tab` -- MLI stack: `gamma/mli/RMLI_tab` -- All-pair baseline table: `gamma/diff/bperp_all_pairs.txt` -- Adjacent-pair table: `gamma/diff/itab_adjacent` -- Pair-specific RSLC products: `gamma/rslc/*_to_*.rslc` -- Pair-specific quality reports: `gamma/rslc/*_to_*.rslc.coreg_quality` -- Adjacent interferograms: `gamma/int/*.int` -- Adjacent coherence rasters: `gamma/int/*.cc` -- Adjacent interferogram previews: `gamma/int/*.int.bmp` -- Adjacent coherence previews: `gamma/int/*.cc.bmp` -- Coherence statistics: `logs/*_cc_stats.txt` -- Reference RDC DEM: `gamma/dem/20240905_8rlks.rdc.dem` -- Common-reference stack tables: `gamma/common_20240905/SLC_tab`, `gamma/common_20240905/RMLI_tab` -- Common-reference differential stack: `gamma/common_20240905/diff/*/*_8rlks.diff_filt.unw` -- Gamma `mb` time-series list: `gamma/common_20240905/timeseries/diff_ts.tab` -- Gamma `mb` phase time-series: `gamma/common_20240905/timeseries/diff_ts_*.diff` -- Gamma `mb` residual sigma: `gamma/common_20240905/timeseries/sigma_ts` -- Gamma `mb` height correction: `gamma/common_20240905/timeseries/hgt_correction` -- Gamma `ts_rate` output: `gamma/common_20240905/timeseries/ts_rate` -- Gamma `ts_rate` sigma output: `gamma/common_20240905/timeseries/sigma_rate` -- Preview rasters: `gamma/common_20240905/timeseries/*.bmp` -- Geocoded GeoTIFF exports: `publish/geotiff/*.tif` -- Explicit LOS velocity previews: `publish/geotiff/los_rate_toward_mm_per_year.bmp`, `publish/geotiff/los_rate_away_mm_per_year.bmp` -- Geocoded web previews: `publish/geotiff/los_rate_toward_mm_per_year.geo_preview.png`, `publish/geotiff/los_sigma_mm_per_year.geo_preview.png` -- Monitoring-point curve: `publish/monitor_points/auto_low_sigma_high_rate_timeseries.png` -- Monitoring-point values: `publish/monitor_points/auto_low_sigma_high_rate_timeseries.csv` -- Trial summary: `publish/trial_summary.json` - -### Baseline Notes - -`base_calc` generated 10 all-pair entries. The adjacent temporal network is: - -| Pair | Delta days | Bperp from `base_calc` | -| --- | ---: | ---: | -| 20240516 -> 20240711 | 56 | 646.09670 | -| 20240711 -> 20240905 | 56 | -236.76960 | -| 20240905 -> 20250417 | 224 | 249.35880 | -| 20250417 -> 20251002 | 168 | 59.18150 | - -The first validated pair was `20240711 -> 20240905` because it has a 56-day interval and moderate perpendicular baseline in this stack. - -### Adjacent-Pair Quality - -Final `SLC_coreg.py` quality-test summaries: - -| Pair | Accepted offsets | Final std range | Final std azimuth | -| --- | ---: | ---: | ---: | -| 20240516 -> 20240711 | 2476 / 2688 | 0.0861 | 0.1120 | -| 20240711 -> 20240905 | 2483 / 2656 | 0.0370 | 0.1245 | -| 20240905 -> 20250417 | 2322 / 2688 | 0.0487 | 0.0617 | -| 20250417 -> 20251002 | 2274 / 2688 | 0.0304 | 0.0533 | - -Coherence statistics from Gamma big-endian float files: - -| Pair | valid `[0,1]` | p25 | median | p75 | p99 | Comment | -| --- | ---: | ---: | ---: | ---: | ---: | --- | -| 20240516 -> 20240711 | 100% | 0.127489 | 0.212285 | 0.323520 | 0.649398 | Weakest pair; high Bperp and low coherence | -| 20240711 -> 20240905 | 100% | 0.629307 | 0.733220 | 0.802884 | 0.909973 | Strong pair | -| 20240905 -> 20250417 | 100% | 0.339094 | 0.485239 | 0.612798 | 0.833928 | Usable trial pair | -| 20250417 -> 20251002 | 100% | 0.394165 | 0.582921 | 0.734878 | 0.932300 | Usable trial pair | - -All adjacent pairs generated expected Gamma products. The full 5-scene chain can continue, but the first pair is a quality risk for unwrapping/IPTA. A more conservative follow-up is to run the 4-scene sub-stack from `20240711` to `20251002`, or keep the 5-scene stack but down-weight or exclude `20240516 -> 20240711` if later unwrapping/IPTA residuals are poor. - -Implementation note: - -- `SLC_coreg.py` writes the refined offset parameter file to a secondary-date path such as `gamma/slc/20240711.slc.off`. -- Trial scripts copy that file to pair-specific paths such as `gamma/rslc/20240711_to_20240905.rslc.off`. -- Future skip-pair or non-adjacent networks must use pair-specific offset files to avoid accidental reuse after the same secondary scene is coregistered to another reference. - -### Common-Reference Time-Series Trial - -The image-based Gamma IPTA path was also run using a common `20240905` reference geometry. This is closer to the official `mb -> ts_rate` time-series chain than the first adjacent-pair wrapped interferogram check. - -Common-reference inputs and products: - -- reference geometry: `20240905` -- DEM source: existing Copernicus30 cache under `backend/runtime/pyint_dem_cache` -- RDC DEM: `gamma/dem/20240905_8rlks.rdc.dem` -- common-reference SLC table: `gamma/common_20240905/SLC_tab` -- common-reference MLI table: `gamma/common_20240905/RMLI_tab` -- common-reference differential ITAB: - -```text -1 3 1 1 -2 3 2 1 -3 4 3 1 -3 5 4 1 -``` - -Filtered differential coherence statistics: - -| Pair | valid `[0,1]` | p25 | median | p75 | p99 | -| --- | ---: | ---: | ---: | ---: | ---: | -| 20240516 -> 20240905 | 100% | 0.497369 | 0.884586 | 0.945308 | 0.980583 | -| 20240711 -> 20240905 | 100% | 0.919149 | 0.968188 | 0.979449 | 0.990185 | -| 20240905 -> 20250417 | 100% | 0.366279 | 0.790519 | 0.909991 | 0.978094 | -| 20240905 -> 20251002 | 100% | 0.193104 | 0.815562 | 0.917209 | 0.972355 | - -Gamma `mb` outputs: - -- `gamma/common_20240905/timeseries/diff_ts_001.diff` through `diff_ts_005.diff` -- `gamma/common_20240905/timeseries/diff_ts.tab` -- `gamma/common_20240905/timeseries/itab_ts` -- `gamma/common_20240905/timeseries/sigma_ts` -- `gamma/common_20240905/timeseries/hgt_correction` - -Gamma `ts_rate` outputs: - -- `gamma/common_20240905/timeseries/ts_rate` -- `gamma/common_20240905/timeseries/ts_const` -- `gamma/common_20240905/timeseries/sigma_rate` -- `gamma/common_20240905/timeseries/ts_rate.bmp` -- `gamma/common_20240905/timeseries/sigma_rate.bmp` -- `gamma/common_20240905/timeseries/hgt_correction.bmp` - -GeoTIFF exports: - -- `publish/geotiff/ts_rate_rad_per_year.tif` -- `publish/geotiff/sigma_rate_rad_per_year.tif` -- `publish/geotiff/sigma_ts_rad.tif` -- `publish/geotiff/hgt_correction_m.tif` -- `publish/geotiff/los_rate_m_per_year.tif` -- `publish/geotiff/los_sigma_m_per_year.tif` -- `publish/geotiff/los_rate_away_mm_per_year.tif` -- `publish/geotiff/los_rate_toward_mm_per_year.tif` -- `publish/geotiff/los_sigma_mm_per_year.tif` -- `publish/geotiff/los_rate_toward_mm_per_year.geo_preview.png` -- `publish/geotiff/los_sigma_mm_per_year.geo_preview.png` -- `publish/trial_summary.json` - -Float output statistics using Gamma big-endian float: - -| File | Non-zero pixels | p25 | median | p75 | p99 | -| --- | ---: | ---: | ---: | ---: | ---: | -| `ts_rate` | 8,868,956 | -1.663257 | -0.271621 | 0.869116 | 2.787572 | -| `sigma_rate` | 8,868,956 | 0.396037 | 0.677578 | 1.010539 | 2.298520 | -| `sigma_ts` | 8,892,237 | 0.000704 | 0.001557 | 0.002535 | 0.518188 | -| `hgt_correction` | 8,892,250 | -16.989384 | 7.596325 | 36.411520 | 98.636933 | - -Explicit LOS velocity output statistics in `mm/year`: - -| File | Non-zero pixels | p01 | p25 | median | p75 | p99 | -| --- | ---: | ---: | ---: | ---: | ---: | ---: | -| `los_rate_toward_mm_per_year.rdc` | 8,868,956 | -52.779640 | -16.455760 | 5.142855 | 31.491957 | 84.590944 | -| `los_sigma_mm_per_year.rdc` | 8,868,956 | 0.530715 | 7.498536 | 12.829198 | 19.133450 | 43.519972 | - -This is a successful first local Gamma official-chain time-series trial. It is still a technical validation run, not a production-grade SBAS product: the stack has only 5 dates, the network is minimal, and reference region and unwrapping masks were conservative defaults. - -The exported GeoTIFFs are EPSG:4326, `2222 x 2237`, Float32, LZW-compressed Cloud Optimized GeoTIFFs with `NoData=0`. - -The `*.bmp` files generated by Gamma `rasdt_pwr` are RDC processing-geometry browse images. They are useful for quick processing QA but are not map products and should not be used as the default UI map preview. The UI/default web preview should use the `*.geo_preview.png` files generated from the EPSG:4326 GeoTIFF products. - -### LOS Sign Convention - -Gamma `ts_rate` is a phase-rate raster in `rad/year`. Converting phase rate to LOS displacement rate requires a sign convention: - -- `los_rate_away_mm_per_year = phase_rate * wavelength / (4*pi) * 1000` -- `los_rate_toward_mm_per_year = -phase_rate * wavelength / (4*pi) * 1000` - -Gamma `dispmap` documents two conventions: - -- `sflg=0`, the default: motion away from radar is negative, so motion toward radar is positive; deformation and unwrapped phase have opposite signs. -- `sflg=1`: motion away from radar is positive; deformation and unwrapped phase have the same sign. - -For system productization, use explicit names and prefer `los_rate_toward_mm_per_year` as the default display product because it matches Gamma `dispmap` default `sflg=0`. Keep the away-positive version available when another downstream convention requires direct phase-sign products. - -### Monitoring Point Curve - -An example monitoring point was selected automatically from low-sigma, high-rate, non-edge pixels: - -- radar pixel: range `336`, azimuth `2290` -- approximate lon/lat: `131.4340324903`, `43.8008322757` -- reference date in the plotted time series: `20240711` -- LOS convention: toward radar positive, away from radar negative -- fitted LOS velocity: `50.1085 mm/year` -- fitted LOS velocity sigma: `0.0063 mm/year` - -Generated outputs: - -- `publish/monitor_points/auto_low_sigma_high_rate_timeseries.png` -- `publish/monitor_points/auto_low_sigma_high_rate_timeseries.csv` -- `publish/monitor_points/auto_low_sigma_high_rate_metadata.json` - -The plotted values are: - -| Date | LOS displacement, toward-positive mm | -| --- | ---: | -| 20240516 | -7.673957 | -| 20240711 | 0.000000 | -| 20240905 | 7.673922 | -| 20250417 | 38.412669 | -| 20251002 | 61.464305 | - -This is a single example point only. It is not a validated monitoring-point network and should not be interpreted as a representative area-wide deformation curve. The automatic selection favors a non-edge pixel with relatively high absolute velocity and low fitted sigma so the curve is visually inspectable. Production monitoring points need one of these inputs: - -- user-clicked map lon/lat -- imported engineering monitoring-point layer -- a configured regular grid or point-of-interest set -- a quality-filtered automatic point sampler with spacing, coherence/sigma thresholds, and manual review - -Until that is implemented, the single curve is a capability demonstration and should be labeled as such in the UI. - -### Current Open Items - -- Review the `ts_rate.bmp`, `sigma_rate.bmp`, `hgt_correction.bmp`, LOS velocity BMPs, monitoring-point PNG, and exported GeoTIFFs visually in GIS. -- Tune reference region, coherence thresholds, and pair network before treating the result as production. -- Decide whether to keep the weak/long 2025 pair, add skip-pairs, or use a denser data sequence when more LT1 precise orbits are available. -- Keep system integration as orchestration around Gamma commands; do not implement custom SBAS inversion in application code. - -## Productization Decisions After Trial - -If the 5-scene trial succeeds, add a new managed processor instead of bending existing ISCE/SARscape flows: - -```text -processor_code = gamma_ipta_sbas -engine_code = gamma -workflow = gamma_ipta_sbas -``` - -Required system changes: - -- Add a Gamma IPTA stack manifest builder. -- Add LT1-specific hard grouping keys: - - satellite platform, not only family, unless cross-satellite LT1A/LT1B is explicitly validated - - relative orbit - - orbit direction - - imaging mode - - polarization - - scene strip/center bucket - - product variant or station/submode where it affects compatibility -- Add a Gamma baseline audit step before final pair network selection. -- Persist selected Gamma pair network separately from the coarse planning graph. -- Keep system code as orchestration only; Gamma remains the processing authority. - -## Current Recommendation - -Use the manual audited `LT1B relOrbit 114 DESCENDING STRIP1 HH / E131.2 N43.8` stack and the trial scripts as the reference path for productization. - -Do not start Gamma IPTA production from the current automatic time-series plan. It can be used for discovery, but production stack selection needs the hard grouping keys and Gamma baseline audit described above. - -For the next engineering step, add a managed `gamma_ipta_sbas` processor that orchestrates the Gamma commands rather than reimplementing SBAS inversion in application code. diff --git a/docs/INDEX.md b/docs/INDEX.md index 83159d2..8ec1707 100644 --- a/docs/INDEX.md +++ b/docs/INDEX.md @@ -1,142 +1,78 @@ # 文档索引 -本页用于区分“当前有效文档”和“历史参考文档”。 +最后更新:2026-05-28 -原则: +本页是当前有效文档入口。没有列在本页的历史设计、实验记录和过程文档不再作为当前系统事实依据。 -- 只有列在“当前有效”区域的文档,才作为现网架构、部署、运维和产品边界的事实依据。 -- 没有列入“当前有效”区域的材料,默认只作为设计过程记录、阶段性任务单或历史参考。 -- 历史文档即使还保留在仓库中,也不应被当作当前系统的真实规则。 +## 总览与部署 -## 1. 当前有效 +- [../README.md](../README.md) + 项目总览、当前生产入口、启动链路和文档入口。 -- [../README.md](../README.md) - 项目总览、当前架构和启动入口。 +- [DEPLOYMENT.md](DEPLOYMENT.md) + Windows + PostgreSQL + WSL2 + Gamma/ISCE2/ENVI 的部署与运行说明。 -- [DOCUMENTATION_GOVERNANCE.md](DOCUMENTATION_GOVERNANCE.md) - 文档治理约定,定义事实来源优先级、命名规则、归档规则与语言/编码约束。 +- [DOCUMENTATION_GOVERNANCE.md](DOCUMENTATION_GOVERNANCE.md) + 文档治理规则、事实来源优先级和清理约定。 -- [CURRENT_STATUS_20260425.md](CURRENT_STATUS_20260425.md) - 2026-04-25 的项目状态快照,包含结果目录、运行时、健康检查摘要。 +- [FRONTEND_NAVIGATION_ARCHITECTURE.md](FRONTEND_NAVIGATION_ARCHITECTURE.md) + 当前左侧导航和生产管理工作台视图模型。 -- [DEPLOYMENT.md](DEPLOYMENT.md) - 当前 Windows + PostgreSQL + WSL2 部署模型、环境变量和启动链路。 +## 生产与结果 -- [DATABASE_SELF_MAINTENANCE_AUDIT_20260425.md](DATABASE_SELF_MAINTENANCE_AUDIT_20260425.md) - 当前数据库自维护机制与现场数据库一致性审计结果。 +- [PRODUCTION_RESULTS_MULTI_ENGINE_DESIGN_20260423.md](PRODUCTION_RESULTS_MULTI_ENGINE_DESIGN_20260423.md) + 统一结果目录、标准产品包、catalog 与多引擎结果共存约定。 -- [CODEBASE_CLEANUP_AUDIT_20260425.md](CODEBASE_CLEANUP_AUDIT_20260425.md) - 代码库清理审计,记录过渡文件、文档治理和编码显示问题的判定结果。 +- [DINSAR_PRODUCTION_CORES_OVERVIEW.md](DINSAR_PRODUCTION_CORES_OVERVIEW.md) + ENVI/SARscape、ISCE2、Gamma/PyINT 三条 D-InSAR 生产核心说明。 -- [PROJ_CONFIGURATION.md](PROJ_CONFIGURATION.md) - PROJ / GDAL 相关专项配置说明。 +- [SBAS_INSAR_CURRENT_WORKFLOW.md](SBAS_INSAR_CURRENT_WORKFLOW.md) + 当前 Gamma SBAS-InSAR 生产、AOI 选栈、结果 catalog、产物和 LOS 符号约定。 -## 2. 当前执行中的核心设计 +## 运行时与专项配置 -- [PRODUCTION_RESULTS_MULTI_ENGINE_DESIGN_20260423.md](PRODUCTION_RESULTS_MULTI_ENGINE_DESIGN_20260423.md) - 多引擎结果目录、发布结构和 catalog 治理设计。 - -- [FLOOD_GEOTIFF_GAMMA_PREPROCESS_DESIGN_20260515.md](FLOOD_GEOTIFF_GAMMA_PREPROCESS_DESIGN_20260515.md) - 洪涝模块 GeoTIFF 化与 GAMMA 前处理评估,定义“原始数据到分析级 TIF 由可插拔处理器完成,水体/洪涝算法只运行在 GeoTIFF 层”的当前技术决策。 - -- [FLOOD_DISASTER_ANALYSIS_SYSTEM_DESIGN_20260514.md](FLOOD_DISASTER_ANALYSIS_SYSTEM_DESIGN_20260514.md) - 洪涝灾害分析独立系统早期设计,保留工作台、产品包和矢量套合分析边界;其中 ENVI/SARscape 作为洪涝主线的表述已被 2026-05-15 GeoTIFF 化设计取代。 - -- [WSL_RUNTIME_REFACTOR_DESIGN_20260422.md](WSL_RUNTIME_REFACTOR_DESIGN_20260422.md) +- [WSL_RUNTIME_REFACTOR_DESIGN_20260422.md](WSL_RUNTIME_REFACTOR_DESIGN_20260422.md) WSL 共享运行时和 Broker 设计。 -- [ISCE2_MANAGED_DINSAR_IMPLEMENTATION_20260424.md](ISCE2_MANAGED_DINSAR_IMPLEMENTATION_20260424.md) - ISCE2 托管式 D-InSAR 落地说明。 +- [PROJ_CONFIGURATION.md](PROJ_CONFIGURATION.md) + PROJ / GDAL 配置说明。 -- [ISCE2_PRODUCTION_RELIABILITY_HARDENING_DESIGN_20260424.md](ISCE2_PRODUCTION_RELIABILITY_HARDENING_DESIGN_20260424.md) - ISCE2 生产链路稳定性修补与约束。 +- [ISCE2_MANAGED_DINSAR_IMPLEMENTATION_20260424.md](ISCE2_MANAGED_DINSAR_IMPLEMENTATION_20260424.md) + ISCE2 托管 D-InSAR 落地说明。 -## 3. 时序 InSAR / SBAS +- [ISCE2_PRODUCTION_RELIABILITY_HARDENING_DESIGN_20260424.md](ISCE2_PRODUCTION_RELIABILITY_HARDENING_DESIGN_20260424.md) + ISCE2 生产链路稳定性约束。 -- [SBAS_INSAR_PRODUCTION_PIPELINE_DESIGN_20260519.md](SBAS_INSAR_PRODUCTION_PIPELINE_DESIGN_20260519.md) - Gamma/LT1 SBAS-InSAR 独立生产页面与统一流水线收口设计。 -- [SBAS_INSAR_GEOJSON_RESULT_MANAGEMENT_DESIGN_20260527.md](SBAS_INSAR_GEOJSON_RESULT_MANAGEMENT_DESIGN_20260527.md) - SBAS-InSAR GeoJSON 覆盖范围、行政区/AOI 按需生产、独立结果管理和产品 catalog 设计。 -- [TIMESERIES_LEGACY_DEPRECATION_20260521.md](TIMESERIES_LEGACY_DEPRECATION_20260521.md) - 旧 ISCE2/MintPy 时序生产链路停用记录,定义当前入口隐藏、配置默认关闭和后续物理删除条件。 -- [GAMMA_IPTA_LT1_SBAS_TRIAL_RUNBOOK_20260518.md](GAMMA_IPTA_LT1_SBAS_TRIAL_RUNBOOK_20260518.md) - Gamma 官方 DIFF + IPTA 路径处理 LT1 数据的本地试验记录、产物和符号约定。 +## 数据与业务模块 -说明: +- [SENTINEL1_SOURCE_ORBIT_ASSET_DESIGN_20260512.md](SENTINEL1_SOURCE_ORBIT_ASSET_DESIGN_20260512.md) + Sentinel-1 / LT-1 源数据与精密轨道资产层设计。 -- 当前 SBAS-InSAR 生产主线是 Gamma 独立生产页面,不再使用旧 ISCE2/MintPy 时序生产页作为入口。 -- 旧 `timeseries-production` 后端、旧前端面板和 `ps_timeseries_runs` 相关结构暂时保留兼容,但默认关闭。 -- `ISCE2_SBAS_TIMESERIES_DESIGN.md`、`ISCE2_SBAS_PRODUCT_SPEC.md`、`ISCE2_SBAS_ENGINEERING_DESIGN_20260428.md` 仅作为历史参考,不再作为当前生产事实依据。 -- `SBAS_*` 命名的一批旧文档已经归档,只保留历史追溯价值。 +- [FLOOD_GEOTIFF_GAMMA_PREPROCESS_DESIGN_20260515.md](FLOOD_GEOTIFF_GAMMA_PREPROCESS_DESIGN_20260515.md) + 洪涝模块 GeoTIFF 化与 Gamma 前处理方向。 -## 4. 配对与前端导航 +- [FLOOD_DISASTER_ANALYSIS_SYSTEM_DESIGN_20260514.md](FLOOD_DISASTER_ANALYSIS_SYSTEM_DESIGN_20260514.md) + 洪涝灾害分析工作台、产品包和矢量套合边界。 -- [SENTINEL1_SOURCE_ORBIT_ASSET_DESIGN_20260512.md](SENTINEL1_SOURCE_ORBIT_ASSET_DESIGN_20260512.md) - Sentinel-1 / LT-1 源数据与精密轨道资产层设计,定义统一源产品库存、轨道资产、scene 绑定、启动自维护和健康检查边界。 -- [SENTINEL1_SYSTEM_ENHANCEMENT_MASTER_PLAN_20260510.md](SENTINEL1_SYSTEM_ENHANCEMENT_MASTER_PLAN_20260510.md) - Sentinel-1 系统增强主维护文档,汇总数据管理、精轨、配对、任务分发、Gamma/PyINT 生产、结果管理和分阶段实施边界。 -- [SENTINEL1_DATA_MANAGEMENT_ADAPTATION_PLAN_20260510.md](SENTINEL1_DATA_MANAGEMENT_ADAPTATION_PLAN_20260510.md) - Sentinel-1 源数据管理、精轨匹配、配对约束、分发和后续生产适配的分阶段改造规划。 -- [SENTINEL1_DINSAR_GAMMA_ISCE2_FEASIBILITY_20260510.md](SENTINEL1_DINSAR_GAMMA_ISCE2_FEASIBILITY_20260510.md) - Sentinel-1 D-InSAR 在不使用 ENVI + SARscape 核心时,基于 Gamma/PyINT 与 ISCE2 的可行性、接入边界和推荐实施顺序。 -- [DINSAR_PAIRING_DISTRIBUTION_LOGIC_20260508.md](DINSAR_PAIRING_DISTRIBUTION_LOGIC_20260508.md) - 2026-05-08 源码走读记录,梳理 D-InSAR 配对缓存、策略筛选、批次保存、数据分发和生产 worker 执行链路。 -- [DINSAR_SOURCE_BUNDLE_REVERSE_TOOL_TASK_20260511.md](DINSAR_SOURCE_BUNDLE_REVERSE_TOOL_TASK_20260511.md) - D-InSAR 去重源数据包的目录协议、续分发规则和外部反向还原工具任务书。 -- [PAIRING_ENHANCEMENT_DESIGN.md](PAIRING_ENHANCEMENT_DESIGN.md) -- [FRONTEND_NAVIGATION_ARCHITECTURE.md](FRONTEND_NAVIGATION_ARCHITECTURE.md) +## 安全 -说明: +- [SECURITY_AUDIT_2026-03-12.md](SECURITY_AUDIT_2026-03-12.md) + 安全审计记录。 -- `PAIRING_ENHANCEMENT_DESIGN.md` 保留配对语义、策略命名和产品交互边界设计价值。 -- 当前 pairing cache 的运行态事实,以 `CURRENT_STATUS_20260425.md` 和 `DATABASE_SELF_MAINTENANCE_AUDIT_20260425.md` 为准。 +## 工作笔记 -## 5. Gamma / PyINT +- [../INIT.md](../INIT.md) + 工作笔记。只用于辅助理解现场状态,不替代正式文档。 -- [PYINT_GAMMA_INTEGRATION_DESIGN_20260418.md](PYINT_GAMMA_INTEGRATION_DESIGN_20260418.md) -- [PYINT_INPUT_ASSET_ADAPTATION_DESIGN_20260419.md](PYINT_INPUT_ASSET_ADAPTATION_DESIGN_20260419.md) -- [PYINT_LT1_PRECISE_ORBIT_BRIDGE_DESIGN_20260419.md](PYINT_LT1_PRECISE_ORBIT_BRIDGE_DESIGN_20260419.md) -- [PYINT_GAMMA_AB_FINDINGS_20260420.md](PYINT_GAMMA_AB_FINDINGS_20260420.md) +## 已删除的历史材料 -说明: +以下材料已从仓库文档树删除,不再维护: -- 这部分保留当前仍有导航价值的 PyINT / Gamma 设计与结论。 -- 旧的 `GAMMA_WSL2_INTEGRATION_PLAN.md` 已于 2026-04-26 归档,因为它描述的是已被替代的 `/idl` + 独立 Gamma 服务方案。 -- 真正的当前运行时事实,以 `WSL_RUNTIME_REFACTOR_DESIGN_20260422.md`、`README.md` 和健康面板为准。 +- 旧 `docs/archive/` 历史堆积目录; +- 旧 SBAS 过程文档和试验 runbook; +- 旧 ISCE2/MintPy/SARscape 时序生产设计; +- 过期的 PyINT/Gamma 实验记录; +- 过期的 Sentinel-1 阶段计划; +- 过期的配对增强计划和阶段性审计快照。 -## 6. 安全与审计 - -- [SECURITY_AUDIT_2026-03-12.md](SECURITY_AUDIT_2026-03-12.md) - -## Clone Bootstrap - -- [../scripts/bootstrap_clone.ps1](../scripts/bootstrap_clone.ps1) - Fresh-server bootstrap entry for clone-based deployment. It keeps `.env` - initialization, frontend dependency install/build, runtime bootstrap, and - deployment validation in one place without changing the main startup chain. - -## 7. 工作笔记 - -- [../INIT.md](../INIT.md) - -说明: - -- `INIT.md` 视为工作笔记,不是架构、部署和运行事实的最高依据。 -- 需要判断“当前真实规则”时,优先看 `README.md`、本页和长期有效文档。 - -## 8. 历史参考 - -以下材料默认视为历史设计记录或过程文档: - -- [archive/INDEX.md](archive/INDEX.md) -- `docs/archive/` -- 已归档的 `SBAS_*` 阶段文档 -- 已归档的 `GAMMA_WSL2_INTEGRATION_PLAN.md` -- 已归档的 `PAIRING_SYSTEM_REFACTOR_PLAN_20260413.md` -- 已归档的 `DINSAR_ENHANCEMENT_TASKS.md` -- 已归档的 `WSL2_ISCE2_MINTPY_SBAS_INTEGRATION_PLAN_20260412.md` -- 已归档的 `项目汇报.md` -- 已归档的各类 `*_EXPERIMENT_*` / `*_PROGRESS_*` / `*_TODO_*` - -最后更新:2026-05-15 -最近修订:2026-05-27 +需要判断当前实现时,优先看代码入口和本索引列出的文档。 diff --git a/docs/ISCE2_LT1_ENHANCEMENT_ALIGNMENT_20260427.md b/docs/ISCE2_LT1_ENHANCEMENT_ALIGNMENT_20260427.md deleted file mode 100644 index dd88e4c..0000000 --- a/docs/ISCE2_LT1_ENHANCEMENT_ALIGNMENT_20260427.md +++ /dev/null @@ -1,154 +0,0 @@ -# ISCE2 LT-1 Enhancement Alignment 2026-04-27 - -## Purpose - -This note records why the managed `ISCE2` `lt1_stripmap` production profile now -enables the built-in stripmap enhancement steps by default, and how that choice -relates to the existing SARscape `custom6` production chain. - -## Background - -The SARscape `custom6` chain already goes beyond a bare minimum D-InSAR run. -Its production semantics include: - -1. Interferogram generation -2. Filtering and coherence -3. Orbital trend / residual phase frequency removal -4. Phase unwrapping -5. GCP-based refinement and reflattening -6. Phase to displacement and geocoding - -This means the current LT-1 production baseline in the system is not a -scientifically "raw" interferometric export. It is an operationally enhanced -delivery chain. - -## ISCE2 Mapping - -ISCE2 stripmap does not expose the exact same SARscape modules, but it does -provide native enhancement steps that address the same operational risk class: -residual misregistration and geometry-driven long-wavelength artifacts. - -The relevant built-in ISCE2 controls are: - -- `doDenseOffsets` -- `doRubbersheetingRange` -- `doRubbersheetingAzimuth` -- `do split spectrum` -- `do dispersive` -- `rubberSheetSNRThreshold` -- `rubberSheetFilterSize` - -When enabled, the stripmap workflow: - -- estimates dense offsets from cross-correlation -- filters / masks those offsets -- updates the geometry offset fields -- performs a fine resampling pass using the corrected offsets -- unwraps low/high-band interferograms and estimates a dispersive ionosphere term -- geocodes the ionosphere-corrected nondispersive phase for delivery - -This is not identical to SARscape's `RemoveResidualPhaseFrequency` plus -`RefinementAndReflattening`, but it is the closest native ISCE2 enhancement -path inside the standard stripmap application. - -## Production Decision - -The managed `ISCE2` `lt1_stripmap` profile now treats these steps as part of the -default LT-1 production workflow: - -- split-spectrum ionosphere correction: enabled -- dense offsets: enabled -- range rubbersheeting: enabled -- azimuth rubbersheeting: enabled - -Default numeric parameters: - -- `rubberSheetSNRThreshold = 5.0` -- `rubberSheetFilterSize = 9` -- `denseWindowWidth = 64` -- `denseWindowHeight = 64` -- `denseSearchWidth = 20` -- `denseSearchHeight = 20` -- `denseSkipWidth = 32` -- `denseSkipHeight = 32` - -These defaults are stored as profile semantics in code, not as loose `.env` -feature toggles. - -## Runtime Dependency - -The stripmap ionosphere implementation imports `cv2` and `scipy`. - -Deployment check: - -```bash -/home/administrator/miniconda3/envs/insar_wsl_v1/bin/python -c "import cv2, scipy; print('ionosphere_ok')" -``` - -Repair command for an existing runtime: - -```bash -conda install -n insar_wsl_v1 -c conda-forge opencv scipy -``` - -The range rubbersheeting implementation in ISCE2 imports -`astropy.convolution` from `runRubbersheetRange.py`. The shared WSL conda -runtime therefore must include `astropy`. - -Deployment check: - -```bash -/home/administrator/miniconda3/envs/insar_wsl_v1/bin/python -c "from astropy.convolution import convolve; print('astropy_ok')" -``` - -Repair command for an existing runtime: - -```bash -conda install -n insar_wsl_v1 -c conda-forge astropy -``` - -## Boundary - -This change does **not** mean that every long-wavelength ramp problem is solved. - -It only means the default managed ISCE2 LT-1 profile now includes the native -registration-enhancement path that was previously omitted. - -If a run still shows a strong residual scene-wide ramp after rubbersheeting, -that should be treated as a separate quality / post-processing issue and should -be diagnosed explicitly rather than silently hidden inside export logic. - -## Operational Implication - -When comparing current SARscape and ISCE2 LT-1 products: - -- SARscape `custom6` remains the more explicitly refined chain -- ISCE2 `lt1_stripmap` is no longer a bare stripmap baseline -- both engines now include standard enhancement intent in default production - -This makes cross-engine behavior more defensible for LT-1 operational delivery. - -## Operator Controls - -As of 2026-04-29, the production UI no longer hides these choices behind code -defaults only. - -The ISCE2 production panel now exposes the managed LT-1 profile parameters in -three groups: - -- `Execution` -- `Delivery` -- `Enhancement` - -The user-visible controls now cover: - -- split-spectrum ionosphere correction on/off -- dense offsets on/off -- range rubbersheeting on/off -- azimuth rubbersheeting on/off -- reference normalization mode (`coh_median` or `none`) -- deramp mode (`plane` or `none`) - -This keeps the default managed behavior unchanged, while allowing operators to -fall back toward a more conservative stripmap delivery path when a specific -scene looks worse after enhancement. diff --git a/docs/ISCE2_SBAS_ENGINEERING_DESIGN_20260428.md b/docs/ISCE2_SBAS_ENGINEERING_DESIGN_20260428.md deleted file mode 100644 index f439f96..0000000 --- a/docs/ISCE2_SBAS_ENGINEERING_DESIGN_20260428.md +++ /dev/null @@ -1,663 +0,0 @@ -# ISCE2 + MintPy SBAS Engineering Design - -Updated: 2026-04-28 - -## 1. Purpose - -This document defines the engineering expansion plan for the current stack-based time-series InSAR route: - -- `LT-1 stack batch -> ISCE2 stripmapStack -> MintPy SBAS -> publish bundle -> psinsar catalog` - -The repository already has a working phase-1 skeleton. The goal of this document is not to restart the design from zero, but to align the next implementation round with the code that already exists in: - -- `backend/app/services/timeseries_service.py` -- `backend/app/routers/timeseries_production.py` -- `backend/app/services/psinsar_catalog_service.py` -- `frontend/src/TimeseriesProductionPanel.jsx` -- `frontend/src/components/PsinsarCatalogPanel.jsx` - -This document supersedes the "missing pieces" parts of `docs/ISCE2_SBAS_TIMESERIES_DESIGN.md` for the current implementation phase. - -## 2. Decisions - -### 2.1 Primary processing route - -Keep the current scientific split: - -- ISCE2 is responsible for LT-1 stack preparation, stack geometry, co-registration, baseline generation, interferogram generation, and unwrap inputs. -- MintPy is responsible for SBAS inversion and time-series products. -- The system registers only publish-grade bundles, not raw MintPy work directories. - -This means the production claim for the current phase is: - -- `SBAS time-series production on top of ISCE2 + MintPy` - -It is not: - -- full PS-InSAR -- full StaMPS integration -- full commercial-grade atmospheric/error-correction stack - -### 2.2 Keep the current business model - -Use the current model already implemented in code: - -- planning-layer stack snapshot: - - `PsTaskBatchORM` - - `PsTaskItemORM` -- business-facing production run: - - `PsTimeseriesRunORM` -- step orchestration: - - `WorkflowRunORM` - - `WorkflowStepORM` -- publish/catalog registration: - - `ResultProductORM` - - `ResultAssetORM` - - `ResultIssueORM` - -Do not redesign the run model into a new engine abstraction in this round. - -### 2.3 Keep naming stable for now - -Current naming in the repository is mixed: - -- product family shown to users: `timeseries` -- processing mode: `sbas` -- catalog namespace and package schema legacy: `psinsar` - -For this round: - -- keep `product_family = timeseries` -- keep `mode = sbas` -- keep `catalog_name = psinsar` for compatibility -- keep `psinsar.publish.v1` ingestion support working - -Do not do a DB/API namespace rename and a pipeline hardening round at the same time. - -## 2.4 Current implementation scope (2026-04-28) - -This round is intentionally constrained to avoid impact on other production business: - -- no DB schema migration -- no catalog namespace rename -- no workflow framework refactor -- no change to the system self-maintenance / self-check contract - -The implementation landed in this round focuses on pipeline hardening around the existing `PsTimeseriesRunORM` path: - -- preflight gating before SBAS run creation -- stronger runtime self-check visibility -- publish-bundle validation before catalog registration -- frontend visibility for preflight, runtime checks, and publish validation - -This means the current engineering target is: - -- make the existing ISCE2 + MintPy SBAS route operationally safer - -not: - -- redesign the overall architecture -- replace the existing result registration model -- introduce a second persistence path for timeseries products - -### 2.5 Phase-2 scope (2026-04-28) - -The second implementation round keeps the same production chain but upgrades the planning trace from ad-hoc JSON to first-class additive schema objects. - -Additive schema only: - -- new planning tables: - - `TimeseriesStackPlanORM` - - `TimeseriesStackPlanItemORM` -- nullable trace columns on existing objects: - - `PsTaskBatchORM.plan_id` - - `PsTaskBatchORM.plan_strategy` - - `PsTaskItemORM.plan_item_ref_id` - - `PsTimeseriesRunORM.plan_id` - - `PsTimeseriesRunORM.plan_strategy` - -Operational rules for phase 2: - -- do not introduce a separate migration framework -- rely on the existing database self-maintenance path: - - `Base.metadata.create_all()` - - missing-column auto-add in `backend/app/db_maintenance.py` -- keep phase-1 `planning_context` / `remark` compatibility for old batches - -The engineering target of phase 2 is: - -- formalize `plan -> batch -> run -> publish bundle -> catalog product` traceability -- expose `plan_id` in frontend production and result views -- keep old batches runnable without backfilling or hard migration -- expose `GET /timeseries-plans/{plan_id}` for plan audit/detail lookup - -## 3. Current Baseline In Code - -The current code already implements the core production skeleton. - -### 3.1 Run record and workflow - -`backend/app/models/orm.py` - -- `PsTimeseriesRunORM` already stores: - - run identity - - batch binding - - processor/runtime metadata - - work and publish roots - - input/orbit/quality summaries - - failure state -- `WorkflowRunORM` and `WorkflowStepORM` already support DAG execution and retry. -- `SystemJobORM` already supports queued worker execution per workflow step. - -### 3.2 Current workflow steps - -`backend/app/services/timeseries_service.py` - -Current step chain is already eight steps: - -1. `prepare` -2. `stack_prep_initial` -3. `materialize` -4. `stack_prep_refresh` -5. `run_isce2_stack` -6. `run_mintpy_sbas` -7. `export_publish_bundle` -8. `register_psinsar_product` - -This is already the correct backbone for the managed SBAS route. - -### 3.3 Current scientific execution boundary - -The scientific boundary is still script-based, and that is acceptable for now: - -- `experiments/isce2_sbas_timeseries/scripts/build_lt1_stack_prep.py` -- `experiments/isce2_sbas_timeseries/scripts/materialize_lt1_stack_scenes.py` -- `experiments/isce2_sbas_timeseries/scripts/prepare_lt1_stack_dem.py` -- `experiments/isce2_sbas_timeseries/scripts/run_generated_stack_runfile_ubuntu2404.sh` -- `experiments/isce2_sbas_timeseries/scripts/run_mintpy_sbas_unified_env_smoketest_ubuntu2404.sh` -- `experiments/isce2_sbas_timeseries/scripts/export_mintpy_publish_products_ubuntu2404.sh` - -The current implementation should continue to wrap these scripts instead of rewriting the scientific logic prematurely. - -### 3.4 Current frontend and ops surface - -Already present: - -- run submission and run detail: - - `frontend/src/TimeseriesProductionPanel.jsx` -- product catalog panel: - - `frontend/src/components/PsinsarCatalogPanel.jsx` -- health-check visibility: - - `frontend/src/HealthCheckPanel.jsx` -- catalog rebuild API: - - `backend/app/routers/ps_products.py` - -So the next round is a hardening and extension round, not an empty scaffold round. - -## 4. Main Gaps - -The next engineering work should focus on the following gaps. - -### 4.1 Self-check is present but still shallow - -Current runtime check already validates: - -- WSL distro -- Python path -- stack script path -- configured helper scripts -- MintPy import -- DEM/orbit/output root presence - -What is still missing: - -- write permission checks for work and publish roots -- DEM sidecar consistency checks -- runtime dependency checks for scientific imports used by ISCE2/MintPy -- batch-level readiness checks before a run is queued -- publish-bundle structural validation before catalog registration - -### 4.2 Quality summary exists, but quality gating is weak - -Current code validates: - -- stack prep readiness -- required run files -- required ISCE2 output directories -- required MintPy outputs -- publish manifest existence - -But it still does not promote enough scientific quality indicators into release gates, for example: - -- interferogram count versus expected network count -- non-empty unwrap/correlation outputs -- valid-pixel ratio after `maskAllValid` -- temporal coherence thresholds -- reference point presence and stability summary - -### 4.3 Frontend is functional but still operationally thin - -Current frontend can: - -- submit a run -- run WSL check -- list runs -- show workflow steps -- retry failed workflow steps -- browse catalog entries - -Still missing: - -- structured preflight diagnostics for the selected batch -- clearer phase summaries per run -- direct visibility into quality summaries and key artifacts -- better linkage between run detail and published product detail -- a richer product detail view closer to the D-InSAR catalog panel depth - -### 4.4 Result management needs stricter contract enforcement - -The catalog path is correct, but the following rules should be made explicit and enforced: - -- `manifest.json` is the only registration entrypoint -- every published run must have a stable `publish_dir` -- required assets must exist before registration -- missing assets should generate catalog issues and possibly quarantine status -- every product should carry: - - processor code - - runtime id - - native output trace - - stack identity - -## 5. Target Pipeline - -### 5.1 Input contract - -The run input must remain stack-based, not pair-based. - -Source objects: - -- one `ps_task_batch` -- many `ps_task_items` -- one selected stack manifest: - - `input/selected_stack_manifest.json` -- one generated stack manifest: - - `input/stack_input_manifest.json` - -The selected manifest is the planning snapshot. - -The generated stack manifest is the execution snapshot and must include: - -- stack dates -- reference date -- stack key -- group key -- resolved DEM and orbit dependencies -- readiness flags -- blocking reasons -- generated ISCE2 command arguments - -### 5.2 Runtime directory model - -Keep the current directory split: - -- work root: - - `backend/runtime/timeseries_work//...` -- publish root: - - `TIMESERIES_PRODUCT_DIR//runs//...` - -Recommended internal layout under the work root: - -- `input/` -- `inputs/dem/` -- `stack_work/` -- `logs/` -- `mintpy/` - -Recommended publish layout: - -- `manifest.json` -- `assets/` -- `preview/` -- `metadata/` - -### 5.3 Managed workflow - -The current eight-step chain is the correct managed workflow and should be kept: - -1. `prepare` - - validate batch - - resolve stack identity - - choose reference date - - write `selected_stack_manifest.json` -2. `stack_prep_initial` - - generate execution-layer stack manifest - - resolve DEM/orbits - - tell the system whether materialization is the only blocker -3. `materialize` - - materialize LT-1 scenes into stack input layout - - materialize orbit XML and local dependencies -4. `stack_prep_refresh` - - re-run readiness check after materialization - - must reach ready state -5. `run_isce2_stack` - - prepare local DEM sidecars - - generate run files - - run `run_01` to `run_08` - - validate `geom_reference`, `baselines`, and `Igrams` -6. `run_mintpy_sbas` - - write MintPy config - - run controlled `smallbaselineApp` - - validate core MintPy outputs -7. `export_publish_bundle` - - geocode MintPy outputs - - export GeoTIFF browse layers - - generate preview and manifest - - augment the manifest with canonical metadata -8. `register_psinsar_product` - - register the publish bundle into catalog - - mark the run as published - -### 5.4 Current publish contract - -Keep `docs/ISCE2_SBAS_PRODUCT_SPEC.md` as the publish contract source of truth. - -Required publish assets for the managed SBAS route: - -- `assets/geo_timeseries.h5` -- `assets/geo_velocity.h5` -- `assets/velocity.tif` -- `assets/geo_temporalCoherence.h5` -- `assets/geo_maskTempCoh.h5` -- `preview/velocity_preview.png` -- `metadata/smallbaselineApp.cfg` -- `manifest.json` - -Optional but recommended: - -- `preview/numTriNonzeroIntAmbiguity.png` -- extra quality JSON files - -## 6. Self-Check Design - -Self-check should exist at four levels. - -### 6.1 Runtime preflight - -Primary entry: - -- `POST /timeseries-production/wsl-check` - -Current checks should be kept and extended with: - -- WSL distro reachable -- configured Python reachable -- ISCE2 stack script import/help check -- MintPy import check -- helper script existence checks -- DEM root existence -- orbit pool existence -- publish root existence -- work root existence -- write-test for work root -- write-test for publish root -- DEM sidecar consistency check -- optional import checks for: - - `cv2` - - `scipy` - - `astropy` - -Return structure should remain machine-readable so the frontend can render a diagnostic card instead of a plain message string. - -### 6.2 Batch preflight - -Add a run-specific preflight before or during `create_run`. - -Minimum checks: - -- scene count meets SBAS minimum -- all scene dates are valid -- scene dates are unique -- direction is consistent -- source files exist and are readable -- orbit coverage is complete or explicitly degraded -- `group_key` and `stack_key` are derivable -- publish path does not collide with another active run - -Recommended surface: - -- a new backend helper in `timeseries_service.py` -- frontend summary block in `TimeseriesProductionPanel.jsx` - -### 6.3 In-run gates - -Each workflow step should continue to fail fast when hard requirements are not met. - -Required gates: - -- `stack_prep_refresh` must report `ready_for_stackStripMap_nofocus = true` -- all expected run files must exist before stack execution -- `run_08_igram` output directories must exist -- MintPy required outputs must exist and be non-empty -- export must generate a manifest plus required assets -- registration must succeed against the catalog service - -### 6.4 Post-publish health - -Health is not only "the run finished". - -The catalog and package checks must continue to validate: - -- manifest exists -- publish dir exists -- processor code present -- runtime id present for WSL-native engines -- native output dir present -- canonical package schema valid -- manifest count versus DB count consistency - -## 7. Frontend Design - -### 7.1 Timeseries production panel - -Keep `frontend/src/TimeseriesProductionPanel.jsx` as the main run workspace. - -Planned enhancements: - -- show structured runtime preflight results -- show batch preflight results before submission -- show phase-oriented run summary: - - input prepared - - stack ready - - ISCE2 complete - - MintPy complete - - exported - - published -- show key paths and quality summary blocks without forcing the operator to inspect raw JSON -- keep failed-step retry -- add clearer linkage to the published product once available - -### 7.2 Product catalog panel - -Keep `frontend/src/components/PsinsarCatalogPanel.jsx` as the catalog entry. - -Planned enhancements: - -- retain catalog status and rebuild actions -- enrich product detail with: - - stack identity - - processor/runtime identity - - preview and primary assets - - quality summary - - asset list - - issue list - - coverage summary -- keep the publish bundle as the fact source - -### 7.3 Health panel - -Keep health visibility in `frontend/src/HealthCheckPanel.jsx`. - -The timeseries section should continue to show: - -- catalog status -- rebuild need -- manifest vs DB counts -- issue count - -It should remain aligned with: - -- `timeseries_result_catalog` -- `product_packages` -- `wsl_runtime` - -## 8. Result Management and Registration - -### 8.1 Registration rule - -Register only from: - -- `/manifest.json` - -Do not register from: - -- MintPy work directories -- ISCE2 runtime directories -- ad hoc copied assets - -### 8.2 Catalog model - -Keep: - -- `catalog_name = psinsar` -- `product_family = timeseries` - -Current catalog service already derives: - -- product id -- display name -- stack identity -- runtime and processor metadata -- bbox from asset summaries -- preview and primary asset paths - -The next round should strengthen issue generation for missing assets and invalid package states. - -### 8.3 Quarantine policy - -Do not auto-delete broken publish packages. - -If a rebuild finds broken packages, the preferred behavior is: - -- keep package on disk -- create `ResultIssueORM` records -- downgrade `health_status` -- use quarantine status only when the package is structurally unusable - -This keeps auditability intact. - -## 9. Implementation Strategy - -### 9.1 Do not over-refactor first - -The old design expected many new modules. That is no longer necessary because the codebase already has the main modules. - -For the next round: - -- keep `timeseries_service.py` as the orchestration center -- keep `job_handlers.py` as worker entrypoints -- keep `psinsar_catalog_service.py` as catalog authority -- extract helper modules only when a block becomes independently reusable or too large - -### 9.2 Recommended implementation phases - -#### Phase A: contract and self-check hardening - -Files likely involved: - -- `backend/app/services/timeseries_service.py` -- `backend/app/services/health_service.py` -- `frontend/src/TimeseriesProductionPanel.jsx` - -Target: - -- stronger runtime report -- batch preflight -- clearer failure reasons - -#### Phase B: quality summary and gating - -Files likely involved: - -- `backend/app/services/timeseries_service.py` -- `experiments/isce2_sbas_timeseries/scripts/build_mintpy_publish_bundle.py` -- `backend/app/services/psinsar_catalog_service.py` - -Target: - -- richer `quality_summary_json` -- richer manifest quality block -- stronger publish/register gates - -#### Phase C: frontend run and catalog UX - -Files likely involved: - -- `frontend/src/TimeseriesProductionPanel.jsx` -- `frontend/src/components/PsinsarCatalogPanel.jsx` -- `frontend/src/api/timeseriesProduction.js` -- `frontend/src/api/psinsarProducts.js` - -Target: - -- better preflight display -- better run summary -- richer product detail - -#### Phase D: validation and operator closure - -Target: - -- one small LT-1 AOI end-to-end validation -- one rerun-from-failure validation -- catalog rebuild validation -- deployment/ops notes update - -## 10. Non-Goals For This Round - -Do not include the following in the same implementation round: - -- Gamma/PyINT timeseries integration -- full PS-InSAR or StaMPS -- atmospheric correction productization -- topographic residual correction productization -- large database namespace migration from `psinsar` to `timeseries` - -These are valid future directions, but they should not be mixed into the current SBAS production hardening round. - -## 11. Acceptance Criteria - -The engineering expansion can be treated as complete for this round when all of the following are true: - -1. An operator can run runtime preflight and understand failures before queuing a run. -2. A stored PS stack batch can be submitted as one managed SBAS run. -3. The run can execute through all eight workflow steps in the managed path. -4. Failure at any step produces a clear error and supports controlled retry. -5. The publish bundle is complete and canonical. -6. The product is registered into the `psinsar` catalog from `manifest.json`. -7. The frontend can show: - - run state - - workflow step state - - published product linkage - - product assets and quality summary -8. `GET /api/health` remains healthy for: - - `timeseries_result_catalog` - - `product_packages` - - `wsl_runtime` - -## 12. Related Documents - -- `docs/ISCE2_SBAS_TIMESERIES_DESIGN.md` -- `docs/ISCE2_SBAS_PRODUCT_SPEC.md` -- `docs/DEPLOYMENT.md` -- `docs/CURRENT_STATUS_20260425.md` -- `experiments/isce2_sbas_timeseries/README.md` diff --git a/docs/ISCE2_SBAS_PRODUCT_SPEC.md b/docs/ISCE2_SBAS_PRODUCT_SPEC.md deleted file mode 100644 index 7d6ada1..0000000 --- a/docs/ISCE2_SBAS_PRODUCT_SPEC.md +++ /dev/null @@ -1,275 +0,0 @@ -# ISCE2 SBAS Product Specification - -Updated: 2026-04-06 - -## 1. Goal - -Define the stable product contract for the current phase-1 SBAS route: - -- `ISCE2 stripmapStack -> MintPy SBAS -> geocode/export -> psinsar publish bundle` - -This document answers three practical questions: - -- what the final SBAS deliverables are -- which files are runtime-only vs publish-grade -- which file the future system should treat as the publish entrypoint - -## 2. Product Layers - -The current experiment has three artifact layers. - -### 2.1 Processing-layer runtime artifacts - -Generated under a MintPy work directory such as: - -- `stack_work/mintpy_sbas_v5/` - -These are processing outputs, not yet the final publish contract. - -Key files: - -- `timeseries.h5` - - radar-coordinate time-series cube - - core scientific output -- `velocity.h5` - - radar-coordinate velocity solution - - useful for quick inspection and later geocode -- `temporalCoherence.h5` - - quality indicator after inversion -- `maskTempCoh.h5` - - thresholded reliability mask -- `maskAllValid.h5` - - strict runtime mask used before inversion -- `numTriNonzeroIntAmbiguity.h5` - - unwrap/network diagnostic -- `numTriNonzeroIntAmbiguity.png` - - browseable diagnostic preview - -Current runtime boundary: - -- the minimum validated SBAS runtime boundary is `timeseries.h5` plus `velocity.h5` - -### 2.2 Publish-layer standardized artifacts - -Generated under a publish directory such as: - -- `publish/mintpy_sbas_v5/` - -This is the current publish-grade boundary for system embedding. - -Directory contract: - -- `manifest.json` -- `assets/` -- `preview/` -- `metadata/` - -### 2.3 Catalog-layer registration artifacts - -The future backend should publish the standardized bundle into the `psinsar` catalog. - -Current rule: - -- the backend should not register raw MintPy runtime paths directly -- the backend should register the publish bundle rooted by `manifest.json` -- the backend should populate `manifest.json.group_key` from task/run metadata during publish registration - -## 3. Canonical Publish Bundle - -### 3.1 Entrypoint - -The publish entrypoint is: - -- `manifest.json` - -Reason: - -- it binds product identity, stack dates, reference info, artifact paths, and quality summary in one place -- it allows the backend to register one bundle without hard-coding per-file conventions -- it decouples future UI/backend integration from the temporary MintPy work directory layout - -### 3.2 Primary data products - -#### `assets/geo_timeseries.h5` - -Role: - -- primary time-series product - -Why it matters: - -- this is the file that proves the system has true time-series capability -- every valid pixel contains multi-date displacement values rather than one summary statistic - -Recommended system use: - -- archive as the canonical analysis cube -- expose as the main download for advanced analysis -- use as the source for future point-query / profile / date-slice APIs - -#### `assets/geo_velocity.h5` - -Role: - -- geocoded structured velocity product - -Recommended system use: - -- archive together with the time-series cube -- keep for programmatic reading when HDF5-native access is preferred - -#### `assets/velocity.tif` - -Role: - -- publish-grade browse/export raster - -Why it matters: - -- easiest file for GIS browsing, thumbnail generation, map services, and first-screen display - -Recommended system use: - -- default map layer for result browsing -- not the only final product -- should be treated as a summary view derived from the time-series solution - -### 3.3 Quality products - -#### `assets/geo_temporalCoherence.h5` - -Role: - -- structured quality field - -#### `assets/temporalCoherence.tif` - -Role: - -- GIS-friendly quality raster - -Recommended use: - -- review where the inversion is reliable - -#### `assets/geo_maskTempCoh.h5` - -Role: - -- structured reliable-pixel mask - -#### `assets/maskTempCoh.tif` - -Role: - -- GIS-friendly publish mask - -Recommended use: - -- mask browsing products -- support frontend display filtering - -### 3.4 Preview and diagnostics - -#### `preview/velocity_preview.png` - -Role: - -- lightweight preview image for cards, list pages, or quick QA - -#### `preview/numTriNonzeroIntAmbiguity.png` - -Role: - -- diagnostic browse image - -#### `metadata/source_quality_summary.json` - -Role: - -- compact machine-readable quality summary - -#### `metadata/smallbaselineApp.cfg` - -Role: - -- reproducibility record of the MintPy configuration - -## 4. File Priorities By Use Case - -If the goal is system publish or catalog registration: - -- focus on `manifest.json` - -If the goal is proving time-series capability: - -- focus on `assets/geo_timeseries.h5` - -If the goal is first-screen map display: - -- focus on `assets/velocity.tif` - -If the goal is quality control: - -- focus on `assets/temporalCoherence.tif` -- focus on `assets/maskTempCoh.tif` - -If the goal is full reproducibility: - -- keep `manifest.json` -- keep `metadata/smallbaselineApp.cfg` -- keep runtime `maskAllValid.h5` as a workflow artifact even if it is not published as a primary product - -## 5. Current Product Typing - -Current artifact typing implemented by the experiment bundle builder: - -- `timeseries_cube` - - `assets/geo_timeseries.h5` -- `velocity_map` - - `assets/geo_velocity.h5` -- `velocity_geotiff` - - `assets/velocity.tif` -- `temporal_coherence` - - `assets/geo_temporalCoherence.h5` -- `temporal_coherence_geotiff` - - `assets/temporalCoherence.tif` -- `quality_mask` - - `assets/geo_maskTempCoh.h5` -- `quality_mask_geotiff` - - `assets/maskTempCoh.tif` -- `preview_png` - - `preview/velocity_preview.png` -- `diagnostic_png` - - `preview/numTriNonzeroIntAmbiguity.png` - -## 6. Publish Rules - -Current recommended rules: - -- publish the geocoded bundle, not the radar-coordinate MintPy runtime directory -- treat `manifest.json` as the registration root -- treat `geo_timeseries.h5` as the canonical time-series data product -- treat `velocity.tif` as the default visualization product -- always publish at least one quality layer beside the velocity layer -- keep diagnostics and config snapshots for reproducibility even if the frontend does not display all of them - -## 7. Current Open Items - -Still to be formalized before production: - -- final CRS contract beyond the current `EPSG:4326` experiment assumption -- whether the backend stores one product record per artifact or one bundle-root record plus child artifacts -- how the frontend will query time-series pixels from `geo_timeseries.h5` -- whether extra export formats such as COG, CSV point extraction, or JSON summaries should be added - -## 8. Current Judgment - -For the current phase-1 SBAS route, the final deliverable should be understood as: - -- one manifest-driven publish bundle -- centered on `assets/geo_timeseries.h5` -- summarized by `assets/velocity.tif` -- constrained by `assets/temporalCoherence.tif` and `assets/maskTempCoh.tif` - -That is the product contract the future system should embed first. diff --git a/docs/ISCE2_SBAS_TIMESERIES_DESIGN.md b/docs/ISCE2_SBAS_TIMESERIES_DESIGN.md deleted file mode 100644 index 2744170..0000000 --- a/docs/ISCE2_SBAS_TIMESERIES_DESIGN.md +++ /dev/null @@ -1,636 +0,0 @@ -# ISCE2 SBAS Time-Series Production Design - -Updated: 2026-04-29 - -## 1. Goal - -Add stack-based time-series production under the current ISCE2-oriented architecture. - -The current repository already has: - -- image discovery and stack selection -- PS/time-series stack batch persistence -- single-pair D-InSAR production - -The current repository does not yet have: - -- a stack-level production entry -- time-series workflow orchestration -- SBAS inversion output registration -- PS-InSAR result/product pages beyond placeholders - -This design focuses on the first deliverable: - -- implement SBAS time-series production first -- keep true PS-InSAR or StaMPS as a later phase - -Related product contract: - -- `docs/ISCE2_SBAS_PRODUCT_SPEC.md` - -## 2. Current State - -### 2.1 What already exists - -- `backend/app/routers/pairing.py` - - `/find-ps-timeseries` can search image stacks for time-series use. -- `backend/app/routers/task_batches.py` - - `/task-batches/ps` persists a selected stack into `PsTaskBatchORM` and `PsTaskItemORM`. -- `backend/app/models/orm.py` - - `ps_task_batches` and `ps_task_items` already represent the planning-layer stack snapshot. -- `backend/app/models/orm.py` - - `workflow_runs`, `workflow_steps`, and `workflow_artifacts` already exist and are suitable for multi-step orchestration. -- `backend/app/models/orm.py` - - `result_products` already supports multiple catalogs through `catalog_name`. -- `frontend/src/App.jsx` - - `ps_production`, `ps_products`, `psinsar_results`, and `psinsar_analysis` are reserved placeholders. - -### 2.2 What is missing - -- `backend/app/dinsar_engines/isce2_engine.py` is pair-oriented. - - It runs a custom LT-1 `stripmapApp.py` flow for one pair or a pair-root directory. - - It does not model stack/network/time-series execution. -- Current `SBAS` in pairing-related code and docs means a pairing strategy. - - It does not mean completed SBAS inversion, velocity estimation, or time-series products. -- The current PS batch is not a production run. - - It is only a stored stack selection. - -### 2.3 Architectural implication - -Time-series production is not a small extension of the current pair-based D-InSAR engine. - -The processing object changes from: - -- `pair -> one run -> one main displacement output` - -to: - -- `stack/network -> multi-step run -> multiple intermediate and final products` - -Because of that, time-series production should not be forced into `DinsarEngine` as-is. - -Still, the LT-1 input-preparation layer should be shared. - -Recommended reuse point: - -- keep DEM path resolution, orbit-pool resolution, and LT-1 precise-orbit XML generation in a shared helper -- current implementation anchor: - - `backend/app/isce2_pipeline/lt1_input_resolver.py` - -### 2.4 Experiment Status - -Current LT-1 stack experiment status already de-risks the processing side of phase 1: - -- one offline LT-1 sample stack has completed the generated `run_01` to `run_07` chain under `Ubuntu-24.04` -- the same LT-1 sample stack has also completed `run_08_igram`, producing filtered and unwrapped pair products under `Igrams/` -- the same LT-1 sample stack has now also completed the first MintPy SBAS smoke test through radar-coordinate `timeseries.h5` and `velocity.h5` -- current successful 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` -- the same LT-1 sample stack has now also completed an experiment-layer geocode plus publish-bundle export -- current 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` -- local SAR scenes, local orbit data, and a local DEM were sufficient for this stage -- Earthdata `SWBD` was not treated as a hard dependency during experiments because `run_01_reference` can recover with a synthetic all-land `waterMask` -- current runtime split decision: - - keep ISCE2 stack processing in the existing WSL `isce2` env - - validate MintPy in a separate WSL `mintpy` env to avoid mutating the working processing env on the development machine -- phase-4 unified-env update: - - a recreated WSL env `isce2_mintpy_v1` has now also completed the same LT-1 SBAS smoke test and publish export without the `isce` bridge - - current 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` - - current 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` - - current judgment: - - unified env is viable at experiment layer and is now the preferred SBAS experiment runtime - - existing WSL `isce2` should remain untouched for current D-InSAR production safety - - bridge env remains the fallback baseline until a fuller comparison is written -- current MintPy bridge decision: - - keep `mintpy` isolated - - bridge only the top-level `isce` package from the `isce2` env into the `mintpy` env, because MintPy's ISCE stripmap metadata path imports `isce` - - do not bridge the full `isce2` `site-packages`, because that polluted the MintPy runtime with conflicting `h5py` -- current MintPy runtime workaround decision: - - use a strict `maskAllValid.h5` before inversion to suppress unstable partial-network pixels - - use a repo-local patched launcher for the current MintPy `1.6.2` single-pixel partial-network inversion bug - - current implementation anchors: - - `experiments/isce2_sbas_timeseries/scripts/create_mintpy_all_ifgram_mask.py` - - `experiments/isce2_sbas_timeseries/scripts/run_smallbaselineApp_patched.py` - - `experiments/isce2_sbas_timeseries/scripts/run_mintpy_sbas_smoketest_ubuntu2404.sh` -- current experiment-layer publish export decision: - - keep geocode/export as a separate post-MintPy stage - - current implementation anchors: - - `experiments/isce2_sbas_timeseries/scripts/export_mintpy_publish_products_ubuntu2404.sh` - - `experiments/isce2_sbas_timeseries/scripts/build_mintpy_publish_bundle.py` -- the shared helper refactor is already in place: - - `backend/app/isce2_pipeline/lt1_input_resolver.py` - - compatibility rule: - - the original pair-oriented D-InSAR public entry was not removed - - `backend/app/isce2_pipeline/run_lt1_dinsar_pipeline.py` still owns the existing workflow entry and now delegates shared input preparation to the helper - -### 2.5 Stack Planning Thresholds - -The planning entry that historically used the old PS preparation label is now treated as time-series stack preparation. -The internal API path remains `/find-ps-timeseries` for compatibility. - -Current threshold meaning: - -- `initial_overlap_threshold` - - single-scene AOI coverage gate - - formula: `area(scene footprint ∩ AOI) / area(AOI)` - - default: `0.30` - - purpose: remove scenes that barely intersect the study area -- `final_overlap_threshold` - - final stack footprint consistency gate - - formula: `area(common footprint of selected stack ∩ AOI) / min(area(each selected scene footprint ∩ AOI))` - - default: `0.95` - - purpose: ensure the retained stack has a stable common processing area without requiring each strip to cover the whole AOI - -Planning algorithm rule: - -- first group candidates by orbit direction, satellite family, imaging mode, and polarization -- `LT1A` and `LT1B` are treated as the same `LT1` satellite family for stack planning -- then search each compatible group for the largest stack whose footprint consistency satisfies `final_overlap_threshold` -- when one or more outlier scenes break the common area, the planner may drop scenes until a valid stack is found -- if no all-scene common-overlap stack exists, the planner may return a connected pairwise SBAS network when each retained network edge satisfies `final_overlap_threshold` -- at least 3 scenes are required before a stack can be persisted as a `TimeseriesStackPlan` - -## 3. Recommendation - -### 3.1 Deliver SBAS first - -Recommended first path: - -- `stack planning -> stack prep -> ISCE2 stack processing -> MintPy smallbaselineApp -> psinsar product publish -> result browsing` - -Reasons: - -- the repository already has stack-selection concepts -- SBAS outputs are easier to standardize into cataloged products -- MintPy is a practical downstream tool for small-baseline inversion over ISCE outputs -- the UI and backend risk are both lower than starting from full PS-InSAR - -### 3.2 Do not start with StaMPS / true PS route - -True PS-InSAR should be treated as phase 2 or later because it adds extra uncertainty in: - -- candidate selection and amplitude stability logic -- stack export conventions -- result structure standardization -- point-level visualization and analysis UX - -## 4. Recommended Technical Chain - -Phase-1 recommended chain: - -1. User creates or reuses a `PS stack batch`. -2. System creates a stack-level production run bound to that batch. -3. Workflow prepares a working directory and stack metadata. -4. Workflow runs ISCE2 stack processing in WSL. -5. Workflow runs MintPy SBAS inversion in a dedicated WSL MintPy runtime. -6. Workflow exports standardized products and manifest files. -7. Workflow publishes the outputs into a `psinsar` result catalog. -8. Frontend reads the catalog and workflow state for products and results. - -Product interpretation rule: - -- `manifest.json` is the publish entrypoint -- `assets/geo_timeseries.h5` is the canonical time-series data product -- `assets/velocity.tif` is the default browse layer, not the only final deliverable - -## 5. Scope - -### 5.1 In scope for phase 1 - -- SBAS time-series production entry -- stack-level workflow orchestration -- WSL runtime checks for stack and MintPy steps -- product extraction and catalog registration -- basic `ps_production` and `ps_products` pages -- basic `psinsar_results` browsing capability - -### 5.2 Out of scope for phase 1 - -- full PS-InSAR / StaMPS route -- advanced point-based analysis UI -- cluster scheduling -- multi-sensor stack fusion -- automatic reference-point intelligence - -## 6. Target Architecture - -### 6.1 Domain split - -Keep the system split into three layers: - -- Planning layer - - existing `find-ps-timeseries` - - existing `ps_task_batches` -- Production layer - - new stack-level time-series run submission and monitoring -- Result layer - - `psinsar` products and business-facing browsing - -This keeps planning data and production runs decoupled. - -### 6.2 Orchestration model - -Reuse the existing generic workflow subsystem: - -- `workflow_runs` -- `workflow_steps` -- `workflow_artifacts` - -Recommended rule: - -- do not extend `DinsarEngine` for time-series phase 1 -- use `workflow` as the orchestration primitive -- add a thin time-series metadata table that binds: - - one `ps_task_batch` - - one workflow run - - one processing mode - - one output root - -### 6.3 Suggested new metadata table - -Add a new business table such as `ps_timeseries_runs`. - -Suggested fields: - -- `run_id` -- `batch_id` -- `workflow_run_id` -- `mode` - - `sbas` -- `engine_code` - - `isce2` -- `processor_code` - - `isce2_stack_mintpy` -- `direction` -- `status` -- `work_dir` -- `output_dir` -- `publish_dir` -- `reference_date` -- `reference_strategy` -- `params_json` -- `summary_json` -- `error_message` -- `created_by` -- `created_at` -- `updated_at` -- `started_at` -- `ended_at` - -Notes: - -- `PsTaskBatchORM` remains the planning snapshot. -- `WorkflowRunORM` remains the step-level orchestration record. -- `ps_timeseries_runs` becomes the business-facing production record. - -### 6.4 Suggested workflow - -Recommended workflow name: - -- `isce2_sbas_mintpy_v1` - -Recommended steps: - -1. `prepare_stack_input` - - validate stack size and metadata - - build stack working directory - - resolve DEM/orbit/output paths -2. `run_isce2_stack` - - run stack pre-processing in WSL - - collect stack intermediate artifacts -3. `run_mintpy_sbas` - - run repo-controlled MintPy `smallbaselineApp` - - generate strict inversion mask if required by the pinned MintPy runtime - - generate time-series and velocity products -4. `export_standard_products` - - copy or convert outputs into stable publish structure - - generate manifest and preview assets -5. `publish_psinsar_products` - - register catalog entries - - update product status and coverage - -### 6.5 Suggested job types - -Recommended new job types: - -- `TIMESERIES_PREP_STACK` -- `ISCE2_STACK_RUN` -- `MINTPY_SBAS_RUN` -- `EXPORT_PSINSAR_PRODUCTS` -- `PUBLISH_PSINSAR_PRODUCTS` - -These fit naturally under the existing job queue and worker model. - -## 7. Backend Design - -### 7.1 New modules - -Recommended backend additions: - -- `backend/app/routers/timeseries_production.py` -- `backend/app/routers/ps_products.py` -- `backend/app/services/timeseries_service.py` -- `backend/app/services/timeseries_workflow_factory.py` -- `backend/app/services/isce2_stack_service.py` -- `backend/app/services/mintpy_service.py` -- `backend/app/services/psinsar_catalog_service.py` -- `backend/app/services/timeseries_paths.py` - -### 7.2 API sketch - -Recommended new APIs: - -- `POST /timeseries-production/runs` - - submit one SBAS run for one stored PS stack batch -- `GET /timeseries-production/runs` - - list recent time-series runs -- `GET /timeseries-production/runs/{run_id}` - - get business metadata + workflow state + artifacts -- `POST /timeseries-production/runs/{run_id}/retry-step` - - retry failed step when allowed -- `POST /timeseries-production/wsl-check` - - check stack + MintPy runtime environment -- `GET /ps-products` - - list registered `psinsar` products -- `POST /ps-products/rebuild-catalog` - - rebuild `psinsar` product catalog - -### 7.3 Reuse of current tables and services - -Recommended reuse: - -- `ps_task_batches` / `ps_task_items` - - keep as the source stack definition -- `workflow_runs` / `workflow_steps` / `workflow_artifacts` - - keep as the run and step state model -- `result_products` - - extend with `catalog_name = psinsar` -- current job queue / worker / task log chain - - reuse for execution and monitoring - -### 7.4 Product catalog strategy - -Do not build a separate product table just for time-series phase 1. - -Use `result_products` with: - -- `catalog_name = psinsar` -- `engine_code = isce2` -- `product_type` values specific to time-series outputs - -Recommended `product_type` values: - -- `velocity_map` -- `timeseries_cube` -- `temporal_coherence` -- `ifgram_network` -- `point_series_csv` -- `preview_png` -- `summary_report` - -Use the existing generic asset and issue models where possible. - -### 7.5 Current Phase-1 Artifact Contract - -The current validated LT-1 experiment supports the following publish contract: - -- `timeseries_cube` - - source file: - - `assets/geo_timeseries.h5` -- `velocity_map` - - source file: - - `assets/geo_velocity.h5` -- `velocity_geotiff` - - source file: - - `assets/velocity.tif` -- `temporal_coherence` - - source file: - - `assets/geo_temporalCoherence.h5` -- `quality_mask` - - source file: - - `assets/geo_maskTempCoh.h5` -- `temporal_coherence_geotiff` - - source file: - - `assets/temporalCoherence.tif` -- `quality_mask_geotiff` - - source file: - - `assets/maskTempCoh.tif` -- `ifgram_network` - - source file: - - runtime diagnostic source: - - `numTriNonzeroIntAmbiguity.h5` -- `preview_png` - - source file: - - `preview/velocity_preview.png` -- `diagnostic_png` - - source file: - - `preview/numTriNonzeroIntAmbiguity.png` -- retained runtime-only artifacts: - - `maskAllValid.h5` - - `avgSpatialCoh.h5` - - `smallbaselineApp.cfg` - -Current sample manifest draft: - -- `experiments/isce2_sbas_timeseries/configs/sample_psinsar_manifest_lt1_e123p3_n46p1.json` - -Current successful experiment publish bundle: - -- `experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/publish/mintpy_sbas_v5/manifest.json` - -## 8. Directory Conventions - -Recommended Windows roots: - -- `PS_TIMESERIES_WORK_ROOT` -- `PS_TIMESERIES_OUTPUT_ROOT` -- `PSINSAR_PRODUCT_DIR` - -Recommended run layout: - -```text -/// - stack_input/ - isce2_stack/ - mintpy/ - export/ - logs/ -``` - -Recommended publish layout: - -```text -// - manifest.json - preview/ - assets/ - metadata/ -``` - -Rules: - -- production should always have a stable publish directory -- intermediate work directory and final publish directory should stay separate -- raw imagery must not be copied into publish output unnecessarily - -## 9. Frontend Design - -### 9.1 `ps_production` - -Phase-1 page content: - -- select an existing PS stack batch -- choose processing mode - - phase 1 only exposes `SBAS` -- show runtime environment status - - WSL - - ISCE2 stack runtime - - MintPy runtime -- submit workflow run -- show step status, logs, and artifacts - -### 9.2 `ps_products` - -Phase-1 page content: - -- list `psinsar` products -- filter by status, run, batch, direction, date -- publish and rebuild operations -- basic manifest view - -### 9.3 `psinsar_results` - -Phase-1 page content: - -- basic product query -- map preview of velocity or deformation raster -- metadata drawer -- quick link to workflow run and source batch - -### 9.4 `psinsar_analysis` - -Keep this reserved in phase 1. - -Later it can take: - -- point time-series browsing -- rate classification -- hotspot statistics -- thematic reporting - -## 10. Configuration - -Recommended new or clarified settings: - -- `PS_TIMESERIES_ENABLED` -- `PS_TIMESERIES_WORK_ROOT` -- `PS_TIMESERIES_OUTPUT_ROOT` -- `PSINSAR_PRODUCT_DIR` -- `MINTPY_PYTHON` -- `MINTPY_SMALLBASELINE_APP` -- `MINTPY_RUNNER` -- `MINTPY_PATCHED_SMALLBASELINE_APP` -- `MINTPY_TEMPLATE_DIR` -- `PS_TIMESERIES_AUTO_PUBLISH` - -Where practical: - -- reuse `ISCE2_WSL_DISTRO` -- reuse `ISCE2_PYTHON` if MintPy is installed in the same environment -- reuse current DEM and orbit path conventions - -## 11. Key Risk: LT-1 Stack Compatibility - -This is the main technical uncertainty. - -The current repository already uses a custom LT-1 single-pair `stripmapApp.py` flow. -That does not prove official ISCE2 stack tooling will work directly for LT-1/LUTAN1. - -Therefore phase 0 must verify: - -- whether official ISCE2 stack tooling can ingest LT-1 metadata directly -- whether orbit formatting is already sufficient for stack mode -- whether extra stack-input conversion is required - -If official stack tooling is not directly compatible: - -- keep the overall architecture unchanged -- implement a custom stack builder or adapter layer before MintPy - -Do not hard-code the design around official stack scripts until this experiment is confirmed. - -## 12. Option Comparison - -| Option | Fit for current repo | Technical risk | UI/ops complexity | Phase-1 recommendation | -|---|---|---|---|---| -| ISCE2 stack + MintPy SBAS | High | Medium | Medium | Yes | -| ISCE2 StackToStaMPS / PS route | Medium-Low | High | High | No | - -Why SBAS wins first: - -- closer to the current stack-planning model -- easier to standardize outputs -- easier to explain and operate -- lower uncertainty in first delivery - -## 13. TODO List - -### Phase 0. Validation - -- [x] Verify whether LT-1/LUTAN1 can run through official ISCE2 stack tooling. -- [x] Decide whether MintPy shares the current ISCE2 environment or uses a separate env. -- [x] Finalize work/output/publish directory rules. -- [x] Decide the minimal product set for first release. - -### Phase 1. Backend scaffold - -- [ ] Add `ps_timeseries_runs` ORM and schema. -- [ ] Add `timeseries_production` router. -- [ ] Add `timeseries_workflow_factory` for `isce2_sbas_mintpy_v1`. -- [ ] Add new job handlers for stack prep, stack run, MintPy run, export, and publish. -- [ ] Bind workflow runs to `ps_task_batches`. -- [ ] Persist workflow artifacts for major intermediate and final outputs. - -### Phase 2. Catalog and products - -- [ ] Extend catalog publishing to support `catalog_name = psinsar`. -- [ ] Define manifest schema for SBAS products. -- [ ] Add coverage extraction and preview generation. -- [ ] Add rebuild and health-check operations for `psinsar` products. - -### Phase 3. Frontend - -- [ ] Replace the `ps_production` placeholder with a real production panel. -- [ ] Replace the `ps_products` placeholder with a product management panel. -- [ ] Add a basic `psinsar_results` result page. -- [ ] Keep `psinsar_analysis` reserved until point-series UX is clear. - -### Phase 4. Validation and rollout - -- [x] Run one small AOI stack end to end in the experimental workflow. -- [ ] Verify rerun, resume, and failure-recovery behavior. -- [ ] Verify published products can be queried and rendered. -- [ ] Add operator documentation and troubleshooting notes. - -## 14. Experimental Environment Recommendation - -Yes. A dedicated experiment folder is recommended. - -Purpose: - -- validate LT-1 stack compatibility without polluting production code -- collect command templates, notes, and sample manifests -- separate exploratory scripts from backend services - -Recommended location: - -- `experiments/isce2_sbas_timeseries/` - -Rules: - -- store only scripts, notes, configs, and tiny mock artifacts in git -- do not commit raw SAR scenes, DEM rasters, or large intermediate outputs -- once an experiment stabilizes, move the conclusion back into `docs/` and production code diff --git a/docs/ISCE2_STABILIZATION_UPDATELOG_20260427.md b/docs/ISCE2_STABILIZATION_UPDATELOG_20260427.md deleted file mode 100644 index a92a866..0000000 --- a/docs/ISCE2_STABILIZATION_UPDATELOG_20260427.md +++ /dev/null @@ -1,184 +0,0 @@ -# ISCE2 Stabilization Update Log - -Date: `2026-04-27` - -## Scope - -This update hardens the managed `ISCE2` LT-1 stripmap D-InSAR production path against -large raw DEM reuse, long geocode stalls, and incomplete recovered-run metadata. - -## Delivered Changes - -### 1. Managed ISCE2 pipeline hardening - -- Added `--resume-from unwrap|geocode|export` support to the LT-1 pipeline. -- Added `--full-geocode` support. The default path now geocodes only the export-critical - products instead of ISCE2's full default list. -- Added stage-aware logging with stdout flush to improve long-run observability. -- Added geocode DEM subset preparation so resumed geocode/export runs do not reprocess the - full base DEM. -- Added a guard that blocks fresh runs when the selected DEM resolves to a very large raw - base raster without a prepared `.wgs84` sibling. - -### 2. Engine and runtime configuration fixes - -- `ISCE2` now prefers an existing prepared `.wgs84` DEM over the raw base path. -- `PyINT` DEM resolution now follows the same prepared-first preference. -- The WSL ISCE2 runner now passes `resume_from` and `full_geocode` through to the pipeline. -- The WSL runner now sets both `PROJ_DATA` and `PROJ_LIB`. - -### 3. Recovery and catalog self-healing - -- Added a completion-file repair helper for managed ISCE2 runs. -- In-place ISCE2 publish/rebuild now repairs missing: - - `execution_manifest.json` - - `current/isce2__.json` -- This allows recovered runs to be reintroduced into the managed result catalog without - hand-editing completion markers. - -### 4. One-time DEM preparation tooling - -- Added `backend/app/isce2_pipeline/prepare_isce2_base_dem.py`. -- The script resolves DEM paths from `.env`, validates existing prepared outputs, and can - generate a reusable `WGS84` `.wgs84` DEM from an `EGM96` base DEM. -- The script also auto-configures `PROJ` paths for standalone execution. - -### 5. Configuration guidance - -- Updated `.env.example` comments to distinguish: - - raw SARscape/ENVI DEM source path - - prepared ISCE2/PyINT `.wgs84` path - -## Validation - -- Python syntax validation was run for the modified ISCE2 pipeline, engine, runtime, and - result-catalog modules. -- The one-time full DEM preparation completed successfully and produced: - - `.wgs84` - - `.wgs84.xml` - - `.wgs84.vrt` -- The prepared DEM metadata was verified to report `WGS84`. - -## Required Local Follow-Up - -These operational steps are intentionally not committed: - -- Point local `.env` `ISCE2_DEM_PATH` to the prepared `.wgs84` file. -- Point local `.env` `PYINT_PREPARED_DEM_PATH` to the same prepared `.wgs84` file. -- Restart the backend so the running process reloads the updated `.env`. - -## Additional Update: Strict Production Workflow Boundary - -After reviewing the ISCE2 production semantics, the export path was tightened so the -default managed `ISCE2` D-InSAR product remains a strict pipeline result rather than an -implicitly corrected interpretation layer. - -Delivered adjustments: - -- Kept the reference-normalization helper only as an optional debug/export capability. -- Restored the default export behavior to `reference_mode=none`. -- Removed reference-normalization controls from the regular managed production profile so - operators do not treat post-processing heuristics as part of the standard workflow. -- Revalidated the modified pipeline, engine, and WSL runner modules with `python3 -m py_compile` - inside the target WSL runtime environment. - -## Additional Update: DEM Sidecar Path Repair - -After switching the managed DEM bundle to a copied `SRTMDEM_RSP_SARscape` dataset under -`D:\DEM`, an ISCE2 run failed in `topo` even though the outer pipeline XML pointed at the -new location. The root cause was that the copied DEM sidecar XML files still contained old -absolute `/mnt/...` paths in `file_name`, `metadata_location`, and `extra_file_name`. - -Delivered adjustments: - -- Added `backend/app/isce2_pipeline/repair_dem_sidecars.py` to audit and repair moved DEM - sidecar XML files at directory scope. -- Added sidecar self-repair for the selected ISCE2 DEM during pipeline resolution so the - managed run no longer depends on manually editing copied `.xml` files first. -- Documented the migration risk and the recommended repair command in `docs/DEPLOYMENT.md`. - -## Additional Update: LT-1 Enhancement Alignment - -The managed `ISCE2` `lt1_stripmap` production profile now enables the built-in -stripmap enhancement path by default: - -- dense offsets -- range rubbersheeting -- azimuth rubbersheeting - -This was done to bring the default ISCE2 LT-1 production semantics closer to the -existing SARscape `custom6` chain, which already includes non-trivial refinement -steps rather than shipping a bare minimum interferometric result. - -The implementation now passes these parameters end-to-end through: - -- `backend/app/dinsar_engines/isce2_engine.py` -- `deploy/wsl/runners/isce2_runner.py` -- `backend/app/isce2_pipeline/run_lt1_dinsar_pipeline.py` - -The rationale and the SARscape / ISCE2 mapping are documented in: - -- `docs/ISCE2_LT1_ENHANCEMENT_ALIGNMENT_20260427.md` - -## Additional Update: Rubbersheet Runtime Dependency - -The first enhanced LT-1 run reached ISCE2 `dense_offsets` successfully and then -failed at `rubber_sheet_range` with: - -```text -ModuleNotFoundError: No module named 'astropy' -``` - -Root cause: - -- ISCE2's `runRubbersheetRange.py` imports `astropy.convolution`. -- The shared WSL runtime `insar_wsl_v1` had ISCE2 and SciPy installed, but did - not include `astropy`. - -Delivered adjustments: - -- Added `astropy` to `deploy/wsl/conda/insar_wsl_v1.environment.yml`. -- Added a runtime dependency preflight in the WSL runner and LT-1 pipeline so - rubbersheeting fails immediately with a clear message instead of after the - dense-offset stage has already run. -- Added `astropy.convolution` to the ISCE2 WSL availability check. - -Required deployment action: - -```bash -conda install -n insar_wsl_v1 -c conda-forge astropy -``` - -## Additional Update: Real Ionosphere Stage Integration - -The managed `ISCE2` LT-1 stripmap workflow now runs the native stripmap -dispersive correction path instead of faking `PICKLE/ionosphere` state during -resume. - -Delivered adjustments: - -- Enabled `do split spectrum = True` and `do dispersive = True` in the generated - `stripmapApp` XML. -- Changed stage-2 execution from a narrow `unwrap -> unwrap` run to a real - `filter_low_band/unwrap/ionosphere` resume path. -- Changed stage-3 execution to resume from real `ionosphere` state when present, - or from the low/high-band unwrap state when only stage-2 products exist. -- Extended the reduced geocode export list with: - - `ionosphere/dispersive.bil.unwCor.filt` - - `ionosphere/nondispersive.bil.unwCor.filt` - - `ionosphere/mask.bil` -- Updated the export step to prefer geocoded - `ionosphere/nondispersive.bil.unwCor.filt` when available. - -Operational effect: - -- `resume_from=unwrap` now resumes the complete stage-2 chain up to - `ionosphere` -- `resume_from=geocode` now performs real `ionosphere -> geocode` continuation - instead of relying on copied pickle files - -Deployment note: - -- The native ionosphere implementation imports `cv2` and `scipy` -- The WSL runner, pipeline preflight, and health check now verify those modules - before production starts diff --git a/docs/PAIRING_ENHANCEMENT_DESIGN.md b/docs/PAIRING_ENHANCEMENT_DESIGN.md deleted file mode 100644 index ac5a2c7..0000000 --- a/docs/PAIRING_ENHANCEMENT_DESIGN.md +++ /dev/null @@ -1,538 +0,0 @@ -# D-InSAR 配对增强设计文档 - -**版本**: v2.0 -**日期**: 2026-03-08 -**状态**: 概念设计,部分能力已落地 - -> 更新说明(2026-04-26):截至 2026-04-25,pairing cache、pairing health 状态和基础指标统计已经在现网落地。当前运行态事实请优先参考 `CURRENT_STATUS_20260425.md`、`DATABASE_SELF_MAINTENANCE_AUDIT_20260425.md` 和健康检查接口;本文档保留配对语义、策略命名、产品交互和后续演进方向的设计价值。 - ---- - -## 一、背景与目标 - -### 1.1 设计起点(2026-03-08 基线) - -当前配对系统采用**单池全组合**模式: -- 所有影像放入同一个池子,两两组合,用时间基线范围过滤 -- 无法控制主影像(master)和辅影像(slave)的时间段 -- 无法指定配对策略(星型、链式、SBAS 网络等) -- 不支持手动选定参考影像 - -这种设计适合探索性分析,但不满足生产级 InSAR 处理需求。 - -### 1.2 行业标准实践 - -参考成熟 InSAR 软件(ISCE、LiCSBAS、MintPy、GAMMA): - -| 特性 | 行业标准 | 现有系统 | -|---|---|---| -| 主/辅影像分池 | 明确的 reference/secondary 选择 | ❌ 无 | -| 配对策略 | SBAS / Sequential / Star | ❌ 仅全组合 | -| 时间范围控制 | 主辅池独立时间段 | ❌ 单一 start_date | -| 参考影像指定 | 支持手动或自动选择 | ❌ 无 | -| 多卫星支持 | 预留扩展机制 | ⚠️ 硬编码 LT-1 | - -### 1.3 设计目标 - -1. **双池配对机制**:主影像池和辅影像池独立时间范围 -2. **多策略支持**:SBAS / Sequential / Star 三种标准策略 -3. **参考影像控制**:自动选择或用户指定 -4. **多卫星扩展性**:预留 Sentinel-1、ALOS-2、TerraSAR-X 等卫星接入 -5. **向后兼容**:现有配对逻辑作为 SBAS 策略保留 - ---- - -## 二、配对策略详解 - -### 2.1 SBAS(短基线子集) - -**定义**:在时空基线阈值内全组合配对,形成冗余网络。 - -**适用场景**: -- 大范围形变监测 -- 需要高时间分辨率和空间覆盖 -- 对大气误差敏感的区域 - -**参数**: -- `time_baseline_min/max`:时间基线范围(天) -- `spatial_baseline_max_meters`:footprint 中心距上限(米,兼容字段名保留) -- `overlap_threshold`:两景 footprint 最小重叠率(兼容字段名保留) -- `coverage_diversity_penalty`:覆盖多样性惩罚因子 - -**配对逻辑**: -``` -主池影像 × 辅池影像 → 满足时空基线约束 → 覆盖优化 → 最终配对 -``` - -**示例**: -``` -主池: 2024-01-01 ~ 2024-06-30 (10 景) -辅池: 2024-07-01 ~ 2024-12-31 (12 景) -时间基线: 30~90 天 -→ 生成 45 对(经覆盖优化后约 20 对) -``` - -### 2.2 Sequential(顺序配对) - -**定义**:按时间排序,每景影像与后续 N 景配对。 - -**适用场景**: -- 连续监测(如滑坡、地面沉降) -- 需要时间连续性的形变序列 -- 计算资源有限时 - -**参数**: -- `num_connections`:每景与后续几景配对(默认 1) -- 其他时空约束同 SBAS - -**配对逻辑**: -``` -1. 合并主辅池,按日期排序 -2. 每景与后续 num_connections 景配对 -3. 应用时空基线过滤 -``` - -**示例**: -``` -影像序列: [A, B, C, D, E, F] -num_connections = 2 -→ 配对: A-B, A-C, B-C, B-D, C-D, C-E, D-E, D-F, E-F -``` - -### 2.3 Star(星型配对) - -**定义**:选择一个参考影像,所有其他影像与它配对。 - -**适用场景**: -- 时序形变分析(PS-InSAR 前处理) -- 需要统一参考基准 -- 参考影像质量已知且优秀 - -**参数**: -- `reference_image_id`:指定参考影像 ID(可选) -- 自动选择策略:选择与其他影像平均重叠率最高的影像 - -**配对逻辑**: -``` -1. 确定参考影像(用户指定 或 自动选择) -2. 参考影像 × 所有其他影像 -3. 应用时空基线过滤 -``` - -**示例**: -``` -参考影像: 2024-06-15 (质量最优) -其他影像: 20 景 -→ 配对: 20 对(全部以 2024-06-15 为主影像) -``` - ---- - -## 三、双池配对机制 - -### 3.1 时间范围控制 - -| 参数 | 说明 | 默认值 | -|---|---|---| -| `master_date_from` | 主影像起始日期 (YYYYMMDD) | None(不限) | -| `master_date_to` | 主影像截止日期 (YYYYMMDD) | None(不限) | -| `slave_date_from` | 辅影像起始日期 (YYYYMMDD) | None(不限) | -| `slave_date_to` | 辅影像截止日期 (YYYYMMDD) | None(不限) | - -### 3.2 池子定义规则 - -**主影像池**: -```sql -WHERE imaging_date >= master_date_from - AND imaging_date <= master_date_to - AND (其他约束: 卫星、轨道、模式、极化、精轨...) -``` - -**辅影像池**: -```sql -WHERE imaging_date >= slave_date_from - AND imaging_date <= slave_date_to - AND (其他约束: 卫星、轨道、模式、极化、精轨...) -``` - -### 3.3 向后兼容 - -当四个日期参数全为 `None` 时: -- 主辅池合并为同一个池子 -- 行为等效于现有系统(`m.imaging_date <= s.imaging_date` 避免重复) - ---- - -## 四、多卫星扩展设计 - -### 4.1 当前硬编码问题 - -现有代码中卫星相关硬编码: -```python -# spatial_service.py -if master.satellite != slave.satellite: - continue - -# 前端显示 -satellite: "LT-1" -``` - -### 4.2 扩展方案 - -**数据库层**: -- `radar_data.satellite` 字段已支持任意字符串,无需修改 -- 配对函数增加 `p_allowed_satellites` 参数(数组) - -**后端层**: -```python -class PairingRequest(BaseModel): - # 新增 - allowed_satellites: Optional[List[str]] = None # ["LT-1", "S1A", "S1B"] - cross_satellite_pairing: bool = False # 是否允许跨卫星配对 -``` - -**前端层**: -- 配对 Modal 增加卫星多选框 -- 从数据库动态获取可用卫星列表:`SELECT DISTINCT satellite FROM radar_data` - -**配对逻辑**: -```python -# 同卫星配对(默认) -if not cross_satellite_pairing: - if master.satellite != slave.satellite: - continue - -# 跨卫星配对(高级功能,需谨慎) -if allowed_satellites: - if master.satellite not in allowed_satellites: - continue - if slave.satellite not in allowed_satellites: - continue -``` - -### 4.3 卫星特性差异处理 - -不同卫星需要不同的配对参数: - -| 卫星 | 典型时间基线 | 典型 footprint 中心距上限 | 波长 | 备注 | -|---|---|---|---|---| -| LT-1 | 30~90 天 | < 3000 m | L 波段 | 当前系统 | -| Sentinel-1 | 6~12 天 | < 150 m | C 波段 | 高重访频率 | -| ALOS-2 | 14 天 | < 500 m | L 波段 | 与 LT-1 类似 | -| TerraSAR-X | 11 天 | < 200 m | X 波段 | 高分辨率 | - -**建议**: -- 前端提供"卫星预设"按钮,自动填充推荐参数 -- 数据库增加 `satellite_config` 表存储默认配对参数 - ---- - -## 五、数据库函数设计 - -### 5.1 新函数签名 - -```sql -CREATE OR REPLACE FUNCTION find_dinsar_pairs_v2( - -- 时空约束 - p_time_baseline_min INTEGER, - p_time_baseline_max INTEGER, - p_spatial_baseline_max_meters NUMERIC, - p_overlap_threshold NUMERIC, - p_aoi_geom GEOMETRY DEFAULT NULL, - p_require_orbit_data BOOLEAN DEFAULT TRUE, - p_require_same_imaging_mode BOOLEAN DEFAULT TRUE, - p_require_same_polarization BOOLEAN DEFAULT TRUE, - p_aoi_overlap_threshold NUMERIC DEFAULT NULL, - - -- 双池日期(新增) - p_master_date_from TEXT DEFAULT NULL, - p_master_date_to TEXT DEFAULT NULL, - p_slave_date_from TEXT DEFAULT NULL, - p_slave_date_to TEXT DEFAULT NULL, - - -- 多卫星支持(新增) - p_allowed_satellites TEXT[] DEFAULT NULL, - p_cross_satellite_pairing BOOLEAN DEFAULT FALSE -) -RETURNS TABLE ( - master_id INTEGER, - slave_id INTEGER, - master_imaging_date TEXT, - slave_imaging_date TEXT, - time_baseline_days INTEGER, - spatial_baseline_meters NUMERIC, - overlap_ratio NUMERIC -) -``` - -### 5.2 核心 SQL 逻辑变化 - -```sql --- 现有逻辑 -WHERE m.id < s.id -- 避免重复 - AND m.satellite = s.satellite - --- 新逻辑 -WHERE m.id <> s.id - AND m.imaging_date <= s.imaging_date -- 主影像早于辅影像 - -- 主池日期约束 - AND (p_master_date_from IS NULL OR m.imaging_date >= p_master_date_from) - AND (p_master_date_to IS NULL OR m.imaging_date <= p_master_date_to) - -- 辅池日期约束 - AND (p_slave_date_from IS NULL OR s.imaging_date >= p_slave_date_from) - AND (p_slave_date_to IS NULL OR s.imaging_date <= p_slave_date_to) - -- 卫星约束 - AND (p_allowed_satellites IS NULL OR m.satellite = ANY(p_allowed_satellites)) - AND (p_allowed_satellites IS NULL OR s.satellite = ANY(p_allowed_satellites)) - AND (p_cross_satellite_pairing OR m.satellite = s.satellite) -``` - -### 5.3 部署策略 - -1. 创建 `003_pairing_enhancement.sql` 迁移文件 -2. `init_db.py` 自动应用(已有机制,line 354-356) -3. 保留 `find_dinsar_pairs` 旧函数(向后兼容) -4. 后端优先调用 `find_dinsar_pairs_v2`,失败时回退 - ---- - -## 六、后端 API 设计 - -### 6.1 Schema 变更 - -```python -# models/schemas.py - -class PairingRequest(BaseModel): - """D-InSAR 配对请求参数(增强版)""" - - # === 时空约束(保留) === - time_baseline_min: int = Field(default=1, ge=0, le=3650) - time_baseline_max: int = Field(default=90, ge=1, le=3650) - overlap_threshold: float = Field(default=0.5, ge=0.0, le=1.0) - spatial_baseline_max_meters: int = Field(default=3000, ge=0, le=100000) - coverage_diversity_penalty: float = Field(default=0.3, ge=0.0, le=1.0) - require_same_imaging_mode: bool = True - require_same_polarization: bool = True - aoi_overlap_threshold: Optional[float] = Field(default=None, ge=0.0, le=1.0) - - # === 双池日期(新增) === - master_date_from: Optional[str] = Field(default=None, pattern=r'^\d{8}$') - master_date_to: Optional[str] = Field(default=None, pattern=r'^\d{8}$') - slave_date_from: Optional[str] = Field(default=None, pattern=r'^\d{8}$') - slave_date_to: Optional[str] = Field(default=None, pattern=r'^\d{8}$') - - # === 配对策略(新增) === - strategy: str = Field(default="sbas", pattern=r'^(sbas|sequential|star)$') - num_connections: int = Field(default=1, ge=1, le=10) - reference_image_id: Optional[int] = None - - # === 多卫星支持(新增) === - allowed_satellites: Optional[List[str]] = None - cross_satellite_pairing: bool = False - - # === 向后兼容(保留) === - start_date: Optional[str] = Field(default=None, pattern=r'^\d{8}$') - - @field_validator('master_date_to') - def validate_master_date_range(cls, v, info): - if v and info.data.get('master_date_from'): - if v < info.data['master_date_from']: - raise ValueError('master_date_to must >= master_date_from') - return v - - @field_validator('slave_date_to') - def validate_slave_date_range(cls, v, info): - if v and info.data.get('slave_date_from'): - if v < info.data['slave_date_from']: - raise ValueError('slave_date_to must >= slave_date_from') - return v -``` - -### 6.2 服务层变更 - -```python -# services/spatial_service.py - -async def find_dinsar_pairs( - self, - db: AsyncSession, - params: PairingRequest, - aoi_wkt: Optional[str] = None, - require_orbit_data: bool = True -) -> Tuple[List[RadarPair], List[str], bool]: - """ - 增强版配对逻辑: - 1. 调用 find_dinsar_pairs_v2 SQL 函数 - 2. 根据 strategy 后处理结果 - 3. 回退路径同步更新 - """ - - # 1. 调用数据库函数 - pairs = await self._call_db_pairing_function(db, params, aoi_wkt, require_orbit_data) - - # 2. 策略后处理 - if params.strategy == "sbas": - # 覆盖优化(现有逻辑) - pairs = self._optimize_coverage_diversity(pairs, params.coverage_diversity_penalty) - - elif params.strategy == "sequential": - # 顺序配对 - pairs = self._apply_sequential_strategy(pairs, params.num_connections) - - elif params.strategy == "star": - # 星型配对 - pairs = self._apply_star_strategy(pairs, params.reference_image_id) - - # 3. 生成 task_name 并去重 - pairs = self._generate_task_names(pairs) - - return pairs, warnings, fallback_used -``` - ---- - -## 七、前端 UI 设计 - -### 7.1 配对 Modal 布局 - -``` -┌─────────────────────────────────────────────────┐ -│ D-InSAR 配对参数 │ -├─────────────────────────────────────────────────┤ -│ 配对策略: ○ SBAS ○ Sequential ○ Star │ -│ │ -│ [策略说明] │ -│ SBAS: 短基线子集,形成冗余网络,适合大范围监测 │ -│ │ -├─────────────────────────────────────────────────┤ -│ 主影像时间范围 │ -│ 从: [日期选择器 YYYYMMDD] 至: [日期选择器] │ -│ │ -│ 辅影像时间范围 │ -│ 从: [日期选择器 YYYYMMDD] 至: [日期选择器] │ -│ │ -│ ☑ 使用双池模式(不勾选则主辅池合并) │ -├─────────────────────────────────────────────────┤ -│ 时间基线: [1] ~ [90] 天 │ -│ footprint 中心距上限: [3000] 米 │ -│ 两景 footprint 最小重叠率: [0.5] │ -│ 覆盖多样性惩罚: [0.3] │ -│ │ -│ ☑ 成像模式一致 ☑ 极化一致 ☑ 仅精轨影像 │ -├─────────────────────────────────────────────────┤ -│ AOI 来源: ○ 上传 SHP ○ 行政区选择 │ -│ [AOI 选择区域...] │ -│ │ -│ AOI 覆盖率阈值: [0] (0 表示不限制) │ -├─────────────────────────────────────────────────┤ -│ 高级选项 [展开 ▼] │ -│ 卫星选择: ☑ LT-1 ☐ Sentinel-1 ☐ ALOS-2 │ -│ ☐ 允许跨卫星配对(谨慎使用) │ -│ │ -│ Sequential 参数: │ -│ 每景配对数: [1] │ -│ │ -│ Star 参数: │ -│ 参考影像: [自动选择 ▼] 或 手动指定 ID: [___] │ -├─────────────────────────────────────────────────┤ -│ [取消] [开始配对] │ -└─────────────────────────────────────────────────┘ -``` - -### 7.2 策略切换交互 - -- 选择 SBAS:显示"覆盖多样性惩罚"参数 -- 选择 Sequential:显示"每景配对数"参数 -- 选择 Star:显示"参考影像选择"下拉框(从已有影像列表动态加载) - -### 7.3 卫星选择 - -- 从 `GET /api/radar-data/available-satellites` 动态获取 -- 默认只勾选 LT-1 -- 跨卫星配对显示警告提示 - ---- - -## 八、实施计划 - -### Phase 1: 数据库层(1 天) - -- [ ] 创建 `003_pairing_enhancement.sql` -- [ ] 实现 `find_dinsar_pairs_v2` 函数 -- [ ] 更新 `init_db.py` 应用迁移 -- [ ] 测试 SQL 函数(手动执行验证) - -### Phase 2: 后端层(2 天) - -- [ ] 更新 `PairingRequest` Schema -- [ ] 实现策略后处理逻辑(Sequential / Star) -- [ ] 更新 `spatial_service.py` 调用新函数 -- [ ] 更新回退路径逻辑 -- [ ] 新增 `GET /api/radar-data/available-satellites` 端点 -- [ ] 单元测试(pytest) - -### Phase 3: 前端层(2 天) - -- [ ] 更新 `pairingStore.js` 增加新字段 -- [ ] 重构 `PairingModal.jsx` UI -- [ ] 实现策略切换交互 -- [ ] 实现卫星多选 -- [ ] 更新 `usePairingLogic.js` 传参 -- [ ] 前端构建测试 - -### Phase 4: 集成测试(1 天) - -- [ ] 端到端测试(SBAS / Sequential / Star) -- [ ] 双池配对测试 -- [ ] 多卫星配对测试 -- [ ] 向后兼容测试(旧参数仍可用) -- [ ] 性能测试(大数据量) - -### Phase 5: 文档与部署(0.5 天) - -- [ ] 更新 `CLAUDE.md` 记录改动 -- [ ] 更新用户手册(如有) -- [ ] 部署到测试环境 -- [ ] 用户验收测试 - -**总计**: 约 6.5 天 - ---- - -## 九、风险与缓解 - -| 风险 | 影响 | 缓解措施 | -|---|---|---| -| SQL 函数性能下降 | 配对速度变慢 | 保留旧函数,性能对比测试 | -| 前端 UI 复杂度增加 | 用户学习成本 | 提供"快速模式"和"高级模式"切换 | -| 多卫星配对参数不当 | 生成无效配对 | 前端参数校验 + 后端警告提示 | -| 向后兼容性破坏 | 旧配对失效 | 保留 `start_date` 参数,自动转换 | - ---- - -## 十、未来扩展 - -1. **基线网络可视化**:时间-中心距散点图(D3.js / ECharts) -2. **配对质量评分**:根据相干性、大气条件预估配对质量 -3. **自动参数推荐**:基于历史配对结果的机器学习推荐 -4. **批量配对模板**:保存常用配对参数为模板 -5. **Sentinel-1 轨道框架**:支持 Sentinel-1 的 Track/Frame 概念 - ---- - -## 附录 A:术语对照表 - -| 中文 | 英文 | 说明 | -|---|---|---| -| 主影像 | Master / Reference | 配对中的参考影像 | -| 辅影像 | Slave / Secondary | 配对中的从属影像 | -| 时间基线 | Temporal Baseline | 两景影像的时间间隔 | -| footprint 中心距 | Footprint Center Distance | 两景影像 footprint 的中心距离 | -| 短基线子集 | SBAS (Small Baseline Subset) | 配对策略之一 | -| 星型配对 | Star Graph | 单主影像配对策略 | -| 顺序配对 | Sequential Pairing | 时间顺序配对策略 | - ---- - -**文档结束** diff --git a/docs/PYINT_GAMMA_AB_FINDINGS_20260420.md b/docs/PYINT_GAMMA_AB_FINDINGS_20260420.md deleted file mode 100644 index 18c943a..0000000 --- a/docs/PYINT_GAMMA_AB_FINDINGS_20260420.md +++ /dev/null @@ -1,74 +0,0 @@ -# PyINT + Gamma A/B 排查结论 - -更新时间:2026-04-20 - -## 1. 排查目标 - -验证当前仓库内改过的 `PyINT/Gamma` 流程,是否只是“流程层修改”而没有影响科学结果;尤其要定位为什么同一组 LT-1 `Task` 在 ENVI/IDL 核心可产出结果,而当前 PyINT 结果为空。 - -本轮对照任务: - -- 任务目录:`D:\Task_Pool\DInSAR\Task_260416_Gamma_PyINT\Task_20230602_20230720` -- DEM:`D:\DEM\COPDEM_GLO30_China_4326_DEM` -- 实验根目录:`D:\PyINT_AB` -- 生产链 Python:`/home/administrator/miniconda3/envs/isce2/bin/python` - -## 2. 对照实验 - -### Case A:当前代码 + 轨道桥接开启 + rescue 开启 - -- case:`D:\PyINT_AB\current_orbit_rescue` -- 结果:流程可跑完到 `diff` -- 但关键中间结果全 0: - - `diff_filt.zero_ratio = 1.0` - - `cor.zero_ratio = 1.0` - -### Case B:当前代码 + 轨道桥接关闭 + rescue 开启 - -- case:`D:\PyINT_AB\no_orbit_rescue` -- 结果:流程同样可跑完到 `diff` -- 关键中间结果仍然全 0: - - `diff_filt.zero_ratio = 1.0` - - `cor.zero_ratio = 1.0` - -结论:轨道桥接开关不是这次“全 0 结果”的主责任点。 - -### Case C:轨道桥接开启 + 去掉 rescue - -- case:`D:\PyINT_AB\orbit_no_rescue` -- 结果:流程直接死在 `coreg` -- 关键报错: - - `init_offsetm failed` - - `ERROR: number of zero values 195367 in MLI1 image patch exceeds threshold: 32768` - -结论:当前仓库里的 `coreg rescue` 确实在掩盖真实失败。它让一个本应失败的配准任务继续往后执行,最终产生“流程成功但科学结果全 0”的假成功。 - -## 3. 直接结论 - -1. 不能再说“我们现在的改动只改流程、不影响结果”。当前实现已经改变了失败语义,导致无效结果被当成成功结果收口。 -2. 这次任务的主要问题点在 `coreg`,不是 GeoTIFF 导出,不是地理编码,也不是解缠。 -3. LT-1 精密轨道桥接不是这次空结果的主责任点;更深层的根因仍然要继续排查 LT-1 导入 / 配准链路与 ENVI/IDL 核心之间的差异。 - -## 4. 已落实的代码策略 - -为避免系统继续产出“成功但全 0”的无效结果,当前仓库已做两项收敛: - -1. `third_party/PyINT/pyint/coreg_gamma.py` - - 去掉两个 rescue/fallback - - `init_offsetm/offset_pwrm/offset_fitm/gc_map_fine` 失败时不再复制 `lt0 -> lt1` - - offset refinement 失败时不再把 `Srslc0` 直接提升为最终 `RSLC` - - 改为失败即退出 -2. `backend/app/pyint_pipeline/run_lt1_pyint_pipeline.py` - - 增加产物有效性检查 - - 如果 `diff_filt` / `coh` / `unw` / `geo_unw` / `geo_los` 出现“文件存在但二进制全 0”,直接判定此次运行失败 - - 运行失败时附带阶段错误日志路径,例如 `coreg_gamma_all.err` - -## 5. 后续真正要解决的问题 - -这次代码收敛只解决“假成功”问题,还没有解决“为什么 LT-1 在 PyINT/Gamma 下配不准”这个根因。下一阶段建议继续做以下对照: - -1. 对比 ENVI/IDL 成功任务与 PyINT 导入后的 `.slc.par`、多视幅度、DEM 配准输入是否一致。 -2. 对比 LT-1 导入脚本生成的 `SLC/MLI` 几何参数,尤其是时序、PRF、采样间隔、deskew、状态矢量相关字段。 -3. 对比 `mli0` 与目标 `Samp` 的重叠区域,确认 `init_offsetm` 为什么会在中心 patch 上出现大量 0 值。 - -当前判断:真正的科学问题仍在 LT-1 导入 / coreg 前置几何链路,而不是后面的 unwrap / geocode。 diff --git a/docs/PYINT_GAMMA_INTEGRATION_DESIGN_20260418.md b/docs/PYINT_GAMMA_INTEGRATION_DESIGN_20260418.md deleted file mode 100644 index 325b5e5..0000000 --- a/docs/PYINT_GAMMA_INTEGRATION_DESIGN_20260418.md +++ /dev/null @@ -1,601 +0,0 @@ -# PyINT + Gamma 集成总体设计 - -**日期**: 2026-04-18 -**状态**: 总体设计 -**范围**: D-InSAR 生产引擎接入、Gamma 精配对接入、配置管理、数据库策略、运维自检、前端入口 - -## 1. 结论 - -本次集成建议采用两条并行但相互衔接的路线: - -1. 将 `PyINT` 作为新的 D-InSAR 生产引擎接入现有多引擎框架,统一走现有任务队列、运行日志、结果登记与结果目录治理链路。 -2. 将 `Gamma` 配对能力接入现有“配对基础 -> 配对规划 -> 生产执行”链路,作为数据库粗配对结果之上的精化步骤,而不是替换当前配对基础缓存。 - -核心判断如下: - -- `PyINT` 更适合作为“受控外部引擎”集成,而不是直接作为后端内部 Python 库深度嵌入。 -- `Gamma` 精配对是可集成的,但更适合针对“已经筛出的场景集合/网络运行”做二次优化,不适合直接取代当前全库候选对缓存。 -- 一期集成建议不强制修改数据库主结构;优先复用现有 `pairing_network_runs` / `pairing_network_edges` 的 JSON 承载精配对元数据。 -- 如果二期需要对 Gamma 精配对历史做独立检索、统计和运维闭环,再引入单独迁移文件,并通过现有数据库自维护机制自动落库。 - -## 2. 现状与约束 - -### 2.1 当前系统已有基础 - -- 已有 D-InSAR 多引擎抽象:`backend/app/dinsar_engines/base.py` -- 已有引擎注册表:`backend/app/dinsar_engines/registry.py` -- 已有生产任务接口与队列:`backend/app/routers/dinsar_production.py` -- 已有配对基础缓存、网络运行与边追踪: - - `backend/app/models/orm.py` - - `backend/app/services/pairing_cache_service.py` - - `backend/app/services/pairing_state_service.py` - - `backend/app/services/spatial_service.py` -- 已有数据库自维护与 SQL 迁移自动执行:`backend/app/db_maintenance.py` -- 已有运维自检面板与健康检查汇总:`backend/app/services/health_service.py`、`frontend/src/HealthCheckPanel.jsx` -- 已有生产页与配对规划页: - - `frontend/src/DinsarProductionPanel.jsx` - - `frontend/src/panels/PairPlanningPanel.jsx` - -### 2.2 PyINT 项目特征 - -从 `D:\Code\PyINT` 现状看,`PyINT` 不是干净的 SDK,而是以模板和脚本为中心的流程编排层: - -- 主入口为 `pyint/pyintApp.py` -- 配对能力入口为 `pyint/select_pairs.py` -- 严重依赖环境变量: - - `SCRATCHDIR` - - `TEMPLATEDIR` - - `DEMDIR` -- 运行方式偏 Linux / WSL,广泛调用外部命令与 GAMMA CLI -- 更适合作为“流程执行器”被调用,而不是被后端直接 import 后逐步复用内部函数 - -### 2.3 明确约束 - -- 本机只有一个 WSL 环境,不需要设计多 distro 调度系统。 -- 系统级 Windows Python 解释器已经在根 `.env` 中维护,可复用,不应再为 PyINT 额外复制一套 Windows Python 配置。 -- 管理员口令不应进入设计文档、代码或 `.env.example`。权限控制继续复用现有登录态与管理员角色校验。 -- 现有运维自检面板已经较重,PyINT/Gamma 的“操作入口”不应继续堆在健康检查页里。 - -## 3. 总体集成架构 - -### 3.1 总体原则 - -- 不新建平行子系统,优先复用现有引擎、作业、配对、结果目录与目录扫描体系。 -- 不改变现有 `pairing_metric_cache.spatial_baseline_meters` 的语义。 -- 不把 Gamma 精配对结果直接覆盖数据库粗配对缓存。 -- 运维页只看状态,实际操作放在生产页与配对规划页。 - -### 3.2 架构分层 - -#### A. 生产引擎层 - -新增 `pyint` 引擎,挂到现有 `registry` 中,与 `sarscape` / `isce2` / `landsar` 并列。 - -#### B. WSL 执行适配层 - -新增受控执行服务,负责: - -- 读取 `.env` 配置 -- 复用现有 WSL 命令执行与路径转换能力 -- 组装 `PyINT` 所需环境变量 -- 生成模板文件和运行目录 -- 执行 `PyINT` 包装脚本 -- 将输出归一化到系统现有结果结构 - -#### C. 配对精化层 - -保留现有数据库候选对缓存与网络运行。 - -在此基础上新增“Gamma 精配对”步骤: - -1. 先由当前配对接口生成候选网络 -2. 再将该网络对应场景集送入 Gamma / PyINT 配对流程 -3. 生成新的精化网络结果 -4. 前端允许用户查看并选择使用精化后的网络结果 - -#### D. 结果治理层 - -PyINT/Gamma 原始工作目录不直接作为系统正式结果。 - -必须经过适配层输出统一结果包,保证继续兼容: - -- 结果目录扫描 -- 结果目录发布 -- 结果目录桥接一致性 -- 预览图/缩略图生成 -- AI 诊断与 catalog 追踪 - -## 4. PyINT 生产引擎设计 - -### 4.1 目标 - -目标不是把 `PyINT` 原封不动暴露给用户,而是把它包装成当前系统理解的“一个可选生产引擎”。 - -### 4.2 推荐实现方式 - -新增以下后端组件: - -- `backend/app/dinsar_engines/pyint_engine.py` -- `backend/app/services/pyint_service.py` -- `backend/app/pyint_pipeline/run_lt1_pyint_pipeline.py` - -职责划分: - -- `pyint_engine.py` - - 实现 `DinsarEngine` - - 暴露 `engine_code=pyint` - - 提供可用性检查、处理 profile、参数 schema -- `pyint_service.py` - - WSL 执行 - - 路径转换 - - 模板写入 - - 环境变量组装 - - 烟测检查 -- `run_lt1_pyint_pipeline.py` - - 作为受控包装脚本在 WSL 中运行 - - 负责把系统已有 `Task_*` / 配对任务目录映射成 PyINT 项目工作区 - - 调用 `pyintApp.py` 或更细粒度子脚本 - - 收集输出并生成系统结果清单 - -### 4.3 与当前生产链路的关系 - -沿用现有生产链路: - -`前端生产页 -> /dinsar-production/run -> job queue -> pyint_engine.run() -> WSL -> PyINT -> 统一结果包 -> catalog/scan` - -这样做的收益: - -- 不需要新增独立任务中心 -- 不需要新增另一套运行日志 -- 不需要新增另一套前端生产入口 -- 与当前 `DinsarProductionPanel.jsx` 的多引擎 UI 完全兼容 - -### 4.4 输入与工作区组织 - -建议一期仍以当前系统已有的任务目录为输入,不要求用户先手工构造原生 PyINT 项目。 - -推荐工作区结构: - -- 系统输入根目录:沿用当前生产面板 `root_dir` -- PyINT 工作根目录:系统管理目录,例如 `backend/runtime/pyint_work` -- 模板目录:系统管理目录,例如 `backend/runtime/pyint_templates` -- 每次运行独立 `run_key` -- 每个 pair/task 独立 workspace,避免相互污染 - -### 4.5 结果输出策略 - -PyINT 原始输出不能直接作为系统正式结果目录暴露。 - -推荐新增“输出归一化”步骤,将 PyINT/Gamma 输出转为系统现有 bundle 约定,至少包含: - -- 结果主清单 -- 关键输出文件路径 -- 运行元数据 -- pair trace 信息 -- engine/profile 信息 - -必须保证与现有结果目录扫描机制兼容。 - -### 4.6 Profile 设计建议 - -一期建议只开放一个稳定 profile: - -- `lt1_gamma_dinsar` - -不建议一开始把 `PyINT` 全部开关都暴露到前端。应只暴露对当前业务必要的参数,例如: - -- 是否强制重跑 -- 多视参数 -- 相干阈值 -- geocode 开关 -- unwrap 开关 -- 超时 - -其余细节由模板生成器按系统默认值填充。 - -## 5. Gamma 精配对设计 - -### 5.1 目标定位 - -Gamma 精配对不替代当前数据库候选对缓存,而是建立在现有候选网络之上的二次精化机制。 - -推荐定位为: - -- 当前数据库配对:全库级、粗筛级、可快速响应 -- Gamma 精配对:项目级、网络级、精筛级、可生成更可靠的时空基线网络 - -### 5.2 推荐流程 - -1. 用户在现有配对规划页完成粗配对查询 -2. 后端返回 `network_run_id` -3. 用户在“Gamma 精配对”区域发起精化 -4. 系统根据该网络运行对应的场景集合,构建 PyINT/Gamma 工作区 -5. 调用 `select_pairs.py` / `base_calc` 生成精配对网络 -6. 后端将结果落回系统网络结果表示 -7. 前端展示“粗配对结果”和“Gamma 精配对结果”的对比摘要 -8. 用户选择使用哪一版网络继续生产 - -### 5.3 为什么不能直接覆盖当前 pairing cache - -当前 `pairing_metric_cache` 里的 `spatial_baseline_meters` 已经在系统内承担既有语义与下游用途。 -Gamma 计算出的垂直基线/网络属性与当前字段不等价,直接覆盖会带来语义混乱和回归风险。 - -因此必须坚持: - -- 现有缓存保留原语义 -- Gamma 精配对结果单独存储 -- 精配对结果仅作为网络选择依据,不回写粗配对主缓存 - -### 5.4 一期存储策略 - -一期推荐不新建强结构化表,优先复用: - -- `pairing_network_runs.request_params_json` -- `pairing_network_edges.selection_meta_json` - -建议约定写入内容: - -- `refinement_engine: gamma_pyint` -- `refinement_source_run_id` -- `gamma_bperp_m` -- `gamma_tbase_days` -- `gamma_rank` -- `gamma_ifgram_list_path` -- `gamma_artifact_dir` -- `gamma_selection_reason` - -同时新增一个新的 `network_run_id`,把“精配对结果”作为新的网络运行保存,而不是修改原粗配对运行。 - -这样做的收益: - -- 一期可不改数据库结构 -- 保留粗配对和精配对双轨结果,便于审计与回退 -- 复用现有 network run / edge 追踪模型 - -### 5.5 二期可选扩展 - -如果后续有以下需求,再引入数据库迁移: - -- 精配对历史独立检索 -- 精配对任务状态长期统计 -- 精配对工作区清理与资产追踪 -- 精配对失败类型聚合运维 - -二期建议新增表,例如: - -- `pairing_refinement_runs` -- `pairing_refinement_artifacts` - -但这不是一期必须项。 - -## 6. 配置与运行管理方案 - -### 6.1 配置原则 - -- Windows 侧解释器继续复用根 `.env` 中已有的 `PYTHON_PATH` -- WSL 侧只维护 PyINT/Gamma 运行必须配置 -- 因为本机只有一个 WSL 环境,`PyINT` 与 `ISCE2` 默认共用 distro - -### 6.2 建议新增配置项 - -建议在 `.env` / `.env.example` / `backend/app/config.py` 中新增: - -```ini -PYINT_ENABLED=false -PYINT_WSL_DISTRO= -PYINT_WSL_PYTHON= -PYINT_HOME= -PYINT_APP_SCRIPT= -PYINT_TEMPLATE_ROOT= -PYINT_WORK_ROOT= -PYINT_OUTPUT_ROOT= -PYINT_DEM_ROOT= -PYINT_GAMMA_ENV_SCRIPT= -PYINT_DEFAULT_TIMEOUT_SECONDS=43200 -PYINT_SMOKE_TEST_ENABLED=false - -PAIRING_GAMMA_ENABLED=false -PAIRING_GAMMA_WORK_ROOT= -PAIRING_GAMMA_TEMPLATE_ROOT= -PAIRING_GAMMA_TIMEOUT_SECONDS=7200 -``` - -默认策略建议: - -- `PYINT_WSL_DISTRO` 为空时,默认取 `ISCE2_WSL_DISTRO` -- `PYINT_WSL_PYTHON` 为空时,默认取 `ISCE2_PYTHON` -- `PYINT_APP_SCRIPT` 指向 `pyintApp.py` -- `PYINT_WORK_ROOT` / `PAIRING_GAMMA_WORK_ROOT` 使用系统托管目录,不直接让用户任意指定 - -### 6.3 不建议写入设计或配置的内容 - -- 管理员明文密码 -- ASF/GACOS 邮箱密码 -- WSL sudo 密码 - -这些信息如确需使用,也应通过运行时安全注入或机器本地安全配置处理,不写入仓库文档。 - -### 6.4 管理与治理策略 - -建议增加以下治理规则: - -- 所有 PyINT/Gamma 工作目录按 `run_key` 或 `network_run_id` 分目录 -- 所有运行都必须写运行摘要 JSON -- 所有正式产物必须进入统一结果发布目录 -- 中间工作区可按保留策略定期清理 -- 清理动作仅允许管理员执行 - -## 7. 数据库与数据库自维护策略 - -### 7.1 一期结论 - -一期建议: - -- `PyINT` 生产引擎接入不强制改库 -- `Gamma` 精配对接入不强制改库 -- 优先复用现有 run/edge JSON 元数据承载扩展信息 - -### 7.2 二期改库触发条件 - -当满足以下任意条件时,再进入改库: - -- 需要独立查询 Gamma 精配对运行历史 -- 需要单独统计 Gamma 精配对失败率 -- 需要把精配对资产纳入长期运维对象 -- 需要做更细粒度的后台管理界面 - -### 7.3 改库时的落地方式 - -如果二期改库,必须沿用现有数据库自维护机制: - -1. 在 `backend/migrations/` 新增 SQL 迁移文件,例如 `007_pyint_gamma_integration.sql` -2. 在 `backend/app/db_maintenance.py` 的 `MIGRATION_FILES` 中追加文件名 -3. 由 `ensure_database_ready()` 在启动时自动执行迁移 - -约束: - -- 不修改既有字段语义 -- 不破坏现有 `pairing_metric_cache` / `pairing_network_*` 查询逻辑 -- 迁移必须支持重复执行幂等 - -## 8. 运维自检与健康检查设计 - -### 8.1 设计原则 - -运维自检页继续只做“状态观察”,不做主操作入口。 - -PyINT/Gamma 的正式操作入口放在: - -- 生产页 -- 配对规划页 - -### 8.2 健康检查应新增的内容 - -建议在引擎可用性检查中加入 PyINT 项: - -- `PYINT_ENABLED` -- WSL distro 可访问 -- WSL Python 可执行 -- `PYINT_HOME` 存在 -- `pyintApp.py` 存在 -- `GAMMA_ENV_SCRIPT` 可 source -- 关键命令如 `base_calc` 可执行 -- 模板目录可读 -- 工作目录可写 -- DEM 根目录可读 - -### 8.3 健康页展示策略 - -不建议在 `HealthCheckPanel.jsx` 再新增一大块复杂操作区。 - -建议只保留两类展示: - -1. 在现有 `D-InSAR 引擎` 卡片中自然显示 `PyINT` -2. 在健康详情或备注中显示 PyINT/Gamma 的简要检查摘要 - -不建议: - -- 在健康页提供精配对执行按钮 -- 在健康页提供模板编辑入口 -- 在健康页堆叠大量结果目录说明 - -### 8.4 运维修复入口位置 - -- 引擎级问题:在生产页提示不可用原因 -- 配对级问题:在配对规划页处理 -- 只有“环境诊断/烟测”可以保留在运维页 - -## 9. 后端接口设计 - -### 9.1 生产接口 - -现有 `/dinsar-production/engines` 和 `/dinsar-production/run` 可继续复用。 - -需要做的只是: - -- 在引擎注册表中加入 `pyint` -- `list_engines()` 自动返回 PyINT -- `submit_run()` 允许 `engine_code=pyint` - -### 9.2 配对接口 - -建议新增以下接口: - -- `POST /pairing/refine-gamma` - - 输入:`network_run_id` 或明确场景列表 - - 输出:新的精配对 `network_run_id`、摘要、警告、产物位置 -- `GET /pairing/networks/{network_run_id}` - - 继续复用现有接口查看粗配对/精配对网络详情 -- 可选:`GET /pairing/refine-gamma/{network_run_id}/artifacts` - - 用于查看 artifact 摘要,不建议一期必做 - -### 9.3 管理接口 - -建议增加一个轻量管理接口用于 PyINT/Gamma 环境烟测,例如: - -- `POST /dinsar-production/engines/pyint/smoke-check` - -用途仅限管理员环境校验,不参与正式生产提交。 - -## 10. 前端入口与交互布局 - -### 10.1 生产页 - -位置:`frontend/src/DinsarProductionPanel.jsx` - -建议改动: - -- 新增 `PyINT` 引擎卡片 -- 显示 PyINT 可用性状态 -- 根据 profile 展示少量必要参数 -- 保留当前“根目录 + 参数 + 提交任务”交互,不新造独立页面 - -### 10.2 配对规划页 - -位置:`frontend/src/panels/PairPlanningPanel.jsx` - -建议新增一个独立区域: - -- 标题:`Gamma 精配对` -- 放置位置:`配对基础` 卡片下方,`结果与刷新` 卡片上方 - -该区域建议包含: - -- 粗配对网络摘要 -- 发起 Gamma 精配对按钮 -- 精配对结果摘要 -- 粗配对 / 精配对差异提示 -- 选择采用哪一版网络继续生产 - -不建议把精配对塞进现有健康检查页。 - -### 10.3 健康检查页 - -位置:`frontend/src/HealthCheckPanel.jsx` - -建议只做最小改动: - -- 让 `D-InSAR 引擎` 卡片中自动出现 `PyINT` -- 如需要,增加一条 PyINT/Gamma 环境说明 - -不增加复杂控制区,避免界面继续变重。 - -## 11. 涉及改动位置 - -### 11.1 后端 - -- `backend/app/dinsar_engines/registry.py` -- `backend/app/dinsar_engines/pyint_engine.py` 新增 -- `backend/app/services/pyint_service.py` 新增 -- `backend/app/pyint_pipeline/run_lt1_pyint_pipeline.py` 新增 -- `backend/app/routers/dinsar_production.py` -- `backend/app/routers/pairing.py` -- `backend/app/services/health_service.py` -- `backend/app/config.py` -- `.env.example` - -### 11.2 前端 - -- `frontend/src/DinsarProductionPanel.jsx` -- `frontend/src/panels/PairPlanningPanel.jsx` -- `frontend/src/HealthCheckPanel.jsx` -- `frontend/src/api/dinsarProduction.js` -- `frontend/src/api/pairing.js` - -### 11.3 数据库 - -一期可不改。 - -二期若改,涉及: - -- `backend/migrations/007_pyint_gamma_integration.sql` 新增 -- `backend/app/db_maintenance.py` - -## 12. 分阶段实施建议 - -### Phase 1: PyINT 引擎接入 - -- 新增 `pyint_engine` -- 完成 WSL 可用性检查 -- 完成模板生成与工作目录治理 -- 完成生产页引擎选择 -- 完成结果归一化与目录扫描兼容 - -### Phase 2: Gamma 精配对 MVP - -- 新增 `/pairing/refine-gamma` -- 基于现有 `network_run_id` 做精化 -- 精配对结果复用现有 network run / edge 模型表达 -- 前端在配对规划页增加精配对区块 - -### Phase 3: 运维与治理补齐 - -- 增加烟测接口 -- 增加工作区清理策略 -- 增加 artifact 摘要与失败类型归档 - -### Phase 4: 二期结构化增强 - -- 若业务确认需要,再加数据库迁移 -- 把精配对历史与资产纳入更细粒度可检索对象 - -## 13. 风险与规避 - -### 13.1 PyINT 代码稳定性 - -风险: - -- 模板字段和脚本依赖较多 -- 对目录命名和环境变量较敏感 - -规避: - -- 不做深度 import 复用 -- 使用受控包装脚本 -- 限制一期只开放一个稳定 profile - -### 13.2 WSL 与路径问题 - -风险: - -- Windows 路径和 WSL 路径混用 -- 工作区权限与可写性问题 - -规避: - -- 所有路径统一通过适配层转换 -- 工作目录和模板目录由系统托管 - -### 13.3 配对语义污染 - -风险: - -- 把 Gamma 垂直基线直接混写进现有粗配对缓存字段 - -规避: - -- 明确不覆盖 `pairing_metric_cache` 语义 -- 精配对结果单独落在网络运行元数据中 - -### 13.4 前端继续膨胀 - -风险: - -- 把运维、配对、生产操作继续堆到健康检查页 - -规避: - -- 健康页只显示状态 -- 生产操作只放生产页 -- 配对操作只放配对规划页 - -## 14. 最终建议 - -建议按以下判断执行: - -- `PyINT` 生产引擎接入:必要,且应尽快按现有多引擎架构落地。 -- `Gamma` 精配对接入:可行,但应作为“粗配对之后的精化层”落地。 -- 数据库:一期不强制改库;二期若需更强管理能力,再走数据库自维护迁移。 -- 运维自检:只加状态,不加大块操作区。 -- 前端入口:生产页接 `PyINT`,配对规划页接 `Gamma 精配对`。 - diff --git a/docs/PYINT_GAMMA_LT1_INTEGRATION_NOTES_20260430.md b/docs/PYINT_GAMMA_LT1_INTEGRATION_NOTES_20260430.md deleted file mode 100644 index d6eb1cf..0000000 --- a/docs/PYINT_GAMMA_LT1_INTEGRATION_NOTES_20260430.md +++ /dev/null @@ -1,204 +0,0 @@ -# PyINT/Gamma LT-1 集成维护记录 - -更新时间:2026-04-30 - -## 1. 背景 - -本轮工作目标是评估并吸收专家提供的 `D:\Code\PyINT_LT1` 中 LT-1 / Gamma 相关修复,继续推进当前项目内 `PyINT / Gamma` 的 D-InSAR 生产能力。 - -已有排查结论见: - -- `docs/PYINT_GAMMA_AB_FINDINGS_20260420.md` -- `docs/PYINT_GAMMA_INTEGRATION_DESIGN_20260418.md` - -之前 A/B 排查已经确认:当前 PyINT/Gamma 的关键问题主要在 LT-1 导入、DEM 几何和 `coreg` 前置链路,不是解缠、地理编码或导出问题。并且旧的 rescue/fallback 曾经掩盖真实 `coreg` 失败,导致流程成功但产物全 0。 - -## 2. 专家代码判断 - -专家目录: - -`D:\Code\PyINT_LT1` - -该目录不是完整 PyINT 替代仓库,而是一组 LT-1 专用脚本补丁,主要包含: - -- `down2slc_LT1.py` -- `down2slc_cat_LT1.py` -- `down2slc_LT1_all.py` -- `coreg_gamma.py` -- `coreg_gamma_all.py` -- `generate_rdc_dem.py` -- `makedem_pyint.py` -- `makedem.py` -- `diff/unwrap/geocode` 相关脚本 - -可吸收价值: - -- 修正 LT-1 文件名日期提取偏移。 -- 修正 LT-1 批处理错误日志名。 -- 改善 `coreg_gamma.py` 中 master/slave 路径、RSLC 路径和 masterDate 选择。 -- 增加 master 日期本身的 RSLC 生成处理。 -- 对多景拼接场景更明确地区分单片 SLC、update SLC 和最终拼接 SLC。 - -不能整包覆盖的原因: - -- 专家版多个脚本大量使用裸 `os.system()`,部分命令失败不会立即中断。 -- 当前项目已经收紧失败语义,要求 Gamma 核心命令失败即失败,避免再次出现“假成功、全 0 结果”。 -- 专家版引用 `LT1_precision_orbit.py`,但该文件不在 `D:\Code\PyINT_LT1` 中。 -- 当前项目已经有系统级精轨桥接 `backend/app/pyint_pipeline/apply_lt1_precise_orbit.py`,不应退回到未纳管的外部精轨脚本。 -- 当前 WSL 环境没有 `csh/tcsh`,不能让 Python 运行链硬依赖 `LT1_import_SLC_from_zipfiles1` 这类 csh 脚本。 - -## 3. 本轮改动范围 - -本轮只修改 Gamma/PyINT vendored 脚本,不修改 ISCE2 或 ENVI D-InSAR 生产链。 - -改动文件: - -- `third_party/PyINT/pyint/down2slc_LT1.py` -- `third_party/PyINT/pyint/down2slc_cat_LT1.py` -- `third_party/PyINT/pyint/down2slc_LT1_all.py` -- `third_party/PyINT/pyint/coreg_gamma.py` -- `third_party/PyINT/pyint/generate_rdc_dem.py` - -## 4. 已落实策略 - -### 4.1 LT-1 日期解析 - -旧实现使用固定切片 `file0[41:48]`,会把 `20241206` 截成 `0241206`。 - -本轮改为从文件名中正则提取 `20\d{6}`,同时兼容 `.tar.gz` 和 `.tiff` 输入。 - -### 4.2 LT-1 单景导入 - -保留当前 Python 直接调用 `par_LT1_SLC` 的方式,不强制依赖 `csh`。 - -如果运行环境中存在 `par_LT1_SLC_YSLi`: - -- 额外生成 `.slc.update` / `.slc.update.par` -- 将 update 参数文件中的 `state_vector_*` 合回主 `.slc.par` -- 同时对 `.slc.par` 和 `.slc.update.par` 执行当前项目的精轨桥接 - -如果运行环境中不存在 `par_LT1_SLC_YSLi`: - -- 继续使用 `par_LT1_SLC` 的输出 -- 打印 warning -- 仍执行当前项目的精轨桥接 - -当前 WSL/Gamma 环境检查结果: - -- `par_LT1_SLC` 可用 -- `SLC_cat_list.py` 可用 -- `par_LT1_SLC_YSLi` 不可用 -- `csh/tcsh` 不可用 - -因此 `par_LT1_SLC_YSLi` 必须保持可选,不能作为硬依赖。 - -### 4.3 LT-1 多景拼接导入 - -多景场景改为逐景直接调用 Gamma 导入命令,并明确产出: - -- `_.slc` -- `_.slc.par` -- 可选 `_.slc.update` -- 可选 `_.slc.update.par` - -拼接时: - -- 如果所有分片都有 update SLC,则优先用 update SLC tab 调用 `SLC_cat_list.py` -- 如果 update SLC 不可用,则回退到普通 SLC tab,并打印 warning -- 最终 `.slc.par` 仍会再执行一次当前项目的精轨桥接 - -### 4.4 错误日志 - -`down2slc_LT1_all.py` 的错误日志从: - -`down2slc_sen_all.err` - -修正为: - -`down2slc_LT1_all.err` - -这能让运行失败时的阶段日志更准确。 - -### 4.5 coreg / DEM masterDate 兜底 - -`coreg_gamma.py` 和 `generate_rdc_dem.py` 增加 masterDate 实际存在性检查: - -- 如果模板中的 `masterDate` 在 `SLC/` 下存在,则照常使用。 -- 如果不存在,则选择最接近的已有 SLC 日期,并打印提示。 - -`coreg_gamma.py` 同时增加 master 日期本身处理: - -- 如果 `Mdate == Sdate`,直接将 master SLC 复制为 master RSLC。 -- 生成 master RSLC 的多视幅度。 -- 不再进入 slave coreg 流程。 - -## 5. 保留的安全边界 - -本轮没有恢复旧 rescue/fallback。 - -必须继续保留: - -- Gamma 核心命令失败即失败。 -- `run_lt1_pyint_pipeline.py` 的全 0 二进制产物检查。 -- 当前项目的 LT-1 精轨桥接和运行摘要记录。 -- `Task_*` 输入资产适配层。 - -这几个边界是防止 PyINT/Gamma 再次产出“看似成功但科学结果无效”的关键。 - -## 6. 已完成验证 - -已完成静态验证: - -```bash -python3 -m py_compile \ - third_party/PyINT/pyint/down2slc_LT1.py \ - third_party/PyINT/pyint/down2slc_cat_LT1.py \ - third_party/PyINT/pyint/down2slc_LT1_all.py \ - third_party/PyINT/pyint/coreg_gamma.py \ - third_party/PyINT/pyint/generate_rdc_dem.py -``` - -已完成 diff 检查: - -```bash -git diff --check -``` - -已确认本轮 Git 改动只包含 Gamma/PyINT 文件和本维护文档,不涉及 ISCE2 / ENVI 生产链。 - -## 7. 后续验证建议 - -建议用历史失败样例做 A/B: - -- `D:\Task_Pool\DInSAR\Task_260416_Gamma_PyINT\Task_20230602_20230720` - -重点看: - -- `down2slc_LT1_all.err` -- `coreg_gamma_all.err` -- `SLC//orbit_bridge_summary.json` -- `pyint_run_summary.json` -- `diff_filt` / `cor` / `unw` / `geo_unw` 的 all-zero 检查结果 - -判断标准: - -- 不能靠 rescue 跳过失败。 -- `coreg` 若失败,应明确失败并留下阶段日志。 -- 若流程成功,关键二进制产物不能全 0。 -- 精轨桥接 summary 应覆盖 master/slave 日期。 - -## 8. 建议提交 - -建议和本轮代码一起提交: - -```powershell -git add docs/PYINT_GAMMA_LT1_INTEGRATION_NOTES_20260430.md ` - third_party/PyINT/pyint/coreg_gamma.py ` - third_party/PyINT/pyint/down2slc_LT1.py ` - third_party/PyINT/pyint/down2slc_LT1_all.py ` - third_party/PyINT/pyint/down2slc_cat_LT1.py ` - third_party/PyINT/pyint/generate_rdc_dem.py - -git commit -m "fix(pyint): integrate LT1 gamma import and coreg fixes" -``` - diff --git a/docs/PYINT_INPUT_ASSET_ADAPTATION_DESIGN_20260419.md b/docs/PYINT_INPUT_ASSET_ADAPTATION_DESIGN_20260419.md deleted file mode 100644 index ff2a5c9..0000000 --- a/docs/PYINT_INPUT_ASSET_ADAPTATION_DESIGN_20260419.md +++ /dev/null @@ -1,554 +0,0 @@ -# PyINT 输入资产适配设计 - -**日期**: 2026-04-19 -**状态**: 总体设计 -**范围**: `Task_*` 路径适配、PyINT DEM 管理、LT-1 精密轨道治理、Gamma 配对前置条件、运维自检与前端入口 - -## 1. 结论 - -本次设计的核心结论如下: - -1. 用户侧继续沿用现有的 `Task_*` 输入模式,不要求手工准备原生 PyINT 项目目录,也不允许直接把任意外部路径当作长期运行依赖。 -2. 需要在现有 `pyint_engine -> run_lt1_pyint_pipeline.py` 之间补一层“输入资产适配层”,把 `Task_*`、DEM、精密轨道统一解析为系统托管的运行输入。 -3. DEM 可以在一期做到“系统托管且真实参与计算”,推荐优先走“本地 FABDEM/DEM 瓦片源 + PyINT 本地生成 DEM 产物”的方案,而不是直接复用 ISCE2 的 `.wgs84` 成品 DEM。 -4. LT-1 精密轨道在当前 PyINT 原生 LT-1 导入链路里,还没有现成的“接入系统轨道池并直接参与计算”的钩子。一期先做“治理级校验 + 按任务解析 + 随跑记录 + 可选准入阻断”,二期再补“真正参与 PyINT/Gamma 计算”的桥接。 -5. 一期不必改数据库结构,先把输入资产记录写入 `.dinsar_run.json`、`pyint_run_summary.json` 和结果 manifest 的扩展摘要。二期只有在需要按 DEM/轨道版本检索历史时才改库,并且必须走现有数据库自维护迁移机制。 -6. 前端主入口应放在现有 D-InSAR 生产面板的 PyINT 引擎区域;运维自检面板只保留状态摘要,不再堆叠新的操作区。 - -## 2. 现状与缺口 - -### 2.1 已经具备的部分 - -- `PyINT` 代码已经收编到仓库内 `third_party/PyINT`,不再依赖仓库外绝对路径。 -- 当前后端已经支持: - - `root_dir` 为单个任务目录,或为包含多个 `Task_*` 子目录的父目录 - - 对每个任务递归发现 `master/`、`slave/` 下的 `LT1*.tar.gz` - - 自动生成 `ifgram_list.txt` - - 自动生成 PyINT template - - 在 `backend/runtime/pyint_work` 下构造 PyINT 工作区并调用 `pyintApp.py` -- 当前系统已有成型的精轨治理链路: - - `MONITOR_ORBIT_DIR` 作为源目录 - - `ORBIT_POOL_ENVI` 作为 LT-1 `.txt` 精轨池 - - `ORBIT_POOL_ISCE2` 作为 ISCE2 `.xml` 精轨池 - - `orbit_converter.py` 已支持同步、修复、隔离和一致性检查 -- 当前系统已有成型的健康检查和目录治理链路: - - `health_service.py` - - `root_registry_service.py` - - 结果目录扫描和 manifest catalog - -### 2.2 目前还没有解决的部分 - -- 当前 PyINT 集成只解决了“`Task_*` 到 PyINT 工作区”的映射,没有解决“系统托管 DEM / 系统托管精轨资产如何进入 PyINT”。 -- 当前 `PYINT_DEM_ROOT` 只是 PyINT 的运行目录或缓存目录,不等价于“系统已经为本次任务解析好了 DEM 输入策略”。 -- 当前 LT-1 PyINT 导入脚本并没有直接消费系统里的 `LT1*_GpsData_GAS_C_YYYYMMDD.txt` 精轨池。 -- 当前前端也没有给 PyINT 提供“提交前资产预检/预览”的位置。 - -### 2.3 一个必须明确的现实约束 - -当前 vendored `PyINT` 的 LT-1 流程里: - -- DEM 侧已有明确入口,`makedem_pyint.py` 可以走本地 `fabdem_dir` 或 OpenTopography。 -- 精轨侧对 LT-1 没有现成的“使用系统 `.txt` 精轨池”的显式接口,现有 LT-1 导入脚本更接近“从压缩包和 XML 元数据生成 SLC 参数”。 - -因此本方案必须分两层描述精轨: - -1. 治理层接入:系统知道本次任务应该使用哪份精轨,能阻断缺失任务,能把依赖记录下来。 -2. 计算层接入:该精轨是否真的被 PyINT/Gamma 的 LT-1 导入过程消费。 - -一期只能承诺第一层,第二层需要专门桥接。 - -## 3. 总体方案 - -### 3.1 新增一层输入资产适配服务 - -建议在 `pyint_service.py` 旁边新增或内聚出一层输入资产适配职责,例如: - -- `resolve_pyint_tasks(root_dir)` -- `resolve_pyint_dem_asset(task_context)` -- `resolve_pyint_orbit_assets(task_context)` -- `materialize_pyint_input_assets(run_context)` -- `build_pyint_input_preview(root_dir)` - -其职责不是替代 PyINT,而是在系统生产语义和 PyINT 原生语义之间做转换。 - -### 3.2 总体执行链路 - -建议链路如下: - -`前端生产面板 root_dir` --> `validate_pyint_root_dir()` --> `PyINT 输入资产适配层` --> `每个 Task_* 解析任务身份、DEM、精轨` --> `运行目录 materialize` --> `run_lt1_pyint_pipeline.py` --> `PyINT / Gamma` --> `pyint_run_summary.json + .dinsar_run.json` --> `结果发布 / catalog` - -### 3.3 不再要求用户准备 PyINT 原生目录 - -用户仍然只需要提供: - -- 单个 `Task_YYYYMMDD_YYYYMMDD` -- 或者一个包含多个 `Task_*` 的批次根目录 - -系统内部自行生成: - -- `project_name` -- `template` -- `DOWNLOAD/` -- `ifgram_list.txt` -- `input_assets/` -- `native output` - -这保证 PyINT 继续是“受控执行器”,不是“要求用户手工维护目录结构的第二套系统”。 - -## 4. 与现有 `Task_*` 路径的配合方式 - -### 4.1 用户输入模式 - -沿用当前模式,不新增新的路径输入方式: - -- 模式 A:直接选一个 `Task_*` -- 模式 B:选一个包含多个 `Task_*` 的父目录 - -任务目录仍要求至少满足: - -- `master/` -- `slave/` -- 目录下可递归发现 `LT1*.tar.gz` - -可选但推荐继续保留: - -- `.dinsar_pair.json` - -### 4.2 任务解析规则 - -建议继续沿用当前逻辑,并把它明确固化为正式约束: - -1. `Task_*` 是业务输入根,不是 PyINT 工作区。 -2. `master/`、`slave/` 下面允许多层子目录,但最终必须能发现原始压缩包。 -3. 任务身份优先从 `.dinsar_pair.json` 读取。 -4. 缺失时再从任务目录名和压缩包文件名推导: - - `task_alias` - - `pair_key` - - `master_date` - - `slave_date` - -### 4.3 运行期目录建议 - -建议把每次运行的托管结构固定为: - -```text -backend/runtime/pyint_work/// - input_assets/ - task_manifest.json - orbits/ - dem/ - / - DOWNLOAD/ - ifgram_list.txt - ... - -backend/runtime/pyint_templates/// - .template - -backend/runtime/pyint_output///native/ - pyint_run_summary.json - .dinsar_run.json - ifgrams/ - ... -``` - -原则: - -- 原始 `Task_*` 只读,不回写。 -- 每次运行独立目录,避免不同 run 相互污染。 -- DEM、轨道、任务解析结果要在 `input_assets/` 下留痕。 - -## 5. DEM 方案 - -### 5.1 不建议直接把 ISCE2 DEM 方案硬套给 PyINT - -当前系统已有 `ISCE2_DEM_PATH`,它对应的是 ISCE2 直接消费的成品 DEM。 - -但当前 PyINT 的 DEM 处理逻辑更接近: - -- 先根据 master SLC 范围解析 DEM 覆盖区域 -- 再通过 `makedem_pyint.py` -- 结合 `fabdem_dir` 或 OpenTopography -- 在 `DEMDIR` 下生成 PyINT / Gamma 所需的 DEM 产物 - -因此一期不建议把 `PYINT_DEM_SOURCE` 简单绑定为 `ISCE2_DEM_PATH`。 - -### 5.2 推荐的 DEM 分层 - -建议把 PyINT 的 DEM 分成三层: - -1. DEM 源 - - 本地 FABDEM/DEM 瓦片根目录 - - 或 OpenTopography 在线源 -2. DEM 运行缓存 - - 即当前 `PYINT_DEM_ROOT` -3. 本次任务解析后的 DEM 产物 - - 位于 `PYINT_DEM_ROOT//...` - - 被 `generate_rdc_dem.py`、`geocode_gamma.py` 等步骤消费 - -### 5.3 推荐配置 - -建议新增或明确以下配置: - -```ini -PYINT_DEM_MODE=local_fabdem|opentopo -PYINT_FABDEM_ROOT= -PYINT_OPENTOPO_DEM_TYPE=SRTMGL1 -PYINT_DEM_ROOT= -PYINT_DEM_STRICT=true -``` - -说明: - -- `PYINT_DEM_MODE=local_fabdem` 为推荐默认值。 -- `PYINT_FABDEM_ROOT` 指向本机统一维护的 FABDEM/DEM 瓦片目录。 -- `PYINT_DEM_ROOT` 继续作为 PyINT DEM 运行缓存根。 -- 若后续确实验证可直接复用某个成品 DEM,再新增单独模式,不要和一期混在一起。 - -### 5.4 运行时行为 - -当用户提交 PyINT 任务时: - -1. 适配层先解析 DEM 模式。 -2. 若为 `local_fabdem`: - - 把 `fabdem_dir` 写入本次运行生成的 template - - `DEMDIR` 指向本次受控缓存根 -3. 若为 `opentopo`: - - 只在运行时注入 API key,不把敏感值写入仓库文档或 `.env.example` -4. 运行完成后记录: - - DEM 模式 - - DEM 源根目录 - - 生成产物目录 - - 关键 DEM 文件是否生成成功 - -### 5.5 DEM 与前端的关系 - -不建议在前端让用户手工输入单次 DEM 路径。 - -推荐做法是: - -- 前端只展示“当前 DEM 策略” -- 例如: - - `本地 FABDEM` - - `OpenTopography` - - `未配置` -- 如果 DEM 不可用,则在 PyINT 引擎区阻断提交 - -## 6. 精密轨道方案 - -### 6.1 一期目标不是“假装已经真正进计算” - -当前 LT-1 PyINT 原生脚本没有明确消费系统精轨池的接口,因此一期要把目标定义准确: - -- 系统必须能按任务解析 master/slave 对应的精轨文件 -- 系统必须能知道精轨是否缺失 -- 系统必须把这次运行实际匹配到的精轨记录下来 -- 系统必须能根据策略决定“警告放行”还是“阻断提交” - -但不能在未完成桥接前,对外宣称“精轨已经真实参与 LT-1 PyINT 计算”。 - -### 6.2 一期建议的精轨策略 - -建议精轨配置分为: - -```ini -PYINT_ORBIT_POLICY=validate_only|require_txt|stage_txt -PYINT_ORBIT_POOL_TXT= -PYINT_RECORD_INPUT_ASSETS=true -``` - -默认建议: - -- `PYINT_ORBIT_POOL_TXT` 为空时默认继承 `ORBIT_POOL_ENVI` -- `PYINT_ORBIT_POLICY=require_txt` - -三种策略含义: - -- `validate_only` - - 找得到则记录 - - 找不到只警告 -- `require_txt` - - 找不到直接阻断运行 -- `stage_txt` - - 除了要求存在,还把匹配到的轨道文件复制或硬链接到本次运行目录 - -### 6.3 轨道解析规则 - -对每个 task,按如下顺序解析: - -1. 从 `.dinsar_pair.json`、原始压缩包文件名或元数据确定: - - 卫星 `LT1A/LT1B` - - `master_date` - - `slave_date` -2. 到系统轨道池中查找: - - `LT1A_GpsData_GAS_C_YYYYMMDD.txt` - - `LT1B_GpsData_GAS_C_YYYYMMDD.txt` -3. 分别解析 master/slave 结果 -4. 形成本次运行的轨道摘要 - -### 6.4 与现有轨道治理链路的关系 - -PyINT 不应新建第二套精轨目录。 - -应直接复用现有治理链路: - -- 源目录:`MONITOR_ORBIT_DIR` -- 运行池:`ORBIT_POOL_ENVI` -- 一致性修复:`orbit_converter.py` -- 健康检查:`health_service.py` - -也就是说: - -- PyINT 的精轨输入来源仍应是系统托管的轨道池 -- 不是让用户每次在前端再手工填一条轨道路径 - -### 6.5 一期的落地方式 - -建议每次运行都在 `input_assets/orbits/` 下落盘一个轨道摘要,例如: - -```json -{ - "policy": "require_txt", - "pool_root": "D:\\orbit_pools\\envi", - "master": { - "date": "20250112", - "satellite": "LT1A", - "path": "D:\\orbit_pools\\envi\\LT1A\\LT1A_GpsData_GAS_C_20250112.txt", - "staged_path": "...\\input_assets\\orbits\\LT1A_GpsData_GAS_C_20250112.txt", - "resolved": true - }, - "slave": { - "date": "20250309", - "satellite": "LT1A", - "path": "D:\\orbit_pools\\envi\\LT1A\\LT1A_GpsData_GAS_C_20250309.txt", - "staged_path": "...\\input_assets\\orbits\\LT1A_GpsData_GAS_C_20250309.txt", - "resolved": true - } -} -``` - -这一步先解决: - -- 任务是否可跑 -- 运行可追溯 -- 后续桥接可复用 - -### 6.6 二期的“真正参与计算”桥接 - -如果要让系统精轨真实参与 LT-1 PyINT/Gamma 计算,建议单独做一个技术 Spike,候选方向有两个: - -1. 修改或包装 PyINT 的 LT-1 导入步骤 - - 在 `down2slc_LT1.py` / `LT1_import_SLC_from_zipfiles1` 前后插入系统精轨桥接步骤 -2. 在 PyINT 前增加一个 LT-1 预处理适配器 - - 先把系统精轨和原始场景解析成更稳定的中间输入 - - 再把中间输入交给 PyINT 后续流程 - -建议优先方向是第 1 种,因为它改动面更小。 - -但在明确 Gamma 对 LT-1 外部精轨的实际消费方式之前,不建议直接承诺实现周期。 - -## 7. Gamma 配对集成的关系 - -`select_pairs.py` / Gamma 精配对本身是可集成的,但它不应绕开输入资产治理。 - -建议关系如下: - -1. 生产引擎侧先把 PyINT 的 DEM / 轨道输入治理打通。 -2. Gamma 精配对继续作为“配对规划之后的精化步骤”存在。 -3. 精配对任务默认继承同一套: - - WSL 环境 - - PyINT vendored 代码 - - 轨道治理配置 -4. 前端入口仍放在 `PairPlanningPanel`,不放进运维自检。 - -换句话说: - -- “生产引擎接入”是必要前置。 -- “Gamma 配对接入”是可行的,但它应复用同一套输入资产治理,而不是另起一套路径和配置。 - -## 8. 运行元数据、结果治理与数据库策略 - -### 8.1 一期不改数据库主结构 - -一期建议不改库,原因是: - -- 当前已有 `.dinsar_run.json` -- 当前已有 `pyint_run_summary.json` -- 当前已有结果 manifest / catalog - -这些已经足够承载输入资产摘要。 - -### 8.2 一期建议记录的内容 - -建议把以下信息写入运行元数据: - -- `input_assets.task_source` - - `root_dir` - - `task_dir` - - `archives.master[]` - - `archives.slave[]` -- `input_assets.dem` - - `mode` - - `source_root` - - `cache_root` - - `resolved_output_dir` - - `key_outputs` -- `input_assets.orbits` - - `policy` - - `pool_root` - - `master` - - `slave` - - `stage_mode` - -### 8.3 二期改库触发条件 - -只有出现以下需求时再改库: - -- 需要按 DEM 版本检索历史 PyINT 结果 -- 需要按轨道版本检索历史 PyINT 结果 -- 需要统计“某批结果使用了哪套轨道/DEM” -- 需要把 PyINT 输入资产做成后台长期查询对象 - -### 8.4 如果改库,必须走现有数据库自维护机制 - -如果进入二期改库,必须: - -1. 在 `backend/migrations/` 新增 SQL 迁移文件 -2. 在 `backend/app/db_maintenance.py` 的迁移列表中登记 -3. 让现有数据库自维护机制自动执行 - -不允许手工改表绕过现有机制。 - -## 9. 健康检查、接口与前端位置 - -### 9.1 运维自检面板只做状态,不做主操作入口 - -当前健康页已经比较重,因此新增内容应控制在“状态摘要”层面: - -- `PyINT enabled` -- `PyINT home` -- `PyINT WSL` -- `PyINT DEM strategy` -- `PyINT orbit policy` -- `PYINT_FABDEM_ROOT` 可读 -- `PYINT_ORBIT_POOL_TXT` / `ORBIT_POOL_ENVI` 可读 - -不建议新增: - -- 手工触发 PyINT 任务按钮 -- 手工填 DEM 路径 -- 手工填轨道路径 - -### 9.2 生产页的建议位置 - -在 `DinsarProductionPanel` 的 PyINT 引擎区域增加“输入资产预检摘要”,展示: - -- 识别到的任务数 -- 无效任务数 -- DEM 策略 -- 轨道策略 -- 已解析轨道数量 -- 缺失轨道数量 -- 是否允许提交 - -### 9.3 建议新增一个轻量预检接口 - -建议新增: - -- `POST /dinsar-production/engines/pyint/preview-input-assets` - -返回: - -- 任务解析结果 -- DEM 配置状态 -- 轨道解析状态 -- 阻断原因 -- 警告列表 - -这样前端可以在正式提交前给出明确反馈,而不是等任务进入队列后才失败。 - -### 9.4 PairPlanning 页的位置 - -Gamma 精配对仍建议放在 `PairPlanningPanel`: - -- 先显示已有粗配对网络 -- 再提供 Gamma 精配对入口 -- 不把它塞回健康检查页 - -## 10. 推荐实施顺序 - -### Phase 1:输入资产适配层 - -- 固化 `Task_*` 路径解析规则 -- 新增输入资产预检模型 -- 生成 `task_manifest.json` -- 生成 `input_assets/orbits/` 与 `input_assets/dem/` 目录 - -### Phase 2:DEM 正式接入 - -- 增加 `PYINT_DEM_MODE` -- 增加 `PYINT_FABDEM_ROOT` -- 在 template 生成时注入 `fabdem_dir` / `opentopo_*` -- 把 DEM 解析摘要写入 run metadata - -### Phase 3:精轨治理接入 - -- 增加 `PYINT_ORBIT_POLICY` -- 复用 `ORBIT_POOL_ENVI` -- 运行前做 master/slave 精轨解析 -- 缺失时按策略阻断 -- 把轨道文件 staging 到运行目录 - -### Phase 4:前端预检入口 - -- 生产面板显示资产预检摘要 -- 健康页只增加状态项 - -### Phase 5:精轨计算桥接 Spike - -- 研究 LT-1 PyINT/Gamma 当前导入链路如何真正消费外部精轨 -- 决定是补包装步骤还是补脚本修改 - -### Phase 6:Gamma 精配对集成 - -- 在配对规划页复用同一套 PyINT/Gamma 环境和资产治理策略 - -## 11. 最终建议 - -对于你提出的三个问题,建议明确回答如下: - -1. `Task_*` 路径怎么配合 - 继续沿用现在的任务目录,不改用户输入方式;系统内部新增适配层把任务目录转换为 PyINT 工作区。 - -2. DEM 怎么处理 - 一期就做系统托管,推荐以本地 FABDEM/DEM 源目录为标准输入,由 PyINT 在受控 `DEMDIR` 下生成本次任务真正使用的 DEM 产物。 - -3. 精密轨道怎么处理 - 一期先接入系统精轨池做校验、阻断、记录和 staging;二期再补“真实参与 LT-1 PyINT/Gamma 计算”的桥接。当前实现还不能直接把这一步视为已经完成。 - -基于当前代码现状,最稳妥的方向不是“删掉 Task_* 模式重新发明一套 PyINT 路径”,而是“把 Task_* 保留为业务输入,把 DEM/精轨补成系统托管资产适配层”。 - -## 补充:现有 DEM 复用实现(2026-04-19) - -当前代码已补充 `PYINT_DEM_MODE=prepared_file`,用于复用系统现有 DEM 资产。 - -- `PYINT_PREPARED_DEM_PATH` 可显式指定现有 DEM 基础文件。 -- 若该值为空,运行时会按顺序回退解析 `ISCE2_DEM_PATH` 和 `IDL_DINSAR_DEM_BASE_FILE`。 -- 如果目标文件同名存在 `.par`,则视为现成的 Gamma DEM,直接写入 PyINT 模板中的 `DEM=...`。 -- 如果目标文件没有 `.par`,但同名存在 `.xml`、`.hdr` 或 `.vrt`,则视为系统现有源 DEM。 -- 对“系统现有源 DEM”,PyINT 在 `makedem_pyint.py` 中会根据 `master` 的 `SLC_par` 覆盖范围先裁剪局部窗口,再转换为本次任务使用的 Gamma DEM。 -- 这样可以复用系统已经维护的中国区或全局 DEM,不必强制切回 FABDEM 或重新在线下载。 - -这个实现的约束也需要明确: - -- 现有源 DEM 仍必须是可被 GDAL 打开的本地文件。 -- 运行环境里仍需要 `gdal_translate` 可用,因为裁剪发生在 WSL/PyINT 侧。 -- 该模式的本质不是“直接把 ISCE2 DEM 原样交给 Gamma”,而是“把系统现有 DEM 当作受控源,再为每次 PyINT 任务生成 Gamma 可消费的局部 DEM 产物”。 diff --git a/docs/PYINT_LT1_PRECISE_ORBIT_BRIDGE_DESIGN_20260419.md b/docs/PYINT_LT1_PRECISE_ORBIT_BRIDGE_DESIGN_20260419.md deleted file mode 100644 index 1a520bd..0000000 --- a/docs/PYINT_LT1_PRECISE_ORBIT_BRIDGE_DESIGN_20260419.md +++ /dev/null @@ -1,405 +0,0 @@ -# PyINT LT-1 精密轨道桥接设计 - -**日期**: 2026-04-19 -**状态**: 方案设计 -**范围**: LT-1 精密轨道真正参与 PyINT / Gamma 计算、与现有 `Task_*` 输入模式协同、前后端与运维落点、分阶段实施 - -## 1. 结论 - -当前仓库已经完成了 LT-1 轨道 TXT 的治理级接入,但还没有完成“精密轨道真实参与 PyINT / Gamma 计算”这一层。 - -本次设计的核心结论如下: - -1. 不能把系统轨道池里的 `LT1*_GpsData_GAS_C_YYYYMMDD.txt` 简单当成 `par_LT1_SLC` 的直接输入,因为当前 PyINT / Gamma 的 LT-1 导入链并没有暴露这样的接口。 -2. 正确的桥接点是 LT-1 导入完成后生成的 `.slc.par` / `.slc.update.par` 里的 `state_vector_*` 段,而不是当前外层 `run_lt1_pyint_pipeline.py` 的任务参数层。 -3. 推荐方案是在现有 Windows 侧轨道治理不变的前提下,在 WSL 侧新增一个 LT-1 精轨桥接 helper,把系统选中的精轨 TXT 重采样到 Gamma 参数文件已有的时间栅格,再回写 `.slc.par`。 -4. 桥接动作必须发生在 LT-1 导入之后、DEM / coreg / 干涉处理之前;只做“提交前预检”或“运行记录留痕”是不够的。 -5. 一期先不改数据库,先把桥接结果写进 `input_assets`、`pyint_run_summary.json`、结果 manifest 和运行日志;只有在后续确实需要跨运行检索、统计、追责时,再通过现有数据库自维护机制补迁移。 - -## 2. 现状与依据 - -### 2.1 当前本地代码链路 - -现有 vendored PyINT 的 LT-1 流程为: - -`pyintApp.py` --> `down2slc_LT1_all.py` --> `down2slc_LT1.py` 或 `down2slc_cat_LT1.py` --> `LT1_import_SLC_from_zipfiles1` --> `par_LT1_SLC` / `par_LT1_SLC_YSLi` --> 生成 `.slc.par` / `.slc.update.par` - -已确认的关键事实: - -- [pyintApp.py](/D:/Code/Insar_management_system_v2/third_party/PyINT/pyint/pyintApp.py) 会先执行 LT-1 `raw2slc`,然后再进入 DEM、coreg、差分干涉。 -- [down2slc_LT1.py](/D:/Code/Insar_management_system_v2/third_party/PyINT/pyint/down2slc_LT1.py) 与 [down2slc_cat_LT1.py](/D:/Code/Insar_management_system_v2/third_party/PyINT/pyint/down2slc_cat_LT1.py) 是当前 LT-1 Python 入口。 -- [LT1_import_SLC_from_zipfiles1](/D:/Code/Insar_management_system_v2/third_party/PyINT/pyint/LT1_import_SLC_from_zipfiles1) 已经显式处理 `state_vector_*`,说明轨道状态向量确实是 LT-1 导入链中的有效控制点。 -- [20210110.slc.par](/D:/Code/Insar_management_system_v2/third_party/PyINT/pyint/20210110.slc.par) 展示了 Gamma 参数文件中的状态向量布局,包括: - - `number_of_state_vectors` - - `time_of_first_state_vector` - - `state_vector_interval` - - `state_vector_position_i` - - `state_vector_velocity_i` - -### 2.2 当前系统已有能力 - -仓库已经具备以下基础: - -- [pyint_input_assets_service.py](/D:/Code/Insar_management_system_v2/backend/app/services/pyint_input_assets_service.py) 已能从 `ORBIT_POOL_ENVI` / `PYINT_ORBIT_POOL_TXT` 解析 master/slave 对应的 LT-1 精轨 TXT。 -- [run_lt1_pyint_pipeline.py](/D:/Code/Insar_management_system_v2/backend/app/pyint_pipeline/run_lt1_pyint_pipeline.py) 已能把 `Task_*` 目录物化成 PyINT 工作区,并记录 `orbit_policy` 与 `input_assets`。 -- [DinsarProductionPanel.jsx](/D:/Code/Insar_management_system_v2/frontend/src/DinsarProductionPanel.jsx) 已经有 PyINT 输入资产预检入口,能够把“轨道是否齐全”提前暴露给用户。 -- ISCE2 侧已经有 LT-1 轨道 TXT 解析链,可复用 [convert_lt1_orbit_to_isce_xml.py](/D:/Code/Insar_management_system_v2/backend/app/isce2_pipeline/convert_lt1_orbit_to_isce_xml.py) 与 [lt1_input_resolver.py](/D:/Code/Insar_management_system_v2/backend/app/isce2_pipeline/lt1_input_resolver.py) 中的 `parse_orbit_file`、时间窗口解析等逻辑。 - -### 2.3 Gamma 官方文档给出的关键约束 - -用户提供的 Gamma 官方文档是: - -- - -其中与本设计直接相关的结论有两点: - -1. 在 LT-1 repeat-pass DInSAR 流程中,Gamma 文档明确说明,`par_LT1_SLC` 导入后需要立即检查并过滤 orbit state vectors,并使用 `ORB_filt_spline.py` 做校验。 -2. 在 LT-1 tandem single-pass 流程中,文档同样建议“读入数据后立刻检查/过滤状态向量”,以确保后续 MLI 参数和几何步骤使用的是修正后的状态向量。 - -这意味着桥接点必须放在“LT-1 导入完成之后立刻执行”,而不是只在外层运行摘要里记录轨道来源。 - -## 3. 当前缺口 - -当前实现还缺以下一层: - -1. 系统已经知道“这次任务应该用哪份精轨 TXT”,但 PyINT / Gamma 还不知道。 -2. 预检面板只能阻断“轨道缺失”的任务,不能保证“轨道已进入计算”。 -3. 只改外层 `run_lt1_pyint_pipeline.py` 不够,因为 PyINT 内部会自己完成 `raw2slc -> dem -> coreg` 连续流程,桥接必须插在内部 `raw2slc` 之后。 -4. `cat` 场景不能只更新单个 `.slc.par`。当前 [down2slc_cat_LT1.py](/D:/Code/Insar_management_system_v2/third_party/PyINT/pyint/down2slc_cat_LT1.py) 最终拼接依赖 `*.slc.update.par`,所以方案必须覆盖: - - 每个分片导入后的参数文件 - - 最终拼接得到的 `.slc.par` - -## 4. 目标与非目标 - -### 4.1 目标 - -- 保留当前 `Task_*` 输入模式,不要求用户维护第二套 PyINT 原生目录。 -- 让 LT-1 精轨 TXT 真正参与 PyINT / Gamma 计算,而不是只做治理留痕。 -- 同时覆盖单场景和多分片 `cat` 场景。 -- 与现有 DEM 策略、结果目录、运行日志、预检面板兼容。 -- 为后续 Gamma 配对集成保留复用路径。 - -### 4.2 非目标 - -- 一期不修改数据库主结构。 -- 一期不在运维自检页新增复杂操作区。 -- 一期不承诺完成 LT1A/LT1B tandem 单通道单程干涉生产链,只保证当前 repeat-pass PyINT 流程的精轨桥接。 -- 一期不让用户在前端手工输入单次轨道路径。 - -## 5. 推荐总体方案 - -### 5.1 分层思路 - -推荐把方案拆成“控制面”和“计算面”两层。 - -#### A. 控制面,继续由现有后端负责 - -控制面继续沿用现有资产治理链路: - -- 从 `Task_*` 解析 master/slave 的卫星与日期 -- 从 `PYINT_ORBIT_POOL_TXT` 或 `ORBIT_POOL_ENVI` 定位精轨 TXT -- 在 `input_assets/orbits/` 下留痕 -- 在预检接口中返回“是否可提交” - -这部分由现有 [pyint_input_assets_service.py](/D:/Code/Insar_management_system_v2/backend/app/services/pyint_input_assets_service.py) 继续承担。 - -#### B. 计算面,新增 WSL 侧精轨桥接 helper - -新增一个 helper,例如: - -- `backend/app/pyint_pipeline/apply_lt1_precise_orbit.py` - -其职责是: - -1. 读取当前任务已解析好的 orbit manifest / staged TXT。 -2. 读取目标 `.slc.par` 或 `.slc.update.par`。 -3. 复用现有 LT-1 TXT 解析逻辑,解析精轨状态向量。 -4. 以 Gamma 当前参数文件已有的时间栅格为目标,进行插值和回写。 -5. 备份原始参数文件。 -6. 可选调用 `ORB_filt_spline.py` 做二次校验或残差诊断。 -7. 输出 `orbit_bridge_summary.json`。 - -### 5.2 为什么目标时间栅格要复用 `.slc.par` 自身 - -推荐不要自己发明新的状态向量数量和时间间隔,而是直接复用当前 `.slc.par` 中已有的: - -- `number_of_state_vectors` -- `time_of_first_state_vector` -- `state_vector_interval` - -然后把系统精轨 TXT 插值到这个时间栅格上。 - -这样做的收益是: - -- 不改变 Gamma 已经生成的参数文件结构。 -- 不引入新的向量个数假设。 -- 更容易和 `ORB_filt_spline.py`、后续 DEM / coreg 步骤兼容。 -- 对 `cat` 场景、已有模板和下游脚本影响最小。 - -### 5.3 插值策略 - -推荐优先使用“基于位置和速度的 Hermite 插值”,原因是 LT-1 TXT 同时提供了位置和速度。 - -最小实现要求如下: - -- 先复用 [convert_lt1_orbit_to_isce_xml.py](/D:/Code/Insar_management_system_v2/backend/app/isce2_pipeline/convert_lt1_orbit_to_isce_xml.py) 的 `parse_orbit_file` 解析状态向量。 -- 根据 `.slc.par` 的采样时间点计算目标 UTC 时间序列。 -- 对目标时间序列进行状态向量重采样。 -- 回写 `state_vector_position_i` 和 `state_vector_velocity_i`。 - -如果一期为了稳妥,不想一次性引入更复杂的插值器,也可以先做: - -- 线性插值作为第一落地版 -- `ORB_filt_spline.py` 作为强校验 - -但长期建议还是切到 Hermite,以减少轨道形状失真。 - -## 6. 推荐挂接点 - -### 6.1 不推荐只改最外层 wrapper - -不推荐只在 [run_lt1_pyint_pipeline.py](/D:/Code/Insar_management_system_v2/backend/app/pyint_pipeline/run_lt1_pyint_pipeline.py) 里做轨道处理,因为它在 PyINT 看来只是外层启动器,无法插入到内部 `raw2slc` 与 `makedem` 之间。 - -### 6.2 推荐挂接点 - -推荐优先修改以下 vendored Python 脚本: - -- [down2slc_LT1.py](/D:/Code/Insar_management_system_v2/third_party/PyINT/pyint/down2slc_LT1.py) -- [down2slc_cat_LT1.py](/D:/Code/Insar_management_system_v2/third_party/PyINT/pyint/down2slc_cat_LT1.py) - -推荐执行时机: - -1. 每次 `LT1_import_SLC_from_zipfiles1` 完成后: - - 对当前生成的 `.slc.par` - - 对当前生成的 `.slc.update.par` - 执行一次桥接 -2. `SLC_cat_list.py` 生成最终 `.slc.par` 后: - - 再对最终参数文件执行一次桥接或至少一次强校验 - -这样可以同时满足: - -- 符合 Gamma 文档“导入后立即检查/过滤状态向量”的原则 -- 覆盖单片和多分片场景 -- 避免只在最终产物上补丁而遗漏拼接过程 - -`LT1_import_SLC_from_zipfiles1` 本身先不作为一期主改点,除非后续验证发现必须把逻辑进一步下沉到 shell 层才能完全覆盖。 - -## 7. 运行时流程 - -推荐的整体执行顺序如下: - -1. 用户在生产面板选择 PyINT 引擎并填写 `root_dir` -2. 后端调用 PyINT 输入资产预检 -3. 系统为每个 `Task_*` 解析: - - master/slave 日期 - - master/slave 卫星 - - 对应精轨 TXT - - DEM 策略 -4. `run_lt1_pyint_pipeline.py` 物化: - - `input_assets/orbits/` - - `task_manifest.json` - - PyINT 工作区和模板 -5. 外层 wrapper 将 orbit manifest 路径、helper 路径和桥接开关注入 WSL 环境 -6. PyINT 执行 `down2slc_LT1.py` / `down2slc_cat_LT1.py` -7. 每个 LT-1 导入步骤完成后,调用 `apply_lt1_precise_orbit.py` -8. helper 回写 `.slc.par` -9. PyINT 继续执行 DEM、coreg、差分干涉、解缠、地理编码 -10. 系统输出: - - `orbit_bridge_summary.json` - - `pyint_run_summary.json` - - 结果 manifest 摘要 - -## 8. 建议新增配置 - -当前已有: - -```ini -PYINT_ORBIT_POLICY=require_txt -PYINT_ORBIT_POOL_TXT= -``` - -建议新增或明确以下配置: - -```ini -PYINT_LT1_PRECISE_ORBIT_ENABLED=true -PYINT_LT1_PRECISE_ORBIT_MODE=replace_and_validate -PYINT_LT1_PRECISE_ORBIT_STRICT=true -PYINT_LT1_PRECISE_ORBIT_MARGIN_SECONDS=120 -PYINT_LT1_PRECISE_ORBIT_VALIDATE_WITH_ORB_FILT=true -PYINT_LT1_PRECISE_ORBIT_BACKUP=true -``` - -建议含义如下: - -- `PYINT_LT1_PRECISE_ORBIT_ENABLED` - - 是否启用真实桥接 -- `PYINT_LT1_PRECISE_ORBIT_MODE` - - `replace_and_validate` 为推荐默认值 - - 后续也可扩展 `validate_only` -- `PYINT_LT1_PRECISE_ORBIT_STRICT` - - 桥接失败时是否阻断任务 -- `PYINT_LT1_PRECISE_ORBIT_MARGIN_SECONDS` - - 对场景时间窗口额外扩展的秒数 -- `PYINT_LT1_PRECISE_ORBIT_VALIDATE_WITH_ORB_FILT` - - 是否调用 `ORB_filt_spline.py` 做残差校验 -- `PYINT_LT1_PRECISE_ORBIT_BACKUP` - - 是否在回写前备份原始 `.slc.par` - -## 9. 元数据与落盘策略 - -一期建议不改数据库,先把桥接痕迹记录到运行产物里。 - -### 9.1 `input_assets` 侧 - -建议在 `input_assets/orbits/` 下保留: - -- 解析到的 master/slave 精轨 TXT -- `orbit_resolution.json` -- `orbit_bridge_request.json` - -### 9.2 运行摘要侧 - -建议在 `pyint_run_summary.json` 中新增: - -```json -{ - "orbit_bridge": { - "enabled": true, - "mode": "replace_and_validate", - "status": "applied", - "master": { - "orbit_txt": "..." - }, - "slave": { - "orbit_txt": "..." - }, - "applied_files": [ - { - "path": ".../20250309.slc.par", - "role": "slave", - "vector_count": 15, - "validated": true - } - ] - } -} -``` - -### 9.3 结果 manifest 侧 - -结果 manifest 只保留摘要,不重复放大块明细,建议记录: - -- 是否启用精轨桥接 -- 桥接状态 -- master/slave 轨道来源 stem -- 是否通过 `ORB_filt_spline.py` 校验 - -### 9.4 数据库策略 - -一期不改数据库。 - -如果后续明确需要: - -- 按轨道版本检索历史运行 -- 统计桥接失败原因 -- 审计某次产品到底使用了哪份精轨 - -再通过现有 [db_maintenance.py](/D:/Code/Insar_management_system_v2/backend/app/db_maintenance.py) 机制新增迁移。 - -## 10. 前端与运维落点 - -### 10.1 前端 - -前端主入口继续放在现有 PyINT 生产区域,不新增独立页面。 - -建议在 [DinsarProductionPanel.jsx](/D:/Code/Insar_management_system_v2/frontend/src/DinsarProductionPanel.jsx) 的 PyINT 输入资产预检卡中增加两类信息: - -- 全局级: - - `精轨桥接: 已启用 / 仅治理 / 未启用` - - `桥接模式` -- 任务级: - - master/slave 是否已解析精轨 - - 本次是否满足真实桥接前置条件 - -不要让用户手工输入 orbit 路径。 - -### 10.2 运维自检 - -运维自检页不再承载新的操作区,只保留状态摘要。 - -建议在引擎健康或 PyINT 健康项里补充: - -- `PYINT_LT1_PRECISE_ORBIT_ENABLED` -- helper 脚本是否存在 -- `PYINT_ORBIT_POOL_TXT` / `ORBIT_POOL_ENVI` 是否可读 -- `ORB_filt_spline.py` 是否可调用 - -不建议把桥接按钮堆进现有 [HealthCheckPanel.jsx](/D:/Code/Insar_management_system_v2/frontend/src/HealthCheckPanel.jsx)。 - -## 11. 与 Gamma 配对集成的关系 - -这套桥接不是只服务 D-InSAR 生产,也是在为后续 Gamma 配对打基础。 - -原因是: - -- 如果后续要把 Gamma / PyINT 配对结果真正纳入系统,配对阶段对 baseline 和场景几何的一致性要求会更高。 -- 只做“轨道存在性预检”仍然不够,仍然需要一条“导入后立即修正状态向量”的内部链路。 - -因此推荐把本次 helper 设计成通用能力: - -- 当前用于 `pyintApp.py` 的 LT-1 `raw2slc` -- 后续也可复用于 `select_pairs.py` 前的 LT-1 导入准备 - -## 12. 风险与未决问题 - -当前仍有几项需要在实现阶段验证: - -1. LT-1 TXT 的时间系统与 `.slc.par` 的 `date + seconds-of-day` 是否存在跨日边界问题。 -2. `ORB_filt_spline.py` 更适合用于“替换后校验”还是“替换后再执行一次修正”,需要先做小样本验证。 -3. `SLC_cat_list.py` 当前并未 vendored 到仓库中,实现阶段要进一步确认它对输入 `.par` 的依赖细节。 -4. 当前 repeat-pass 流程与 LT1A/LT1B tandem single-pass 流程并不完全等价,后者需要单独设计。 -5. 如果发现某些场景的 Gamma 原始导入时间栅格明显不合理,可能需要从“复用现有时间栅格”升级到“按 scene window 重新构造时间栅格”。 - -## 13. 分阶段实施建议 - -### Phase 1 - -- 新增 `apply_lt1_precise_orbit.py` -- 复用现有 LT-1 TXT 解析器 -- 实现 `.slc.par` 读取、备份、状态向量回写 -- 产出 `orbit_bridge_summary.json` - -### Phase 2 - -- 修改 `down2slc_LT1.py` 和 `down2slc_cat_LT1.py` -- 在导入后与最终拼接后调用 helper -- 跑通单任务 smoke test - -### Phase 3 - -- 在运行摘要和结果 manifest 中纳入桥接信息 -- 在生产面板预检区域增加“精轨桥接已启用”可见性 -- 在 health 中增加最小状态摘要 - -### Phase 4 - -- 用真实 LT-1 样本比较桥接前后: - - `ORB_filt_spline.py` 残差 - - 后续 coreg 质量 - - 干涉相位整体趋势 -- 评估是否为 Gamma 配对链复用相同 helper - -## 14. 最终建议 - -推荐按以下原则推进: - -1. 继续保留现有 `Task_*` 输入模式和轨道治理链。 -2. 把 LT-1 精轨接入点明确落到 `.slc.par` 的 `state_vector_*` 回写,不再停留在治理层。 -3. 一期先实现“桥接 helper + vendored LT-1 raw2slc 挂接 + 运行元数据留痕”。 -4. 数据库先不动,运维面板先不扩张。 -5. 桥接 helper 从第一天起就按“未来可复用于 Gamma 配对”来设计接口。 diff --git a/docs/SBAS_INSAR_CURRENT_WORKFLOW.md b/docs/SBAS_INSAR_CURRENT_WORKFLOW.md new file mode 100644 index 0000000..313940a --- /dev/null +++ b/docs/SBAS_INSAR_CURRENT_WORKFLOW.md @@ -0,0 +1,246 @@ +# SBAS-InSAR 当前工作流 + +最后更新:2026-05-28 + +本文件是当前 SBAS-InSAR 生产和结果管理的事实文档。旧的 ISCE2/MintPy 时序生产、早期 SBAS 设计草案、实验 runbook 和阶段性记录不再作为当前依据。 + +## 1. 当前结论 + +- 当前 SBAS-InSAR 主线是 Gamma DIFF + IPTA SBAS。 +- 前端入口在“生产管理”工作台内: + - `SBAS-InSAR Production` + - `SBAS-InSAR 结果` +- 后端入口: + - `/api/sbas-insar-production` + - `/api/sbas-insar-products` +- 旧 `/api/timeseries-production`、旧 `ps_production`、旧 `ps_products` 只作为兼容代码保留,不再作为生产管理主入口。 +- 专家文档 `LT1_GAMMA_SBAS_逐命令处理流程.docx` 是 Gamma 命令链路来源;系统实现负责把专家脚本组织成可配置、可审查、可重跑的生产流水线。 + +## 2. 代码入口 + +后端: + +```text +backend/app/routers/sbas_insar_production.py +backend/app/services/sbas_insar_production_service.py +backend/app/routers/sbas_insar_products.py +backend/app/services/sbas_insar_catalog_service.py +deploy/wsl/runners/gamma_sbas_product_tools.py +``` + +前端: + +```text +frontend/src/ProductionWorkspace.jsx +frontend/src/SbasInsarProductionPanel.jsx +frontend/src/SbasInsarProductsPanel.jsx +frontend/src/api/sbasInsarProducts.js +frontend/src/config/appConstants.js +``` + +启动自维护: + +```text +backend/app/main.py + -> sbas_insar_catalog_service.bootstrap_catalog_on_startup_clean() +``` + +## 3. 运行时配置 + +核心配置从 `.env` 读取: + +```text +GAMMA_SBAS_ENABLED +GAMMA_SBAS_RUNTIME_ID +GAMMA_SBAS_WSL_DISTRO +GAMMA_SBAS_PYTHON +GAMMA_SBAS_ENV_SCRIPT +GAMMA_SBAS_WORK_ROOT +GAMMA_SBAS_PRODUCT_ROOT +GAMMA_SBAS_SCRIPT_TEMPLATE_ROOT +GAMMA_SBAS_SOURCE_ROOTS +GAMMA_SBAS_ORBIT_ROOTS +GAMMA_SBAS_DEFAULT_RLKS +GAMMA_SBAS_DEFAULT_AZLKS +GAMMA_SBAS_DEFAULT_MB_MODE +GAMMA_SBAS_DEFAULT_REFERENCE_WINDOW +GAMMA_SBAS_STEP_TIMEOUT_SECONDS +GAMMA_SBAS_WORKFLOW_TIMEOUT_SECONDS +``` + +默认工作根: + +```text +backend/runtime/sbas_insar_production +``` + +默认产品根: + +```text +D:\production_results\timeseries\sbas +``` + +## 4. 生产流程 + +用户侧推荐顺序: + +1. 在 `SBAS-InSAR Production` 输入行政区,查找候选序列。 +2. 选择 READY 候选,生成 Stack Manifest。 +3. 创建计划 Run。 +4. 提交 `Gamma SBAS Workflow` 后台任务。 +5. 审查基线和 itab。 +6. 执行后续 Gamma 阶段。 +7. 发布产品并抽取监测点。 +8. 到 `SBAS-InSAR 结果` 查看结果和下载资产。 + +当前托管阶段: + +```text +prepare_slc +baseline_audit +coregistration +rdc_dem +interferograms +detrend_atm +ipta_timeseries +publish_products +monitor_points +``` + +`workflow/jobs` 是主入口,旧的逐阶段接口保留用于兼容、排错和历史 Run 读取。 + +## 5. AOI 选栈 + +当前页面面向用户显示“生产区域”,不是开发用的中心桶或硬分组。 + +发现接口支持: + +```text +discovery_mode = strict | aoi +admin_region +aoi_bbox +min_aoi_coverage_ratio +min_common_overlap_ratio +``` + +AOI 模式处理逻辑: + +1. 将行政区解析为 AOI 几何。 +2. 使用 LT1 元数据 bbox 筛选与 AOI 相交的影像。 +3. 按观测几何分组: + - satellite + - satellite_mode + - relative_orbit + - orbit_direction + - imaging_mode + - polarization +4. 后端内部按 footprint 公共重叠继续做空间聚类。 +5. 输出日期范围、可用景数、缺精轨数、最大时间间隔、公共重叠和 AOI 覆盖摘要。 + +`center_bucket` 和 `receiving_station` 是内部诊断字段,不作为用户生产入口展示。 + +## 6. 产物契约 + +一次完成的 SBAS Run 是一个结果产品包。核心资产包括: + +```text +run_manifest.json +stack_manifest.json +pair_network.json +gamma_command_manifest.json +workflow_summary.json +product_summary.json +quality_summary.json +monitor_points_summary.json + +publish/geotiff/los_rate_toward_m_per_year.tif +publish/geotiff/los_rate_away_m_per_year.tif +publish/geotiff/los_sigma_m_per_year.tif +publish/geotiff/los_rate_toward_m_per_year.hls.geo_preview.png +publish/geotiff/los_sigma_m_per_year.cc.geo_preview.png +publish/vectors/los_rate_points.geojson.gz +publish/vectors/los_rate_points_summary.json +publish/monitor_points/*_timeseries.png +publish/monitor_points/*_timeseries.csv +publish/monitor_points/*_metadata.json +``` + +结果页默认展示: + +- LOS 速率预览图; +- LOS sigma 预览图; +- 监测点曲线; +- 点矢量下载入口; +- 产品资产列表; +- 行政区、中心点、日期范围、景数和配对数。 + +全量点 GeoJSON.gz 只提供下载,前端不渲染全量点。 + +## 7. LOS 符号约定 + +Gamma `ts_rate` 输出是相位速率,系统同时保留两种 LOS 约定: + +```text +los_rate_away_mm_per_year = phase_rate * wavelength / (4*pi) * 1000 +los_rate_toward_mm_per_year = -phase_rate * wavelength / (4*pi) * 1000 +``` + +默认展示: + +```text +LOS toward radar positive +``` + +即朝向雷达为正,远离雷达为负。该约定接近 Gamma `dispmap` 默认 `sflg=0` 的表达习惯。 + +## 8. 结果目录与 catalog + +SBAS 结果 catalog 复用通用结果表: + +```text +result_products +result_assets +result_issues +result_catalog_state +``` + +SBAS catalog 名称: + +```text +sbas_insar +``` + +结果管理接口: + +```text +GET /api/sbas-insar-products/catalog-status +POST /api/sbas-insar-products/rebuild-catalog +GET /api/sbas-insar-products +GET /api/sbas-insar-products/{product_id} +GET /api/sbas-insar-products/{product_id}/assets/{asset_id} +``` + +启动时会自动扫描已完成 Run 并维护 catalog 状态。 + +## 9. 旧链路状态 + +以下内容不再作为当前生产事实: + +- `experiments/isce2_sbas_timeseries` +- ISCE2 + MintPy 时序生产链 +- SARscape SBAS 时序方案 +- 旧 `ps_production` / `ps_products` 生产页面设计 +- 旧 `geo_timeseries.h5` 作为当前 SBAS 主产品的设计 + +旧代码可保留兼容,但新功能、测试和文档默认围绕 Gamma SBAS 独立生产链路展开。 + +## 10. 当前验证基线 + +截至 2026-05-28 已验证: + +- Gamma SBAS 实验链路可跑通; +- 后端 AOI 候选发现可按行政区返回 READY 候选; +- 牡丹江 AOI 可发现超过旧严格分组 7 景上限的候选; +- 选中候选可生成 Stack Manifest; +- 结果发布支持 GeoTIFF、预览图、监测点曲线和点矢量下载; +- 前端生产页和结果页构建通过。 diff --git a/docs/SBAS_INSAR_GEOJSON_RESULT_MANAGEMENT_DESIGN_20260527.md b/docs/SBAS_INSAR_GEOJSON_RESULT_MANAGEMENT_DESIGN_20260527.md deleted file mode 100644 index aae2f4b..0000000 --- a/docs/SBAS_INSAR_GEOJSON_RESULT_MANAGEMENT_DESIGN_20260527.md +++ /dev/null @@ -1,675 +0,0 @@ -# SBAS-InSAR GeoJSON Coverage And Result Management Design - -Date: 2026-05-27 - -## 1. Purpose - -Gamma SBAS-InSAR production has completed an end-to-end twelve-node run. The next step is to make production and results usable by geography, not only by time sequence and run status. - -This design defines: - -- how production pages show approximate geographic coverage with GeoJSON/bbox; -- how users select administrative regions or AOI to discover and produce SBAS stacks on demand; -- how completed SBAS runs become result products with searchable geographic extent; -- how the results page should display LOS velocity, LOS sigma, and monitoring-point curves. - -## 2. Current Facts - -The current successful run already contains usable geographic metadata: - -```text -run_id = sbas_7537cc71c998 -stack center bucket = E129.2_N44.1 -stack bbox = 128.7690438245, 43.7486321624, 129.6293024728, 44.3582486206 -monitor point = 129.10207098755, 44.15041727515 -``` - -Available metadata sources: - -```text -stack_manifest.scenes[*].bbox -stack_manifest.scenes[*].center_lon / center_lat -stack_manifest.stack.center_bucket -rdc_dem_summary.dem_source.stack_bbox -monitor_points_summary.monitor_outputs[*].metadata.approx_lonlat -published GeoTIFF bounds from GDAL metadata -``` - -Existing platform capabilities: - -```text -backend/geojson/全国行政区.geojson -backend/geojson/层级映射.json -GET /api/aoi/regions/children -GET /api/aoi/regions/{tree_id}/geometry -backend AOI helpers for region_tree_id / GeoJSON / uploaded AOI parsing -frontend Leaflet map and L.geoJSON support -frontend App.jsx existing source-scene footprint and AOI overlay patterns -``` - -The missing piece is productized SBAS-specific coverage and catalog behavior. - -## 3. Design Principles - -1. SBAS production is temporal, but SBAS result consumption is geographic. -2. Production UI should answer "am I processing the right place?" before a long workflow is submitted. -3. Results UI should answer "where is this product, what time range does it cover, and can I download the main outputs?" -4. One completed SBAS run is one result product bundle, not one product per GeoTIFF. -5. GeoJSON/bbox coverage is enough for the first production UX. Full raster map rendering can come later. -6. Administrative-region filtering should reuse existing AOI infrastructure rather than introduce a parallel region system. - -## 4. Coverage Model - -### 4.1 Stack Coverage - -For stack discovery and production run display: - -```json -{ - "center": {"lon": 129.199, "lat": 44.053}, - "bbox": { - "min_lon": 128.769, - "min_lat": 43.749, - "max_lon": 129.629, - "max_lat": 44.358 - }, - "bbox_geojson": { - "type": "Feature", - "properties": {"role": "sbas_stack_bbox"}, - "geometry": { - "type": "Polygon", - "coordinates": [[ - [128.769, 43.749], - [129.629, 43.749], - [129.629, 44.358], - [128.769, 44.358], - [128.769, 43.749] - ]] - } - }, - "scene_bbox_count": 7, - "scene_footprints_geojson": null -} -``` - -First implementation may use stack bbox only. Per-scene rectangles can be added when the map needs to show coverage stability. - -### 4.2 Product Coverage - -For completed results: - -```json -{ - "coverage_source": "stack_manifest_bbox_union", - "geotiff_bounds_verified": true, - "bbox": {...}, - "center": {...}, - "footprint_geojson": {...}, - "administrative_hint": { - "province": "黑龙江省", - "city": null, - "county": null, - "method": "center_point_lookup" - } -} -``` - -The first administrative hint can be center-point based. Later it should become intersection-based and return all intersected regions with approximate overlap area. - -## 5. Administrative Region And AOI Production - -### 5.1 User Workflow - -Production page should support three AOI sources: - -```text -1. Administrative region selection - province -> city -> county, backed by /aoi/regions endpoints - -2. Map-drawn rectangle - converted to bbox GeoJSON - -3. Uploaded GeoJSON/SHP - reuse existing AOI parsing helpers -``` - -The user flow: - -```text -select AOI / administrative region -discover SBAS stack candidates -show candidate time density + geographic coverage -select candidate -create production Run -submit Gamma SBAS workflow -``` - -### 5.2 Discovery Filter - -Stack discovery should accept: - -```json -{ - "region_tree_id": "230000", - "aoi_geojson": {}, - "aoi_bbox": { - "min_lon": 128.7, - "min_lat": 43.7, - "max_lon": 129.7, - "max_lat": 44.4 - }, - "aoi_overlap_min": 0.0, - "stable_stack_overlap_min": 0.3 -} -``` - -Initial filtering can use bbox intersection: - -```text -candidate stack is valid when union_bbox intersects AOI bbox -``` - -Second-stage filtering should use polygon intersection: - -```text -candidate_score += common_stack_area_intersection_ratio -candidate_score += scene_count / temporal_density -candidate_score -= sparse_time_gap_penalty -``` - -### 5.3 Production Guardrails - -Before running workflow: - -```text -show bbox and administrative hint -show date list and max temporal gap -show DEM coverage status -warn when DEM covers center but not full stack bbox -warn when AOI overlap is low -``` - -The current run demonstrated a real issue: - -```text -DEM source covers stack center = true -DEM source covers full stack bbox = false -``` - -This must be visible in the production page before the user trusts the result. - -## 6. Production Page UI - -Add `Geographic Coverage` block to `SbasInsarProductionPanel`. - -Minimum fields: - -```text -Center: lon / lat -BBox: min_lon, min_lat, max_lon, max_lat -Scene footprints: count -DEM coverage: bbox / center -Administrative hint -Monitor points if generated -Actions: view on map, copy GeoJSON, zoom to footprint -``` - -Map preview options: - -```text -Phase 1: small unframed Leaflet map with rectangle overlay -Phase 2: shared main map overlay using existing App.jsx layer mechanisms -Phase 3: per-scene footprints and monitor points overlay -``` - -The production page should not become the result browser. It only provides enough geographic context to avoid wrong-location production. - -## 7. Result Management Module - -### 7.1 Module Boundary - -Create a separate SBAS-InSAR result management module: - -```text -navigation: Results / SBAS-InSAR Results -backend catalog: catalog_name = sbas_insar -product type: sbas_insar_bundle -source: completed Gamma SBAS production Run -``` - -Do not merge SBAS result semantics into D-InSAR result pages. Reuse the common catalog tables but keep service, API, and frontend module separate. - -### 7.2 Product Row - -One completed run becomes one product row: - -```text -result_products.catalog_name = sbas_insar -result_products.run_key = sbas run_id -result_products.engine_code = gamma -result_products.processor_code = gamma_ipta_sbas -result_products.display_name = platform / orbit / area / date span -``` - -Required product metadata: - -```text -platform -relative_orbit -orbit_direction -polarization -reference_date -start_date -end_date -scene_count -pair_count -bbox_min_lon / bbox_min_lat / bbox_max_lon / bbox_max_lat -center_lon / center_lat -administrative_hint -status -health_status -source_run_id -``` - -### 7.3 Asset Roles - -Primary assets from the expert document: - -```text -primary_velocity_geotiff - current: publish/geotiff/los_rate_toward_m_per_year.tif - expert equivalent: geo_los_def_rate.tif - -primary_velocity_preview - current: publish/geotiff/los_rate_toward_m_per_year.hls.geo_preview.png - expert equivalent: los_def_rate.bmp / geo_los_def_rate.bmp - -primary_velocity_rgb_geotiff - current: publish/geotiff/los_rate_toward_m_per_year.hls.geo_rgb.tif - expert equivalent: geo_los_def_rate_rgb.tif - -quality_sigma_geotiff - current: publish/geotiff/los_sigma_m_per_year.tif - expert equivalent: diff.sigma_ts / geo_diff.sigma_ts - -quality_sigma_preview - current: publish/geotiff/los_sigma_m_per_year.cc.geo_preview.png - expert equivalent: diff.sigma_ts.masked.bmp with cc.cm - -monitor_timeseries_csv - current: publish/monitor_points/*_timeseries.csv - expert equivalent: disp_prt_2d output table - -monitor_timeseries_plot - current: publish/monitor_points/*_timeseries.png - -support_manifest - run_manifest.json, workflow_summary.json, gamma_command_manifest.json -``` - -Default result display should foreground: - -```text -LOS velocity preview -LOS sigma preview -monitor point curve if present -footprint map -``` - -Audit/support files should be grouped separately. - -## 8. Backend Design - -### 8.1 Service - -Add: - -```text -backend/app/services/sbas_insar_catalog_service.py -``` - -Responsibilities: - -```text -scan completed SBAS run directories -validate product bundle readiness -derive bbox/center/footprint GeoJSON -extract or verify GeoTIFF bounds -upsert result_products/result_assets/result_issues -provide list/detail/download APIs -bootstrap self-maintenance on startup -``` - -### 8.2 API - -Add: - -```text -GET /api/sbas-insar-products/catalog-status -POST /api/sbas-insar-products/rebuild-catalog -GET /api/sbas-insar-products -GET /api/sbas-insar-products/{product_id} -GET /api/sbas-insar-products/{product_id}/assets/{asset_id} -``` - -List filters: - -```text -date_from -date_to -reference_date -platform -relative_orbit -orbit_direction -status -health_status -region_tree_id -aoi_bbox -aoi_geojson -intersects_bbox -has_monitor_points -limit / offset -``` - -### 8.3 Run Detail Extension - -Extend production run detail: - -```json -{ - "geographic_coverage": { - "center": {"lon": 129.199, "lat": 44.053}, - "bbox": {"min_lon": 128.769, "min_lat": 43.749, "max_lon": 129.629, "max_lat": 44.358}, - "bbox_geojson": {}, - "scene_bbox_count": 7, - "dem_covers_stack_bbox": false, - "dem_covers_stack_center": true, - "monitor_points": [{"point_id": "auto_low_sigma_high_rate", "lon": 129.102, "lat": 44.150}] - } -} -``` - -### 8.4 Startup Self-Maintenance - -On startup: - -```text -ensure result catalog tables exist through existing maintenance -bootstrap sbas_insar catalog state -scan completed SBAS run publish bundles -upsert missing product rows -record issues for: - missing bbox - missing primary velocity GeoTIFF - missing primary preview - missing sigma GeoTIFF - missing sigma preview - missing monitor files when monitor summary says ready - DEM coverage mismatch -``` - -This matches the current self-maintenance direction used by D-InSAR and PsInSAR catalogs. - -## 9. Frontend Design - -### 9.1 Production Page - -Add: - -```text -GeographicCoveragePanel - bbox text - center text - administrative hint - DEM coverage status - mini map rectangle - copy GeoJSON - zoom/open on main map -``` - -Candidate stack cards should show: - -```text -date span -scene count -center bucket -center lon/lat -bbox short text -AOI overlap indicator when AOI is selected -``` - -### 9.2 Result Page - -Add: - -```text -SbasInsarProductsPanel -``` - -List page: - -```text -filters: date range, administrative region, map AOI, platform, orbit, status -cards/table: product name, date span, bbox/admin hint, scene/pair count, preview thumbnail, health badge -actions: open detail, zoom to map, download primary GeoTIFF -``` - -Detail page: - -```text -footprint map -LOS velocity preview -LOS sigma preview -monitor curve -key metadata table -asset table grouped by role -quality and issue summary -link to production Run -``` - -### 9.3 Map Layer Strategy - -Use GeoJSON for the first implementation: - -```text -bbox polygon for stack/product footprint -administrative region boundary layer from existing AOI endpoints -monitor point markers -optional per-scene footprints -``` - -Do not display RDC BMP as map layer. Only geocoded preview PNG/GeoTIFF-derived bounds should be used for map-oriented display. - -## 10. Implementation Sequence - -Recommended order: - -```text -1. Backend geographic_coverage in SBAS run detail. -2. Production page coverage block and bbox GeoJSON mini-map. -3. Add AOI/region filters to SBAS stack discovery request/response. -4. Add sbas_insar_catalog_service and catalog rebuild API. -5. Register current completed run as first SBAS result product. -6. Build SbasInsarProductsPanel list/detail. -7. Add map AOI filtering and administrative-region filtering to result page. -8. Add startup bootstrap/self-check output. -``` - -The first user-visible win is step 1-2: the operator can immediately see whether a Run covers the intended location. - -## 10.1 Implementation Note 2026-05-27 - -Implemented the first slice after commit `9f0ba32`: - -```text -backend/app/services/sbas_insar_production_service.py -frontend/src/SbasInsarProductionPanel.jsx -``` - -Backend now returns `geographic_coverage` from `GET /api/sbas-insar-production/runs/{run_id}`. The field is derived from `stack_manifest.json`, `rdc_dem_summary.json`, and `monitor_points_summary.json` without changing the Gamma expert workflow outputs. - -The returned structure includes: - -```text -bbox -bbox_intersection -center -scene_bbox_count -scene_footprints_geojson -dem_coverage -dem_covers_stack_bbox -dem_covers_stack_center -monitor_points -geojson FeatureCollection -``` - -Frontend now displays the coverage block in two places: - -```text -candidate stack discovery detail -selected production Run detail -``` - -The mini-map uses the existing Leaflet/offline tile configuration and draws: - -```text -stack bbox rectangle -DEM coverage rectangle when available -monitor point markers when available -``` - -Validation against `sbas_7537cc71c998`: - -```text -geographic_coverage.bbox = 128.7690438245,43.7486321624,129.6293024728,44.3582486206 -geojson feature count = 5 -monitor point = auto_low_sigma_high_rate, 129.10207098755,44.15041727515 -backend AST syntax check passed with configured Python -frontend npm run build passed -``` - -Remaining result-management work starts at catalog registration and a separate SBAS products page. - -## 10.2 Implementation Note 2026-05-27 Result Catalog - -Implemented the SBAS result-management slice: - -```text -backend/app/services/sbas_insar_catalog_service.py -backend/app/routers/sbas_insar_products.py -frontend/src/api/sbasInsarProducts.js -frontend/src/SbasInsarProductsPanel.jsx -``` - -Backend behavior: - -```text -catalog_name = sbas_insar -storage root = GAMMA_SBAS_WORK_ROOT/runs -source of truth = completed Gamma SBAS run folders -startup bootstrap = scan publish-ready runs and rebuild index when stale -manual rebuild = POST /api/sbas-insar-products/rebuild through job queue -list/detail = GET /api/sbas-insar-products and /{id} -asset serving = /api/sbas-insar-products/{id}/assets/{asset_id} -``` - -The catalog registers only database metadata and file pointers. It does not copy the large Gamma outputs. - -Important registered assets: - -```text -LOS velocity geocoded preview -LOS sigma geocoded preview -LOS velocity GeoTIFF, toward radar positive -LOS velocity GeoTIFF, away from radar positive -LOS sigma GeoTIFF -Gamma ts_rate and sigma_rate GeoTIFFs -monitor-point PNG/CSV/metadata -run, stack, workflow, product, quality, and monitor summaries -``` - -Frontend behavior: - -```text -Production Management -> SBAS-InSAR 结果 -catalog health cards -searchable result list -result detail -coverage map with stack bbox, DEM bbox, and monitor points -velocity/sigma/monitor preview panels -quality statistics -asset download/open links -issue list -``` - -Known next slices: - -```text -AOI / administrative-region filter for list and discovery -GeoTIFF raster overlay or server-side tile generation -multi-monitor-point comparison view -explicit orbit-trend/detrend quality diagnostics -``` - -## 11. Validation - -Use `sbas_7537cc71c998` as the first validation run. - -Checks: - -```text -geographic_coverage.bbox is present -bbox_geojson draws a rectangle in Leaflet -admin hint is present or explicitly unknown -DEM coverage warning is visible when bbox is not fully covered -result product row exists after catalog rebuild -result detail opens velocity/sigma previews -asset downloads work -map zoom to footprint works -AOI filter returns this product when AOI intersects bbox -AOI filter excludes this product when AOI is far away -``` - -## 12. 2026-05-27 Center-Region UI Closeout - -The gray footprint maps are no longer the primary SBAS UI contract. Production planning and result management now show a location summary instead: - -```text -center lon/lat -center administrative region -stack bbox and common-overlap bbox as text -scene footprint count -monitor point count -``` - -Administrative lookup uses the existing `backend/geojson` AOI region data. The lookup starts with center-point containment, repairs invalid administrative geometries where possible, and falls back to a clear unavailable/not-matched state instead of blocking SBAS production. - -The SBAS result catalog now extracts dates from `stack_manifest.scenes[*].date`. This fixes the old list symptom: - -```text -before: - 至 - / 0景 / 6对 -after : 20240422 至 20250908 / 7景 / 6对 -``` - -The current validation run `sbas_7537cc71c998` rebuilt successfully into the result catalog and matched: - -```text -center = 129.1949239855143, 44.053833584014285 -admin region = 黑龙江省 / 牡丹江市 -date range = 20240422 至 20250908 -scene/pair count = 7 / 6 -``` - -## 13. Open Questions - -1. Administrative-region naming should start with center-point lookup or intersection lookup? - Recommendation: center-point lookup first, intersection later. - -2. Should stack discovery require AOI overlap, or only rank by AOI overlap? - Recommendation: default to rank/filter by bbox intersection, expose minimum overlap later. - -3. Should results use `sbas_insar` or `psinsar` catalog name? - Recommendation: use `sbas_insar`. The current Gamma product is SBAS-InSAR, and old `psinsar` catalog semantics should not be overloaded. - -4. Should GeoTIFF raster be rendered on map immediately? - Recommendation: not in the first slice. Start with footprint GeoJSON and preview images; raster tile rendering can be added after catalog registration is stable. diff --git a/docs/SBAS_INSAR_POINT_VECTOR_AND_MONITOR_CURVE_DESIGN_20260528.md b/docs/SBAS_INSAR_POINT_VECTOR_AND_MONITOR_CURVE_DESIGN_20260528.md deleted file mode 100644 index 105cad9..0000000 --- a/docs/SBAS_INSAR_POINT_VECTOR_AND_MONITOR_CURVE_DESIGN_20260528.md +++ /dev/null @@ -1,137 +0,0 @@ -# SBAS-InSAR 点矢量导出与多监测点曲线设计 - -## 背景 - -当前 Gamma SBAS 专家路径已经产出 LOS 形变速率、LOS sigma、RGB 预览和单个自动监测点曲线。论文和报告中常见的表达方式不是只展示一个自动点,而是以 LOS 速率栅格为主图,并配合若干代表点的时序曲线、质量图和统计说明。 - -专家文档第十二步“结果输出、地理编码与点位时序”给出的标准路径包括: - -- `ts_rate` 计算平均形变速率; -- `rasdt_pwr` 生成速率预览图; -- `geocode_back` 地理编码速率结果; -- `data2geotiff` 输出 GeoTIFF; -- `disp_prt_2d` 根据 `disp_point.txt` 输出点位时序。 - -专家文档没有要求把所有有效像元直接矢量化。全量点矢量属于发布产物扩展,不改变 Gamma/SBAS 计算链路。 - -## 目标 - -1. 保持 GeoTIFF 作为可信主产品。 -2. 新增全量有效像元点 GeoJSON.gz,供用户下载后在 QGIS、ArcGIS、Python 或精细制图流程中使用。 -3. 前端不渲染全量点,只展示文件、点数、字段和下载入口。 -4. 默认自动监测点从 1 个扩展为多个代表点,便于结果页展示多条时序曲线。 - -## 非目标 - -- 不把全量点 GeoJSON 作为前端地图图层渲染。 -- 不用点矢量替代 LOS 速率 GeoTIFF。 -- 不把自动点解释为专家确认点、业务监测网或最终工程控制点。 - -## 点矢量产品 - -输出目录: - -```text -publish/vectors/ - los_rate_points.geojson.gz - los_rate_points_summary.json -``` - -点定义: - -- 来源:地理编码后的 `los_rate_toward_mm_per_year.tif`、`los_rate_away_mm_per_year.tif`、`los_sigma_mm_per_year.tif`。 -- 一个有效像元中心点对应一个 GeoJSON Feature。 -- 有效条件:速率、sigma 为有限数值,且不是 NoData/0 掩膜值。 - -字段: - -```text -run_id -row -col -lon -lat -los_rate_toward_mm_per_year -los_rate_away_mm_per_year -los_sigma_mm_per_year -date_start -date_end -reference_date -admin_province -admin_city -``` - -summary 字段: - -```text -schema -generated_at -ready -feature_count -output_geojson_gz -fields -source_geotiffs -date_start -date_end -reference_date -los_convention -``` - -前端展示: - -- 点数; -- 文件大小; -- 字段说明; -- 下载按钮。 - -## 多监测点曲线 - -默认自动点建议为 5 个: - -```text -P1 auto_away_high_rate_low_sigma -P2 auto_toward_high_rate_low_sigma -P3 auto_abs_high_rate_low_sigma -P4 auto_stable_low_sigma -P5 auto_center_valid -``` - -选择原则: - -- 排除边缘区域; -- 只使用有效像元; -- sigma 越低越优先; -- 高形变点用于展示明显形变信号; -- 稳定点用于对比; -- 中心点用于空间代表性; -- 点之间设置最小距离,避免扎堆。 - -手动点: - -- 仍保留 `manual_lonlat` 模式; -- 当用户或后续点位管理页面提供点位时,按手动点优先; -- 自动点仅作为无手动点时的默认代表点。 - -前端展示: - -- 保留每个点的 PNG/CSV/metadata 下载; -- 结果页可展示多张点位曲线预览; -- 后续再实现同一坐标轴上的多曲线叠加。 - -## 生产链路位置 - -点矢量和多监测点都放在专家路径第十二步之后: - -1. Gamma 输出速率、sigma、GeoTIFF; -2. 生成点矢量 GeoJSON.gz; -3. 提取多个监测点时序; -4. catalog 自动登记产物; -5. 前端展示下载和曲线预览。 - -这样不会改变核心 SBAS 计算过程,只扩展发布和结果管理层。 - -## 风险与约束 - -- GeoJSON 体积会随范围快速增大,所以必须 gzip 压缩,前端不得加载。 -- 大范围任务后续应增加抽稀点矢量、CSV/Parquet 或 GeoPackage/FlatGeobuf 导出。 -- 自动点只适合作为快速检查和报告初稿候选点,正式报告应支持用户指定点、导入点位或专家确认点位。 diff --git a/docs/SBAS_INSAR_PRODUCTION_PIPELINE_DESIGN_20260519.md b/docs/SBAS_INSAR_PRODUCTION_PIPELINE_DESIGN_20260519.md deleted file mode 100644 index f4d7321..0000000 --- a/docs/SBAS_INSAR_PRODUCTION_PIPELINE_DESIGN_20260519.md +++ /dev/null @@ -1,1976 +0,0 @@ -# SBAS-InSAR Production Pipeline Design - -Date: 2026-05-19 - -## Decision - -SBAS-InSAR production becomes an independent production workflow and page. It must not depend on the existing coarse time-series pairing layer as its production authority. - -The old time-series pairing code may remain temporarily for compatibility and candidate discovery, but the new SBAS-InSAR page and backend API should bypass it by default. Deletion should happen only after the new workflow can create, run, publish, and browse Gamma SBAS/IPTA products end to end. - -The legacy ISCE2/MintPy time-series production chain is disabled by default. The `timeseries-production` backend code and old catalog pages may remain as compatibility code, but they are no longer exposed as production-management subpages. The active SBAS production route is `/api/sbas-insar-production` and the active UI view is `sbas_insar_production`. - -Update 2026-05-25: - -- The previous ad-hoc SBAS stage buttons are no longer the primary production design. -- The primary design follows the expert document: workspace directories, a run-level `manifest.json`, step scripts, `state/step_status.json`, and one Gamma SBAS workflow job. -- The already successful experiment is not discarded. Its verified Gamma commands are reused as the first bridge implementation while the scripts are moved toward expert-document templates. -- Old stage endpoints may remain temporarily for compatibility and for reading historical runs, but the UI should favor `Gamma SBAS Workflow`. - -Primary runtime configuration is now read from `.env` through: - -```text -GAMMA_SBAS_ENABLED -GAMMA_SBAS_RUNTIME_ID -GAMMA_SBAS_WSL_DISTRO -GAMMA_SBAS_PYTHON -GAMMA_SBAS_ENV_SCRIPT -GAMMA_SBAS_WORK_ROOT -GAMMA_SBAS_PRODUCT_ROOT -GAMMA_SBAS_SCRIPT_TEMPLATE_ROOT -GAMMA_SBAS_DEFAULT_RLKS -GAMMA_SBAS_DEFAULT_AZLKS -GAMMA_SBAS_DEFAULT_MB_MODE -GAMMA_SBAS_DEFAULT_REFERENCE_WINDOW -GAMMA_SBAS_STEP_TIMEOUT_SECONDS -GAMMA_SBAS_WORKFLOW_TIMEOUT_SECONDS -``` - -Startup self-check now reports the Gamma SBAS runtime, work root, product root, Python interpreter, and WSL distro. Database self-maintenance still runs through the existing `ensure_database_ready()` startup path; no new SBAS-only database table is required for this slice. - -## Scope - -Initial production target: - -- Sensor: LT1 SLC -- Engine: Gamma -- Workflow: Gamma DIFF + IPTA `mb` + `ts_rate` -- Processor code: `gamma_ipta_sbas` -- Default display product: LOS velocity toward radar positive - -Out of scope for the first implementation slice: - -- custom SBAS inversion in application code -- direct reuse of the current PS/time-series pair graph as final Gamma `itab` -- full automatic stack approval without a Gamma baseline and quality audit -- cross-satellite LT1A/LT1B stack mixing - -## Product Contract - -Every successful SBAS-InSAR production run should publish: - -- `product_summary.json` -- `stack_manifest.json` -- `gamma_command_manifest.json` -- `pair_network.json` -- `quality_summary.json` -- `los_rate_toward_mm_per_year.tif` -- `los_rate_toward_mm_per_year.geo_preview.png` -- `los_rate_toward_mm_per_year.rdc_preview.bmp` -- `los_rate_away_mm_per_year.tif` -- `los_rate_away_mm_per_year.rdc_preview.bmp` -- `los_sigma_mm_per_year.tif` -- `los_sigma_mm_per_year.geo_preview.png` -- `los_sigma_mm_per_year.rdc_preview.bmp` -- `ts_rate_rad_per_year.tif` -- `sigma_rate_rad_per_year.tif` -- monitoring-point time-series `png/csv/json` -- raw logs for each Gamma stage - -The current trial product remains the reference implementation: - -```text -backend/runtime/gamma_ipta_trials/lt1b_r114_e1312_n438_20240516_20251002 -``` - -Preview rule: - -- UI default map previews must be rendered from geocoded EPSG:4326 GeoTIFFs. -- RDC/RMLI BMP browse images are processing QA artifacts only. -- Product names should make coordinate state explicit: `geo_preview` for map previews and `rdc_preview` for radar-geometry previews. - -## LOS Sign Convention - -Gamma `ts_rate` outputs phase rate in `rad/year`. The system must store both sign conventions explicitly: - -```text -los_rate_away_mm_per_year = phase_rate * wavelength / (4*pi) * 1000 -los_rate_toward_mm_per_year = -phase_rate * wavelength / (4*pi) * 1000 -``` - -Default UI display: - -```text -LOS toward radar positive -``` - -This matches Gamma `dispmap` default `sflg=0`: motion away from radar is negative, motion toward radar is positive. - -## Page Design - -Add a separate production view: - -```text -Production Management - - D-InSAR Runs - - SBAS-InSAR Production - - D-InSAR Products -``` - -The SBAS page is operational, not a marketing landing page. First screen should show: - -- runtime capability: Gamma install, WSL distro, workflow support -- available SBAS stacks or trial runs -- selected run summary -- LOS velocity geocoded preview -- product file list -- monitoring-point curve -- quality metrics -- stage checklist - -The current old "time-series run" and "time-series products" views are hidden from the production workspace. Legacy route aliases such as `ps_production` and `ps_products` should redirect to the SBAS-InSAR production view rather than opening the old ISCE2/MintPy workflow. - -## Backend API - -Initial read-only API: - -```text -GET /api/sbas-insar-production/capabilities -GET /api/sbas-insar-production/trial-runs -GET /api/sbas-insar-production/trial-runs/{trial_id} -GET /api/sbas-insar-production/trial-runs/{trial_id}/artifacts/{relative_path} -``` - -Primary Gamma SBAS workflow API: - -```text -POST /api/sbas-insar-production/runs/{run_id}/workflow -POST /api/sbas-insar-production/runs/{run_id}/workflow/jobs -``` - -`workflow` prepares the expert-document workspace and writes: - -```text -runs/{run_id}/workspace.json -runs/{run_id}/manifest.json -runs/{run_id}/state/step_status.json -runs/{run_id}/scripts/01_workspace_data.sh -runs/{run_id}/scripts/02_import_lt1_slc.sh -runs/{run_id}/scripts/03_reference_mli.sh -runs/{run_id}/scripts/04_dem_lookup.sh -runs/{run_id}/scripts/05_coreg_prep.sh -runs/{run_id}/scripts/06_coregister_scenes.sh -runs/{run_id}/scripts/07_rmli_average.sh -runs/{run_id}/scripts/08_diff_network.sh -runs/{run_id}/scripts/09_filter_unwrap.sh -runs/{run_id}/scripts/10_detrend_atm.sh -runs/{run_id}/scripts/11_sbas_inversion.sh -runs/{run_id}/scripts/12_outputs_points.sh -``` - -`workflow/jobs` submits one `SBAS_GAMMA_WORKFLOW` background job. The production workflow is now the twelve-section expert-document workflow. Several sections still reuse the already verified experiment scripts internally, but they are no longer hidden behind an eight-stage production view: - -```text -01_workspace_data -> expert workspace/data-layout check -02_import_lt1_slc -> par_LT1_SLC / orbit correction -03_reference_mli -> multi_look / reference MLI checks -04_dem_lookup -> DEM import, lookup table, RDC height -05_coreg_prep -> common-reference stack preparation -06_coregister_scenes -> scene coregistration -07_rmli_average -> RMLI tab/average intensity -08_diff_network -> baseline network and differential phase -09_filter_unwrap -> adaptive filtering, coherence and unwrap -10_detrend_atm -> quad_fit / atm_mod_2d / sub_phase -11_sbas_inversion -> mb / ts_rate, consuming DIFF_atmsub_tab -12_outputs_points -> geocode, browse products, monitoring curve -``` - -Stack discovery and hard-constraint audit API: - -```text -POST /api/sbas-insar-production/stacks/discover -POST /api/sbas-insar-production/stacks/{stack_id}/audit -``` - -`stacks/discover` scans LT1 source roots directly and groups scenes by: - -- platform, for example `LT1A` or `LT1B` -- satellite mode, for example `MONO` -- receiving station -- relative orbit -- orbit direction -- imaging mode -- polarization -- center bucket, for example `E131.2_N43.8` - -It also checks LT1 precise orbit TXT availability against `PYINT_ORBIT_POOL_TXT` / `ORBIT_POOL_ENVI`. - -`stacks/{stack_id}/audit` writes a reproducible manifest under: - -```text -backend/runtime/sbas_insar_production/stack_manifests/{stack_id}/ -``` - -The manifest status is `READY_FOR_GAMMA_BASELINE_AUDIT` only after hard grouping, minimum scene count, precise orbit availability, and an initial adjacent temporal network are satisfied. Gamma `base_calc` remains the next required audit before final `itab` approval. - -Writable production planning API: - -```text -POST /api/sbas-insar-production/stacks/{stack_id}/runs -GET /api/sbas-insar-production/runs -GET /api/sbas-insar-production/runs/{run_id} -GET /api/sbas-insar-production/runs/{run_id}/artifacts/{relative_path} -``` - -The current `runs` submission is a dry-run planning submission. It writes: - -```text -backend/runtime/sbas_insar_production/runs/{run_id}/run_manifest.json -backend/runtime/sbas_insar_production/runs/{run_id}/stack_manifest.json -backend/runtime/sbas_insar_production/runs/{run_id}/pair_network.json -backend/runtime/sbas_insar_production/runs/{run_id}/gamma_command_manifest.json -backend/runtime/sbas_insar_production/runs/{run_id}/monitor_points.json -``` - -The created run status is: - -```text -PLANNED_GAMMA_BASELINE_AUDIT -``` - -This is intentionally not a Gamma execution trigger yet. The next runnable slice should add: - -```text -POST /api/sbas-insar-production/runs/{run_id}/baseline-audit -POST /api/sbas-insar-production/runs/{run_id}/itab-decision -POST /api/sbas-insar-production/runs/{run_id}/coregistration -POST /api/sbas-insar-production/runs/{run_id}/coregistration/jobs -POST /api/sbas-insar-production/runs/{run_id}/monitor-points -POST /api/sbas-insar-production/runs/{run_id}/retry-stage -``` - -`baseline-audit` currently supports: - -- script-only mode: generate/reparse `scripts/01_baseline_audit.sh` and existing outputs -- execution mode: run Gamma `par_LT1_SLC`, `LT1_precision_orbit.py`, `multi_look`, and `base_calc` -- output parsing: write `baseline_audit_summary.json` and `pair_network_baseline_audit.json` - -`itab-decision` is the current production gate: - -- `approve`: copies Gamma `work/gamma/diff/itab_adjacent` to `work/gamma/diff/itab_approved`, writes `itab_decision.json`, moves the run to `ITAB_APPROVED`, and makes `coregistration` the next stage -- `reject`: writes `itab_decision.json`, moves the run to `ITAB_REJECTED`, and blocks further Gamma stages until the pair network is revised - -`coregistration` supports script generation. It writes: - -```text -backend/runtime/sbas_insar_production/runs/{run_id}/scripts/02_coreg_common_ref.sh -backend/runtime/sbas_insar_production/runs/{run_id}/coregistration_plan.json -``` - -The generated script consumes `work/gamma/diff/itab_approved` as the approval gate, uses the stack reference date as common geometry, and prepares Gamma `SLC_coreg.py` calls for every non-reference date. - -`coregistration/jobs` submits the generated script to the existing `SystemTask` + `SystemJob` background queue as job type `SBAS_COREGISTRATION`. The job runs Gamma `SLC_coreg.py`, writes `coregistration_summary.json`, updates the run manifest to `COREGISTRATION_READY` or `COREGISTRATION_FAILED`, and advances the next stage to `rdc_dem` only when all expected RSLC/RMLI outputs and common tab files exist. - -## Backend Services - -First slice: - -```text -sbas_insar_production_service.py - - discover local Gamma IPTA trial summaries - - normalize products and artifact URLs - - expose sign convention and product metadata - - serve safe artifacts from trial roots -``` - -Second slice: - -```text -gamma_ipta_stack_planner.py - - hard group LT1 scenes by platform, relative orbit, direction, mode, polarization, center bucket - - require precise orbit availability - - emit stack_manifest.json - -gamma_ipta_pair_planner.py - - build initial temporal network - - run Gamma baseline audit - - emit pair_network.json and Gamma itab - -gamma_ipta_job_runner.py - - execute official Gamma commands stage by stage - - write command manifests and logs - -sbas_insar_product_publisher.py - - publish GeoTIFF/BMP/CSV/JSON products - - register products into unified result catalog -``` - -## Future Database Model - -Use unified pipeline tables rather than adding many one-off SBAS tables: - -```text -pipeline_runs -pipeline_stages -pipeline_products -pipeline_quality_metrics -pipeline_logs -``` - -Minimum fields for `pipeline_runs`: - -- `run_id` -- `workflow_code = sbas_insar` -- `processor_code = gamma_ipta_sbas` -- `engine_code = gamma` -- `status` -- `stack_manifest_path` -- `work_root` -- `publish_root` -- `created_by` -- `created_at` -- `started_at` -- `ended_at` -- `summary_json` - -For the first slice, use filesystem discovery only. Do not add migrations until the run submission workflow is ready. - -## Gamma Stage Contract - -The managed runner should preserve the successful trial chain: - -1. `par_LT1_SLC` -2. `LT1_precision_orbit.py` -3. `multi_look` -4. `base_calc` -5. `SLC_coreg.py` -6. `gc_map1` / `geocode` / `gc_map_fine` -7. `phase_sim_orb` -8. `SLC_diff_intf` -9. `adf` -10. `mcf` -11. `mb` -12. `ts_rate` -13. `geocode_back` -14. `data2geotiff` -15. LOS sign conversion and preview generation -16. monitoring-point time-series extraction - -The application is an orchestrator. Gamma remains the processing authority. - -## Migration Plan - -Phase 1: read-only SBAS production page - -- add design document -- add backend API for existing Gamma trial discovery -- add page entry and product preview -- keep old time-series page available as legacy - -Phase 2: managed run submission - -- add stack discovery and audit endpoints -- add planned-run submission endpoint -- write production run manifest, command manifest, and monitor-point config -- add Gamma runner skeleton -- queue job with stage updates - -Phase 3: unified pipeline management - -- add generic pipeline tables -- move Gamma SBAS run records into pipeline tables -- register products through the unified product catalog - -Phase 4: remove old SBAS/time-series pairing dependency - -- hide old SBAS entry completely -- keep any reusable discovery functions as internal utilities -- delete obsolete UI and API routes after dependency audit - -## Acceptance Criteria For Phase 1 - -- SBAS-InSAR production appears as its own production workspace view. -- Existing Gamma IPTA trial can be listed from the backend API. -- The selected trial shows LOS velocity preview, sigma/GeoTIFF products, monitor-point curve, and quality summary. -- Artifact serving is constrained to the trial root. -- No existing D-InSAR, flood, or legacy time-series routes are broken. - -## Implementation Progress On 2026-05-19 - -Implemented: - -- read-only SBAS-InSAR production page -- trial product browser for the local Gamma IPTA validation run -- artifact API constrained to published trial outputs -- LT1 filesystem stack discovery independent of the old time-series pairing layer -- hard grouping by platform, satellite mode, receiving station, relative orbit, orbit direction, imaging mode, polarization, and center bucket -- precise orbit TXT availability check against the configured Gamma/PyINT orbit pool -- stack manifest and initial adjacent pair-network JSON generation -- geocoded web previews generated from `los_rate_toward_mm_per_year.tif` and `los_sigma_mm_per_year.tif` -- planned SBAS production run creation from a READY stack manifest -- filesystem production run browser and artifact download API -- Gamma stage plan manifest with execution disabled until baseline audit runner is attached -- monitoring-point config contract with explicit placeholder status for `auto_low_sigma_high_rate` -- Gamma baseline audit script generation and output parser -- baseline audit result display in the SBAS production page -- itab approval/rejection API and page controls -- common-reference coregistration script generation and page summary -- queued `SBAS_COREGISTRATION` background job submission through the existing task/job queue -- coregistration execution summary parser and manifest status update to `COREGISTRATION_READY` / `COREGISTRATION_FAILED` - -Local verification: - -- scanned `1500` LT1 scene directories from the local data pool -- found READY candidates with all required precise orbit TXT files -- generated one manifest at: - -```text -backend/runtime/sbas_insar_production/stack_manifests/sbas_2e6301f64a10/20260519T122146Z_stack_manifest.json -``` - -- created one dry-run production plan at: - -```text -backend/runtime/sbas_insar_production/runs/sbas_ab96afabead5/run_manifest.json -``` - -The dry-run production plan uses the local LT1B relOrbit `114` stack around `E129.2_N44.1`, with `7` scenes and `6` initial adjacent temporal pairs. - -- baseline audit script: - -```text -backend/runtime/sbas_insar_production/runs/sbas_ab96afabead5/scripts/01_baseline_audit.sh -``` - -- baseline audit summary: - -```text -backend/runtime/sbas_insar_production/runs/sbas_ab96afabead5/baseline_audit_summary.json -``` - -The baseline audit ran Gamma 20240627 `par_LT1_SLC`, `LT1_precision_orbit.py`, `multi_look`, and `base_calc` against all 7 LT1B scenes. It completed with status `BASELINE_AUDIT_READY` after the outer terminal command timed out, because the WSL process continued to completion in the background. - -Gamma `base_calc` adjacent-network result: - -- all-pair count: `21` -- adjacent-pair count: `6` -- max absolute perpendicular baseline: `731.9957 m` -- max temporal gap: `224 days` - -The current adjacent network is connected, but several Bperp values are large enough that a human baseline/quality review is still required before using this `itab` for the full SBAS inversion. - -The current run has been approved for the next controlled trial step: - -```text -status = ITAB_APPROVED -next_stage = coregistration -approved itab = backend/runtime/sbas_insar_production/runs/sbas_ab96afabead5/work/gamma/diff/itab_approved -decision record = backend/runtime/sbas_insar_production/runs/sbas_ab96afabead5/itab_decision.json -``` - -The common-reference co-registration script has been generated: - -```text -backend/runtime/sbas_insar_production/runs/sbas_ab96afabead5/scripts/02_coreg_common_ref.sh -backend/runtime/sbas_insar_production/runs/sbas_ab96afabead5/coregistration_plan.json -``` - -The common-reference co-registration job was executed through the backend task/job queue: - -```text -task_id = 047092bb-2c57-4639-8e8b-89ce925d3273 -job_id = 15a66c9c-8814-4566-b299-12081f74ec09 -task status = COMPLETED -job status = COMPLETED -last_error = None -``` - -Gamma `SLC_coreg.py` completed successfully for all expected secondary scenes. It consumed `itab_approved`, not the pre-audit or unapproved pair plan. - -Generated post-job outputs: - -```text -backend/runtime/sbas_insar_production/runs/sbas_ab96afabead5/coregistration_summary.json -backend/runtime/sbas_insar_production/runs/sbas_ab96afabead5/work/gamma/common_20241007/SLC_tab -backend/runtime/sbas_insar_production/runs/sbas_ab96afabead5/work/gamma/common_20241007/RMLI_tab -backend/runtime/sbas_insar_production/runs/sbas_ab96afabead5/work/gamma/common_20241007/rslc/*.rslc -backend/runtime/sbas_insar_production/runs/sbas_ab96afabead5/work/gamma/common_20241007/rmli/*.mli -``` - -The coregistration summary reports: - -```text -scene_count = 7 -expected_secondary_count = 6 -ready_secondary_count = 6 -missing_dates = [] -missing_tabs = [] -ready = true -``` - -Current run state: - -```text -status = COREGISTRATION_READY -next_stage = rdc_dem -``` - -Open product-display decisions: - -- the current Gamma `*.bmp` previews are RDC processing-geometry products; keep them visible only as QA artifacts -- the first UI map preview should use `los_rate_toward_mm_per_year.geo_preview.png` -- the first sigma preview should use `los_sigma_mm_per_year.geo_preview.png` -- the current monitoring-point curve is a single automatic sample point, not a monitoring network -- production monitoring curves need user-selected lon/lat points, imported monitoring points, or a quality-filtered automatic sampler before they can be treated as formal outputs - -## Post-Coregistration Production Design - -Date: 2026-05-21 - -The next production slice should not jump directly from `COREGISTRATION_READY` to final velocity maps. It should keep the same gated pattern used for baseline audit and coregistration: - -```text -generate stage script/plan -> submit background job -> parse summary -> advance manifest status -``` - -The remaining chain is split into four testable stages: - -```text -rdc_dem -> interferograms -> ipta_timeseries -> publish_products -``` - -Each stage gets its own script, summary JSON, task/job type, page action, and artifact entries. This keeps operator testing bounded and avoids hiding a multi-hour Gamma failure inside a single monolithic job. - -### Stage 3: RDC DEM - -Stage id: - -```text -rdc_dem -``` - -Allowed input state: - -```text -run.status = COREGISTRATION_READY -run.next_stage = rdc_dem -``` - -Primary inputs: - -- `work/gamma/common_20241007/SLC_tab` -- `work/gamma/common_20241007/RMLI_tab` -- reference date: `20241007` -- reference MLI parameter file from the common `RMLI_tab` -- project DEM source resolved from the configured DEM strategy - -Do not hardcode the DEM used by the earlier `E131.2/N43.8` trial. That trial script reused: - -```text -backend/runtime/pyint_dem_cache/.../lt1_20230602_20230720...dem -``` - -The active production run is centered near `E129.2/N44.1`, so stage 3 must resolve or build a DEM for this stack explicitly. - -DEM resolution policy: - -1. Prefer an explicitly configured Gamma-compatible or GDAL-readable DEM source. -2. Record the selected DEM source in `rdc_dem_plan.json`. -3. Generate or copy a local run-scoped DEM into: - -```text -work/gamma/dem/ -``` - -4. Fail early if the DEM cannot be read or does not cover the stack/reference geometry. - -Gamma commands: - -- `replace_values` when source DEM nodata cleanup is needed -- `gc_map1` -- `geocode` -- `create_diff_par` -- `init_offsetm` -- `offset_pwrm` -- `offset_fitm` -- `gc_map_fine` -- `geocode` - -Expected files: - -```text -scripts/03_prepare_rdc_dem.sh -rdc_dem_plan.json -rdc_dem_summary.json -logs/20241007_rdc_dem.log -work/gamma/dem/20241007_8rlks.utm.dem -work/gamma/dem/20241007_8rlks.utm.dem.par -work/gamma/dem/20241007_8rlks.UTM_TO_RDC -work/gamma/dem/20241007_8rlks.rdc.dem -work/gamma/dem/20241007_8rlks.diff_par -``` - -Success state: - -```text -status = RDC_DEM_READY -next_stage = interferograms -``` - -Failure state: - -```text -status = RDC_DEM_FAILED -next_stage = fix_rdc_dem -``` - -Summary checks: - -- reference date exists in the common tabs -- reference MLI dimensions are parsed -- DEM source path is recorded -- `rdc.dem`, `UTM_TO_RDC`, `utm.dem.par`, and `diff_par` exist and are non-empty -- Gamma log tail is captured - -User test request after this stage is implemented: - -1. Open the SBAS production page and select `sbas_ab96afabead5`. -2. Confirm it shows `COREGISTRATION_READY` and `next_stage = rdc_dem`. -3. Click `提交 RDC DEM 任务`. -4. Wait for the task to complete. -5. Expected page result: `RDC_DEM_READY`, next stage `interferograms`. -6. Expected artifact: `rdc_dem_summary.json`. -7. If it fails, download or inspect `logs/20241007_rdc_dem.log`. - -### Stage 4: Differential Interferograms - -Stage id: - -```text -interferograms -``` - -Allowed input state: - -```text -run.status = RDC_DEM_READY -run.next_stage = interferograms -``` - -Primary inputs: - -- `work/gamma/common_20241007/SLC_tab` -- `work/gamma/common_20241007/RMLI_tab` -- `work/gamma/common_20241007/itab_approved` -- `work/gamma/dem/20241007_8rlks.rdc.dem` - -Pair source: - -Use the approved Gamma `itab` rows. Do not regenerate pair choices from the old time-series pairing layer at this stage. - -The current approved adjacent network is: - -```text -1 2 1 1 -2 3 2 1 -3 4 3 1 -4 5 4 1 -5 6 5 1 -6 7 6 1 -``` - -Gamma commands per pair: - -- `create_offset` -- `phase_sim_orb` -- `SLC_diff_intf` -- `adf` -- `cc_wave` -- `rasmph_pwr` -- `rasdt_pwr` -- `rascc_mask` -- `mcf` - -Expected files: - -```text -scripts/04_diff_unwrap_common_ref.sh -interferogram_plan.json -interferogram_summary.json -work/gamma/common_20241007/DIFF_tab -work/gamma/common_20241007/itab_ipta -work/gamma/common_20241007/diff/{pair}/{pair}_8rlks.diff -work/gamma/common_20241007/diff/{pair}/{pair}_8rlks.diff_filt -work/gamma/common_20241007/diff/{pair}/{pair}_8rlks.diff_filt.cor -work/gamma/common_20241007/diff/{pair}/{pair}_8rlks.diff_filt.unw -``` - -Success state: - -```text -status = INTERFEROGRAMS_READY -next_stage = ipta_timeseries -``` - -Failure state: - -```text -status = INTERFEROGRAMS_FAILED -next_stage = fix_interferograms -``` - -Summary checks: - -- expected pair count equals approved `itab` row count -- every pair has `diff`, filtered diff, coherence, mask, and unwrapped phase -- `DIFF_tab` line count matches `itab_ipta` row count -- coherence statistics are recorded per pair -- failed pairs are listed explicitly - -User test request after this stage is implemented: - -1. Confirm the run shows `RDC_DEM_READY`. -2. Click `提交差分干涉图任务`. -3. Wait for completion. -4. Expected page result: `INTERFEROGRAMS_READY`, next stage `ipta_timeseries`. -5. Expected summary: `interferogram_summary.json` with `ready_pair_count = 6`. -6. Review any low-coherence warnings before continuing. - -### Stage 5: IPTA Time-Series - -Stage id: - -```text -ipta_timeseries -``` - -Allowed input state: - -```text -run.status = INTERFEROGRAMS_READY -run.next_stage = ipta_timeseries -``` - -Primary inputs: - -- `work/gamma/common_20241007/DIFF_tab` -- `work/gamma/common_20241007/RMLI_tab` -- `work/gamma/common_20241007/itab_ipta` -- reference geometry MLI parameter file - -Gamma commands: - -- `mb` -- `ts_rate` - -The first production implementation should reuse the trial-proven image-based `mb -> ts_rate` invocation shape, but with run-specific paths and the approved `itab`. It should record the exact reference parameter files used by `mb`, because this is a quality-sensitive detail. - -Expected files: - -```text -scripts/05_mb_ts_rate.sh -ipta_timeseries_plan.json -ipta_timeseries_summary.json -work/gamma/common_20241007/timeseries/diff_ts.tab -work/gamma/common_20241007/timeseries/itab_ts -work/gamma/common_20241007/timeseries/sigma_ts -work/gamma/common_20241007/timeseries/hgt_correction -work/gamma/common_20241007/timeseries/ts_rate -work/gamma/common_20241007/timeseries/ts_const -work/gamma/common_20241007/timeseries/sigma_rate -``` - -Success state: - -```text -status = IPTA_TIMESERIES_READY -next_stage = publish_products -``` - -Failure state: - -```text -status = IPTA_TIMESERIES_FAILED -next_stage = fix_ipta_timeseries -``` - -User test request after this stage is implemented: - -1. Confirm the run shows `INTERFEROGRAMS_READY`. -2. Click `提交 IPTA 时序反演任务`. -3. Wait for completion. -4. Expected page result: `IPTA_TIMESERIES_READY`, next stage `publish_products`. -5. Expected summary: `ipta_timeseries_summary.json` showing `ts_rate` and `sigma_rate` exist. - -### Stage 6: Publish Products - -Stage id: - -```text -publish_products -``` - -Allowed input state: - -```text -run.status = IPTA_TIMESERIES_READY -run.next_stage = publish_products -``` - -Primary inputs: - -- `work/gamma/common_20241007/timeseries/ts_rate` -- `work/gamma/common_20241007/timeseries/sigma_rate` -- `work/gamma/common_20241007/timeseries/sigma_ts` -- `work/gamma/common_20241007/timeseries/hgt_correction` -- `work/gamma/dem/20241007_8rlks.UTM_TO_RDC` -- `work/gamma/dem/20241007_8rlks.utm.dem.par` -- reference SLC parameter file for wavelength - -Processing: - -1. Compute wavelength from `radar_frequency`. -2. Convert phase-rate to LOS rate: - -```text -los_rate_away_mm_per_year = phase_rate * wavelength / (4*pi) * 1000 -los_rate_toward_mm_per_year = -phase_rate * wavelength / (4*pi) * 1000 -``` - -3. Use `geocode_back` and `data2geotiff` to export EPSG:4326 GeoTIFFs. -4. Generate web preview PNGs from the geocoded GeoTIFFs, not from RDC BMPs. -5. Write product and quality summaries. - -Expected files: - -```text -scripts/06_publish_products.sh -publish_product_plan.json -publish_product_summary.json -quality_summary.json -publish/geotiff/ts_rate_rad_per_year.tif -publish/geotiff/sigma_rate_rad_per_year.tif -publish/geotiff/los_rate_toward_mm_per_year.tif -publish/geotiff/los_rate_toward_mm_per_year.geo_preview.png -publish/geotiff/los_rate_away_mm_per_year.tif -publish/geotiff/los_sigma_mm_per_year.tif -publish/geotiff/los_sigma_mm_per_year.geo_preview.png -``` - -Success state: - -```text -status = PRODUCTS_READY -next_stage = monitor_points -``` - -Failure state: - -```text -status = PUBLISH_PRODUCTS_FAILED -next_stage = fix_publish_products -``` - -User test request after this stage is implemented: - -1. Confirm the run shows `IPTA_TIMESERIES_READY`. -2. Click `发布 LOS 速率产品`. -3. Wait for completion. -4. Expected page result: `PRODUCTS_READY`. -5. Verify the UI default velocity and sigma previews are geocoded PNGs. -6. Download or open the GeoTIFFs in GIS if needed. -7. Treat RDC BMPs as processing QA only. - -### Stage 7: Monitor Points - -This should not block the first full production result. - -The current automatic sample point is only a debug placeholder. Formal monitoring curves require at least one of: - -- user-provided lon/lat points -- imported monitoring-point layer -- explicitly approved quality-filtered automatic sampler - -Until then, the UI should label monitor curves as sample/debug output, not formal business monitoring points. - -### Implementation Order - -Recommended next coding order: - -1. Add backend `RDC_DEM` stage: plan generation, script generation, queued job, summary parser, manifest update. -2. Add frontend actions and status display for the `rdc_dem` stage. -3. Ask the user to test only `RDC DEM` on `sbas_ab96afabead5`. -4. After that succeeds, implement the interferogram stage. -5. After interferograms succeed, implement IPTA time-series. -6. After IPTA succeeds, implement product publishing and geocoded previews. - -This staged order is intentionally conservative. It keeps each Gamma failure surface small enough to diagnose from one stage log and one summary JSON. - -## 2026-05-22 RDC DEM Implementation Note - -The formal SBAS production workflow now includes a managed `rdc_dem` stage. - -Implemented code paths: - -- Backend service methods: - - `prepare_rdc_dem` - - `execute_rdc_dem` - - `_write_rdc_dem_script` - - `_build_rdc_dem_summary` - - `_refresh_command_manifest_after_rdc_dem` -- FastAPI endpoints: - - `POST /api/sbas-insar-production/runs/{run_id}/rdc-dem` - - `POST /api/sbas-insar-production/runs/{run_id}/rdc-dem/jobs` -- Background job type: - - `SBAS_RDC_DEM` -- Frontend: - - `生成 RDC DEM 脚本` - - `提交 RDC DEM 任务` - - `RDC DEM Plan` status card - -The generated script is intentionally based on the successful trial script: - -```text -gc_map1 -geocode simulated SAR to RDC -create_diff_par -init_offsetm -offset_pwrm -offset_fitm -gc_map_fine -geocode DEM to RDC -``` - -The current `.env` DEM path points to the large SARscape/GDAL raster: - -```text -D:\DEM\SRTMDEM_RSP_SARscape.wgs84 -``` - -That file is not a Gamma `.dem + .dem.par` pair. For this implementation slice, the production stage selects an existing Gamma-format PyINT DEM cache when no explicit Gamma DEM source is configured. On the current machine, the selected LT1 cache covers the stack center and follows the same PyINT/Gamma DEM format that the successful experiment used. - -Known limitation: - -- The existing LT1 PyINT DEM cache covers the stack center but may not fully cover the union of every scene bbox south edge. The plan records both `covers_stack_center` and `covers_stack_bbox`. If the RDC DEM stage fails or creates edge voids, the next fix should generate a fresh Gamma DEM from `D:\DEM\SRTMDEM_RSP_SARscape.wgs84` over the full SBAS stack bbox plus margin, instead of relying on older pair-level DEM caches. - -Local validation completed: - -- Python AST parse passed for: - - `backend/app/services/sbas_insar_production_service.py` - - `backend/app/routers/sbas_insar_production.py` - - `backend/app/services/job_handlers.py` -- `SBAS_RDC_DEM` job handler registration resolves successfully. -- Frontend `npm run build` passed after running with the permission needed for Vite/esbuild subprocess spawn. - -Manual test order for the user: - -1. Restart backend and frontend if they are already running. -2. Open the SBAS-InSAR production page. -3. Select run `sbas_ab96afabead5`. -4. Confirm status is `COREGISTRATION_READY` or `RDC_DEM_SCRIPT_READY`. -5. Click `生成 RDC DEM 脚本`. -6. Confirm `RDC DEM Plan` appears and points to `scripts/03_prepare_rdc_dem.sh`. -7. Click `提交 RDC DEM 任务`. -8. Watch the task until completion. -9. Expected success: - -```text -run.status = RDC_DEM_READY -run.next_stage = interferograms -rdc_dem_summary.ready = true -``` - -Expected output files: - -```text -work/gamma/dem/20241007_8rlks.utm.dem -work/gamma/dem/20241007_8rlks.utm.dem.par -work/gamma/dem/20241007_8rlks.UTM_TO_RDC -work/gamma/dem/20241007_8rlks.rdc.dem -work/gamma/dem/20241007_8rlks.diff_par -logs/20241007_rdc_dem.log -rdc_dem_summary.json -``` - -If the job fails, inspect: - -```text -logs/20241007_rdc_dem.log -rdc_dem_summary.json -``` - -The next production coding stage after `RDC_DEM_READY` is differential interferogram generation. - -## 2026-05-23 Interferogram Stage Implementation Note - -The formal SBAS production workflow now includes a managed `interferograms` stage after `RDC_DEM_READY`. - -Implemented code paths: - -- Backend service methods: - - `prepare_interferograms` - - `execute_interferograms` - - `_write_interferogram_script` - - `_build_interferogram_summary` - - `_build_interferogram_pair_plan` - - `_refresh_command_manifest_after_interferograms` -- FastAPI endpoints: - - `POST /api/sbas-insar-production/runs/{run_id}/interferograms` - - `POST /api/sbas-insar-production/runs/{run_id}/interferograms/jobs` -- Background job type: - - `SBAS_INTERFEROGRAMS` -- Frontend: - - Adds an action to generate the interferogram script. - - Adds an action to submit the interferogram background job. - - Adds an `Interferogram Plan` status card. - -This stage is intentionally derived from the successful Gamma trial script `08_diff_unwrap_common_ref.sh`. The production script keeps the same Gamma command chain: - -```text -create_offset -phase_sim_orb -SLC_diff_intf -adf -cc_wave -rasmph_pwr -rasdt_pwr -rascc_mask -mcf -rasdt_pwr -``` - -The production differences from the trial are: - -- It reads the approved production `itab_approved` instead of hardcoding a trial date list. -- It writes production `DIFF_tab` and `itab_common_ref` for the next `mb` and `ts_rate` stage. -- It records a JSON plan and JSON execution summary under the run directory. -- It keeps all SLCs/RMLIs in the common reference geometry prepared by the coregistration stage. - -Generated files for the current test run: - -```text -backend/runtime/sbas_insar_production/runs/sbas_ab96afabead5/interferogram_plan.json -backend/runtime/sbas_insar_production/runs/sbas_ab96afabead5/scripts/04_diff_unwrap_common_ref.sh -``` - -Current generated plan: - -```text -run_id = sbas_ab96afabead5 -status = INTERFEROGRAMS_SCRIPT_READY -next_stage = execute_interferograms -reference_date = 20241007 -pair_count = 6 -pairs = - 20240422_20240617 - 20240617_20240812 - 20240812_20241007 - 20241007_20250519 - 20250519_20250714 - 20250714_20250908 -``` - -Expected output interface after successful execution: - -```text -work/gamma/common_20241007/DIFF_tab -work/gamma/common_20241007/itab_common_ref -work/gamma/common_20241007/diff//_8rlks.diff_filt.unw -work/gamma/common_20241007/diff//_8rlks.diff_filt.cor -interferogram_summary.json -``` - -Local validation completed: - -- Python AST parse passed for: - - `backend/app/services/sbas_insar_production_service.py` - - `backend/app/routers/sbas_insar_production.py` - - `backend/app/services/job_handlers.py` -- `SBAS_INTERFEROGRAMS` job handler registration resolves successfully. -- The generated `04_diff_unwrap_common_ref.sh` passes `bash -n` in WSL. -- Frontend `npm run build` passed after running with the permission needed for Vite/esbuild subprocess spawn. - -Manual test order for the user: - -1. Restart backend and frontend if they are already running. -2. Open the SBAS-InSAR production page. -3. Select run `sbas_ab96afabead5`. -4. Confirm status is `INTERFEROGRAMS_SCRIPT_READY`. -5. Confirm `Interferogram Plan` shows 6 pairs and `reference_date = 20241007`. -6. Click the interferogram background-job button. -7. Watch the task until completion. -8. Expected success: - -```text -run.status = INTERFEROGRAMS_READY -run.next_stage = ipta_timeseries -interferogram_summary.ready = true -interferogram_summary.ready_pair_count = 6 -``` - -If the job fails, inspect: - -```text -logs/_diff_unwrap_common.log -interferogram_summary.json -work/gamma/common_20241007/DIFF_tab -work/gamma/common_20241007/itab_common_ref -``` - -Known risk to verify in the production test: - -- The trial script proved the command chain with reference-star pairs. The formal production stage uses the approved adjacent `itab` network, including secondary-secondary pairs after common-reference coregistration. This is the right SBAS topology, but the next live test should confirm Gamma accepts the secondary-secondary pair geometry with the current `phase_sim_orb` inputs. If Gamma rejects that geometry, the next correction is to adapt the pair topology or DEM geometry handling while keeping the same managed stage boundary. - -The next production coding stage after `INTERFEROGRAMS_READY` is IPTA time-series inversion with `mb` and `ts_rate`. - -## 2026-05-24 Interferogram Result And IPTA Stage Implementation Note - -The current production run `sbas_ab96afabead5` completed the managed interferogram stage successfully. - -Observed result: - -```text -run.status = INTERFEROGRAMS_READY -run.next_stage = ipta_timeseries -interferogram_summary.ready = true -interferogram_summary.ready_pair_count = 6 -interferogram_summary.pair_count = 6 -DIFF_tab rows = 6 -itab_common_ref rows = 6 -``` - -All six approved SBAS pairs produced the required Gamma outputs: - -```text -_8rlks.off -.sim_unw -_8rlks.diff -_8rlks.diff_filt -_8rlks.diff_filt.cor -_8rlks.diff_filt.cor_mask.bmp -_8rlks.diff_filt.unw -``` - -This confirms that the production adjacent-pair SBAS topology works with the current common-reference geometry, not only the reference-star topology from the earlier experiment. - -The formal SBAS production workflow now includes a managed `ipta_timeseries` stage. - -Implemented code paths: - -- Backend service methods: - - `prepare_ipta_timeseries` - - `execute_ipta_timeseries` - - `_write_ipta_timeseries_script` - - `_build_ipta_timeseries_summary` - - `_select_ipta_mb_reference_mli` - - `_refresh_command_manifest_after_ipta_timeseries` -- FastAPI endpoints: - - `POST /api/sbas-insar-production/runs/{run_id}/ipta-timeseries` - - `POST /api/sbas-insar-production/runs/{run_id}/ipta-timeseries/jobs` -- Background job type: - - `SBAS_IPTA_TIMESERIES` -- Frontend: - - Adds an action to generate the IPTA script. - - Adds an action to submit the IPTA background job. - - Adds an `IPTA Time-Series Plan` status card. - -This stage is derived from the successful Gamma trial script `09_mb_ts_rate.sh`. The production script keeps the same Gamma command chain: - -```text -mb -ts_rate -``` - -The production differences from the trial are: - -- It uses run-specific `DIFF_tab`, `RMLI_tab`, and `itab_common_ref`. -- It records the two `mb` reference parameter files explicitly: - - geometry reference MLI parameter file - - nearest non-reference common-RMLI parameter file used as the `mb` reference parameter -- It writes `ipta_timeseries_plan.json` and `ipta_timeseries_summary.json`. -- It updates the command manifest stage plan and enables the next `publish_products` stage only after `IPTA_TIMESERIES_READY`. - -Generated files for the current test run: - -```text -backend/runtime/sbas_insar_production/runs/sbas_ab96afabead5/ipta_timeseries_plan.json -backend/runtime/sbas_insar_production/runs/sbas_ab96afabead5/scripts/05_mb_ts_rate.sh -``` - -Current generated plan: - -```text -run_id = sbas_ab96afabead5 -status = IPTA_TIMESERIES_SCRIPT_READY -next_stage = execute_ipta_timeseries -reference_date = 20241007 -geometry_reference_mli_par = work/gamma/mli/20241007.mli.par -mb_reference_mli_par = work/gamma/common_20241007/rmli/20240812.mli.par -``` - -Expected output interface after successful execution: - -```text -work/gamma/common_20241007/timeseries/diff_ts.tab -work/gamma/common_20241007/timeseries/itab_ts -work/gamma/common_20241007/timeseries/sigma_ts -work/gamma/common_20241007/timeseries/hgt_correction -work/gamma/common_20241007/timeseries/ts_rate -work/gamma/common_20241007/timeseries/ts_const -work/gamma/common_20241007/timeseries/sigma_rate -ipta_timeseries_summary.json -``` - -Local validation completed: - -- Python AST parse passed for: - - `backend/app/services/sbas_insar_production_service.py` - - `backend/app/routers/sbas_insar_production.py` - - `backend/app/services/job_handlers.py` -- `SBAS_IPTA_TIMESERIES` job handler registration resolves successfully. -- The generated `05_mb_ts_rate.sh` passes `bash -n` in WSL. -- Frontend `npm run build` passed after running with the permission needed for Vite/esbuild subprocess spawn. - -Manual test order for the user: - -1. Restart backend and frontend if they are already running. -2. Open the SBAS-InSAR production page. -3. Select run `sbas_ab96afabead5`. -4. Confirm status is `IPTA_TIMESERIES_SCRIPT_READY`. -5. Confirm `IPTA Time-Series Plan` shows `reference_date = 20241007`. -6. Click the IPTA background-job button. -7. Watch the task until completion. -8. Expected success: - -```text -run.status = IPTA_TIMESERIES_READY -run.next_stage = publish_products -ipta_timeseries_summary.ready = true -``` - -If the job fails, inspect: - -```text -logs/mb_ts_rate.log -ipta_timeseries_summary.json -work/gamma/common_20241007/timeseries/ -``` - -The next production coding stage after `IPTA_TIMESERIES_READY` is product publishing: geocoding `ts_rate` and `sigma_rate`, LOS sign conversion, GeoTIFF generation, web previews, and monitoring-point curve extraction. - -## 2026-05-25 IPTA Failure Triage And Reference-Region Fix - -The first production IPTA run for `sbas_ab96afabead5` failed inside Gamma `mb`. - -Observed result: - -```text -run.status = IPTA_TIMESERIES_FAILED -run.next_stage = fix_ipta_timeseries -execution.returncode = 139 -logs/mb_ts_rate.log = Segmentation fault in mb -``` - -This was not a system-side summary false negative. Gamma `mb` read the input tables successfully: - -```text -DIFF_tab records = 6 -itab_common_ref records = 6 -RMLI_tab entries = 7 -``` - -The failure happened after `mb` printed the reference region and before writing valid `diff_ts.tab` / `itab_ts`. The initial production script reused the experiment's center-pixel reference region: - -```text -range = width / 2 -azimuth = lines / 2 -window = 16 x 16 -``` - -That is fragile for real production stacks. On this run, the center reference window contained zero/invalid unwrapped pixels in multiple interferograms. Gamma `mb` did not emit a clean validation error; it segfaulted. - -Implemented fix: - -- `prepare_ipta_timeseries` now automatically scans the unwrapped interferograms listed in `DIFF_tab`. -- It selects a 16 x 16 reference window with valid nonzero unwrapped values across all pairs and high mean coherence. -- The selected region is written into `ipta_timeseries_plan.json` as `reference_region`. -- `05_mb_ts_rate.sh` now uses the selected `R_REF` / `A_REF` instead of the image center. -- The script removes stale IPTA outputs before running so a retry starts from clean stage outputs. - -Selected reference region for the current run: - -```text -strategy = auto_valid_unwrapped_high_coherence_window -range_pixel = 2152 -azimuth_line = 1512 -window = 16 x 16 -pair_count = 6 -min_valid_pixel_count = 256 -total_valid_pixel_count = 1536 -median_mean_coherence = 0.9953643755 -valid_pixel_count_by_pair = [256, 256, 256, 256, 256, 256] -``` - -Current retry-ready state: - -```text -run.status = IPTA_TIMESERIES_SCRIPT_READY -run.next_stage = execute_ipta_timeseries -scripts/05_mb_ts_rate.sh uses R_REF=2152 and A_REF=1512 -``` - -Validation completed after the fix: - -- Python AST parse passed. -- `SBAS_IPTA_TIMESERIES` job handler resolves successfully. -- Regenerated `05_mb_ts_rate.sh` passes WSL `bash -n`. -- Frontend `npm run build` passed. - -User retry order: - -1. Refresh the SBAS-InSAR production page. -2. Select run `sbas_ab96afabead5`. -3. Confirm status is `IPTA_TIMESERIES_SCRIPT_READY`. -4. Confirm `IPTA Time-Series Plan` contains reference region `2152,1512`. -5. Submit the IPTA task again. - -If the retry still fails, inspect: - -```text -logs/mb_ts_rate.log -ipta_timeseries_summary.json -work/gamma/common_20241007/timeseries/ -``` - -## 2026-05-25 IPTA Mode-1 Failure Closure - -The reference-region fix was necessary but not sufficient. The second production retry still failed inside Gamma `mb` with return code `139`. - -The regenerated plan was correct: - -```text -reference_region = 2152,1512 -window = 16 x 16 -valid_pixel_count_by_pair = [256, 256, 256, 256, 256, 256] -median_mean_coherence = 0.9953643755 -``` - -So the remaining failure was not an invalid reference window. A focused Gamma diagnostic matrix was run against the exact production `DIFF_tab`, `RMLI_tab`, and `itab_common_ref` for run `sbas_ab96afabead5`. - -Diagnostic result: - -```text -full stack, mb mode=1 -> rc=139, segmentation fault -full stack, mb mode=2 -> rc=139, segmentation fault -full stack, mb mode=0 -> rc=0 -reference-star subset, mb mode=1 -> rc=0 -first-3 adjacent-chain subset, mb mode=1 -> rc=0 -single-pair subsets -> non-production diagnostic only, not valid as full stack inversion -``` - -A production-equivalent diagnostic was then run with the same output flags as the experiment script: - -```text -mb sim_flg=1 hgt_flg=1 mode=0 -ts_rate -``` - -That completed successfully and produced: - -```text -diff_ts.tab -itab_ts -sigma_ts -hgt_correction -ts_rate -ts_const -sigma_rate -``` - -The important conclusion is that the original Gamma trial was valid, but the formal production input is not identical to the trial. The trial used a smaller/reference-star style network that Gamma `mb mode=1` accepted. The production run uses the approved full adjacent SBAS chain with 7 dates and 6 interferograms. On the current Gamma 2023 IPTA binary, that full production chain crashes in `mode=1`, while `mode=0` completes. - -Implemented closure: - -- Production default `mb_mode` is now `0`. -- `ipta_timeseries_plan.json` records: - -```text -mb_mode = 0 -mb_mode_description = valid unwrapped phase values required in all layers -``` - -- `05_mb_ts_rate.sh` writes and uses: - -```text -MB_MODE="0" -``` - -- `ipta_timeseries_summary.json` records the mode used by the completed run. -- Router and background job payloads accept `mb_mode`, defaulting to `0`. The UI does not expose this as a normal operator choice yet. -- Script generation now has a fallback path if an existing script file cannot be overwritten because of Windows/WSL ACL drift. - -Current formal service execution result after the fix: - -```text -run_id = sbas_ab96afabead5 -run.status = IPTA_TIMESERIES_READY -run.next_stage = publish_products -execution.returncode = 0 -summary.ready = true -summary.missing_outputs = [] -summary.mb_mode = 0 -diff_ts_row_count = 7 -itab_ts_row_count = 7 -``` - -Output size checks: - -```text -expected_float32_bytes = 45921036 -sigma_ts = 45921036 -hgt_correction = 45921036 -ts_rate = 45921036 -ts_const = 45921036 -sigma_rate = 45921036 -``` - -Operational note: - -During local Codex verification, a direct Windows Python subprocess call to `wsl.exe` returned `Wsl/Service/E_ACCESSDENIED` unless the command was run with external execution permission. That was an execution-context permission issue, not a Gamma processing failure. The same service method completed when WSL execution was allowed. If the production worker reports `Wsl/Service/E_ACCESSDENIED`, fix the worker account/Windows service permissions for WSL access before re-testing Gamma processing. - -One legacy run-directory ACL issue was also fixed manually for `sbas_ab96afabead5`: older files had ACLs that let the current Windows user create new files but not overwrite existing manifest/script files. The current run directory was granted current-user full control so `run_manifest.json`, `ipta_timeseries_plan.json`, and generated scripts can be updated on retries. - -Next stage after this closure is product publishing: - -```text -publish_products -geocode_back ts_rate/sigma_rate -data2geotiff -LOS velocity/sigma products -monitoring point curves -``` - -## 2026-05-26 Product Publishing And Monitoring Point Integration - -The expert-workflow bridge now implements the downstream output work that was previously planned-only. In the current twelve-node workflow these are represented by section 12: - -```text -12_outputs_points -``` - -`10_detrend_atm` is now part of the new development workflow. It is no longer treated as an optional compatibility branch. New production runs should execute section 10 before section 11, and `11_sbas_inversion` should consume `DIFF_atmsub_tab`. - -### 12_outputs_points publish phase - -The stage generates and executes: - -```text -runs/{run_id}/scripts/12_outputs_points.sh -``` - -It follows the experiment-proven path: - -- compute wavelength from `radar_frequency` -- convert `ts_rate` / `sigma_rate` phase rates to LOS -- write both LOS sign conventions: - -```text -los_rate_away_mm_per_year = phase_rate * wavelength / (4*pi) * 1000 -los_rate_toward_mm_per_year = -phase_rate * wavelength / (4*pi) * 1000 -``` - -- geocode with Gamma `geocode_back` -- export GeoTIFFs with Gamma `data2geotiff` -- create RDC QA browse BMPs with `rasdt_pwr` -- create UI map previews from geocoded GeoTIFFs, not from RDC BMPs - -Expected output state: - -```text -run.status = PRODUCTS_READY -run.next_stage = monitor_points -``` - -Expected files: - -```text -publish_product_plan.json -publish_product_summary.json -product_summary.json -quality_summary.json -publish/geotiff/los_rate_toward_mm_per_year.tif -publish/geotiff/los_rate_toward_mm_per_year.geo_preview.png -publish/geotiff/los_rate_away_mm_per_year.tif -publish/geotiff/los_sigma_mm_per_year.tif -publish/geotiff/los_sigma_mm_per_year.geo_preview.png -publish/geotiff/ts_rate_rad_per_year.tif -publish/geotiff/sigma_rate_rad_per_year.tif -``` - -The summary records nonzero finite pixel statistics for LOS velocity and sigma in RDC geometry so the operator can quickly detect blank or extreme outputs. - -### 12_outputs_points monitoring-point phase - -The wrapper also runs the monitoring-point extraction script when product publishing has completed: - -```text -runs/{run_id}/scripts/08_point_timeseries.sh -``` - -It reuses the experiment logic in a parameterized WSL helper: - -```text -deploy/wsl/runners/gamma_sbas_product_tools.py -``` - -Supported point modes in this slice: - -- `auto_low_sigma_high_rate`: automatic non-edge sample point, high absolute LOS velocity and low sigma -- `manual_lonlat`: nearest lookup-table pixel from configured lon/lat points - -The automatic sample remains a diagnostic/sample curve, not a formal monitoring network. - -Expected output state: - -```text -run.status = MONITOR_POINTS_READY -run.next_stage = review_publish_products -``` - -Expected files: - -```text -monitor_points_plan.json -monitor_points_summary.json -publish/monitor_points/{point_id}_timeseries.png -publish/monitor_points/{point_id}_timeseries.csv -publish/monitor_points/{point_id}_metadata.json -``` - -### Workflow Result - -After 10, 11, and 12 complete, the workflow can finish as: - -```text -WORKFLOW_COMPLETED -``` - -instead of `WORKFLOW_PARTIAL`, provided no enabled stage failed. - -Manual test order: - -1. Select run `sbas_ab96afabead5`. -2. Submit Gamma SBAS Workflow with: - -```text -from_step = 12_outputs_points -``` - -3. Expected first completion: - -```text -PRODUCTS_READY -``` - -or full completion: - -```text -MONITOR_POINTS_READY -WORKFLOW_COMPLETED -``` - -4. Confirm the production Run detail shows geocoded LOS velocity and sigma previews. -5. Confirm the monitoring curve is visible and its CSV is downloadable. - -## 2026-05-26 Expert Document Step Index And Color Convention Update - -The user correctly pointed out that the expert document is not an eight-step process. The document has twelve major sections, each with multiple Gamma commands. The production workflow now uses those twelve sections as first-class workflow nodes. The previous eight-stage execution view is retained only as an internal service implementation detail where an already verified experiment script covers more than one expert section. - -The twelve expert sections now appear in `capabilities`, `manifest.json`, `gamma_command_manifest.json`, and the SBAS production page: - -```text -1. Directory and LT1 data preparation -2. Import every LT1 SLC -3. Reference MLI and footprint checks -4. DEM import and lookup table -5. SLC coregistration preparation -6. Coregister every SLC to reference -7. RMLI stack and average intensity -8. Interferogram network and differential phase -9. Adaptive filtering, coherence mask and unwrap -10. Detrend and atmospheric phase removal -11. SBAS inversion -12. Output, geocode and point time-series -``` - -Each section records representative commands from `LT1_GAMMA_SBAS_逐命令处理流程.docx`, mapped workflow stages, and implementation status. Existing successful experiment logic is retained as `implemented_bridge` where it has already proven the same Gamma function, even if the file layout is not yet identical to the document. - -Current acceptance status: - -```text -1-4 implemented_bridge or implemented -5-9 implemented_bridge -10 implemented_bridge: quad_fit/quad_sub/atm_mod_2d/fill_gaps/atm_sim_2d/sub_phase now writes DIFF_atmsub_tab; needs live production validation -11 implemented_bridge: mb/ts_rate now prefers DIFF_atmsub_tab; the full expert multi-pass unw_to_cpx/unw_model refinement is still not fully migrated -12 implemented_bridge: publish and monitor outputs exist; Gamma expert browse color products are now added -``` - -Color and browse products were corrected toward the expert document conventions. Velocity browse products now prefer Gamma `rasdt_pwr` with `hls.cm` and the expert range `-0.08 0.08` m/year. Sigma/quality browse products now use `cc.cm`; because production currently displays LOS sigma-rate rather than `diff.sigma_ts.masked`, the range is adapted to `0.0 0.06` m/year while retaining the expert color table family. Phase, detrend, and atmospheric browse products should use `rmg.cm` with `-6.28 6.28` radians when section 10 is implemented. - -New preferred publish outputs: - -```text -publish/geotiff/los_rate_toward_m_per_year.hls.bmp -publish/geotiff/los_rate_toward_m_per_year.hls.geo_rgb.tif -publish/geotiff/los_rate_toward_m_per_year.hls.geo_preview.png -publish/geotiff/los_sigma_m_per_year.cc.bmp -publish/geotiff/los_sigma_m_per_year.cc.geo_rgb.tif -publish/geotiff/los_sigma_m_per_year.cc.geo_preview.png -publish/geotiff/los_rate_toward_m_per_year.tif -publish/geotiff/los_rate_away_m_per_year.tif -publish/geotiff/los_sigma_m_per_year.tif -``` - -Legacy millimeter-per-year products remain published for comparison with earlier experiments: - -```text -publish/geotiff/los_rate_toward_mm_per_year.tif -publish/geotiff/los_rate_toward_mm_per_year.geo_preview.png -publish/geotiff/los_sigma_mm_per_year.tif -publish/geotiff/los_sigma_mm_per_year.geo_preview.png -``` - -Next implementation target is the rest of section 11. The managed section 10 node now generates `unw.atmsub` products and `DIFF_atmsub_tab`; section 11 consumes that table. The remaining gap is the expert multi-pass `unw_to_cpx` / `unw_model` refinement path. - -## 2026-05-26 Twelve-Node Production Workflow Correction - -The production workflow has been corrected from the temporary `8 coarse stages + 12-section checklist` view to a twelve-node workflow: - -```text -01_workspace_data -02_import_lt1_slc -03_reference_mli -04_dem_lookup -05_coreg_prep -06_coregister_scenes -07_rmli_average -08_diff_network -09_filter_unwrap -10_detrend_atm -11_sbas_inversion -12_outputs_points -``` - -The bridge still reuses verified scripts from the successful experiment where that is safer than rewriting Gamma command chains immediately: - -```text -02-03 reuse the baseline-audit import/multilook/base_calc implementation. -05-07 reuse the common-reference coregistration implementation. -08-09 reuse the differential interferogram/filter/unwrap implementation. -10 uses the expert detrend/atmospheric-correction implementation. -11 uses mb/ts_rate over DIFF_atmsub_tab. -12 runs publish products followed by monitoring-point extraction. -``` - -Dependency handling is no longer a simple linear coarse-stage status check. Section 4 can run after baseline/reference MLI preparation; sections 8-9 require both the DEM stage execution and the coregistration stage execution to be recorded as completed in the current run manifest. - -Old experiment outputs are not accepted as expert-path validation. Development runs may be deleted and regenerated; the twelve-node workflow should be validated from a clean run so that each node has a fresh execution record. Existing summary JSON files may remain as operator evidence, but the workflow runner must not use them to skip expert nodes. - -## 2026-05-26 Expert Section 10 Detrend/ATM Stage - -The formal workflow order is now: - -```text -08_diff_network -09_filter_unwrap -10_detrend_atm -11_sbas_inversion -12_outputs_points -``` - -`10_detrend_atm` writes: - -```text -detrend_atm_plan.json -detrend_atm_summary.json -work/gamma/common_/DIFF_atmsub_tab -work/gamma/common_/itab_atmsub -work/gamma/common_/detrend_atm//_rlks.diff_filt.unw.atmsub -``` - -The stage follows the expert section 10 command family: - -```text -create_diff_par -quad_fit -quad_sub -rasdt_pwr ... rmg.cm -atm_mod_2d -fill_gaps -atm_sim_2d -sub_phase -rasdt_pwr ... rmg.cm -``` - -The `fill_gaps` width for `a0/a1` model grids is inferred from the generated coefficient-file size and the reference MLI aspect ratio. If inference or `fill_gaps` fails, the script falls back to the raw atmospheric coefficients and records the warning in the pair log; this keeps the production test actionable while preserving the expert command path. - -`11_sbas_inversion` now requires `DETREND_ATM_READY` and uses: - -```text -DIFF_TAB = work/gamma/common_/DIFF_atmsub_tab -ITAB = work/gamma/common_/itab_atmsub -``` - -Production test expectation: - -```text -run.status after 10 = DETREND_ATM_READY -run.next_stage after 10 = ipta_timeseries -run.status after 11 = IPTA_TIMESERIES_READY -``` - -## 2026-05-27 Runtime Cleanup And Strict Production Display - -The previous development/test outputs were removed so the next SBAS-InSAR test starts from a clean runtime state: - -```text -backend/runtime/sbas_insar_production/discoveries/* -backend/runtime/sbas_insar_production/runs/* -backend/runtime/sbas_insar_production/stack_manifests/* -backend/runtime/gamma_ipta_trials/* -backend/runtime/gamma_ipta_probe/* -``` - -The frontend SBAS-InSAR production page no longer lists or opens `trial-runs`. It now loads only managed production `runs`, and product links use `/api/sbas-insar-production/runs/{run_id}/artifacts/...`. - -The workflow runner is intentionally strict after this cleanup. Old experiment summaries or sidecar `*_summary.json` files are not accepted as proof that an expert node has completed. A node can be skipped or advanced only when the current run manifest contains a completed execution record for the corresponding stage. - -## 2026-05-27 Result Management And Geographic Coverage Design - -The first clean Gamma SBAS workflow run completed all twelve expert nodes: - -```text -run_id = sbas_7537cc71c998 -status = WORKFLOW_COMPLETED -scene_count = 7 -pair_count = 6 -workflow_summary.completed_count = 12 -workflow_summary.failed_count = 0 -``` - -This means production execution is now viable enough to split the user experience into two modules: - -```text -SBAS-InSAR Production - - discover stack candidates - - create production Run - - submit Gamma SBAS workflow - - inspect 12 expert nodes, scripts, logs, and retry state - -SBAS-InSAR Results - - browse stable products - - inspect geographic footprint and map location - - preview LOS velocity, LOS sigma, and monitoring curves - - download GeoTIFF/PNG/CSV/supporting manifests - - jump back to the source production Run for audit/debug -``` - -### Geographic Coverage Gap - -SBAS is time-series processing, so temporal density is important, but result users primarily ask "where is this product?". The current production page does not make geographic location obvious even though the metadata already exists. - -Available geographic sources in the current run: - -```text -stack_manifest.scenes[*].bbox -stack_manifest.scenes[*].center_lon / center_lat -stack_manifest.stack.center_bucket -rdc_dem_summary.dem_source.stack_bbox -monitor_points_summary.monitor_outputs[*].metadata.approx_lonlat -published GeoTIFF bounds from GDAL metadata -``` - -Example from `sbas_7537cc71c998`: - -```text -stack center bucket = E129.2_N44.1 -stack bbox = 128.7690438245, 43.7486321624, 129.6293024728, 44.3582486206 -monitor point = 129.10207098755, 44.15041727515 -``` - -The production page should add a compact `Geographic Coverage` block near the selected Run summary: - -```text -center lon/lat -stack bbox -scene footprint count -DEM coverage status: covers stack bbox / covers stack center -monitor point lon/lat if generated -open on map / zoom to footprint action -``` - -This block is operational context, not a replacement for a result browser. It helps the operator avoid running or reviewing the wrong location. - -### Result Product Boundary - -One completed SBAS workflow Run should register one result product bundle, not many separate products. This follows the existing D-InSAR/PsInSAR catalog pattern: - -```text -result_products: one row per SBAS bundle -result_assets: multiple files under the bundle -result_issues: missing/invalid/geocoding/quality warnings -catalog_name: sbas_insar -run_key: source SBAS run_id -``` - -The product record should carry first-class query fields: - -```text -platform / satellite -relative_orbit -orbit_direction -polarization -reference_date -start_date -end_date -scene_count -pair_count -bbox_min_lon / bbox_min_lat / bbox_max_lon / bbox_max_lat -center_lon / center_lat -status / health_status -primary_asset_role -quality_asset_role -source_run_id -``` - -Do not treat every GeoTIFF as a separate product row. The user-facing product is the SBAS result for one stack/run over one geographic footprint and time span. - -### Important Assets From The Expert Document - -The expert document makes section 12 outputs the formal product boundary. Important product roles: - -```text -primary_velocity_geotiff - expert source: geo_los_def_rate.tif - current system: publish/geotiff/los_rate_toward_m_per_year.tif - -primary_velocity_rgb_geotiff - expert source: geo_los_def_rate_rgb.tif - current system: publish/geotiff/los_rate_toward_m_per_year.hls.geo_rgb.tif - -primary_velocity_preview - expert source: los_def_rate.bmp / geo_los_def_rate.bmp - current system: publish/geotiff/los_rate_toward_m_per_year.hls.geo_preview.png - -quality_sigma_geotiff - expert source: diff.sigma_ts / geo_diff.sigma_ts - current system: publish/geotiff/los_sigma_m_per_year.tif - -quality_sigma_preview - expert source: diff.sigma_ts.masked.bmp with cc.cm - current system: publish/geotiff/los_sigma_m_per_year.cc.geo_preview.png - -monitor_timeseries_csv - expert source: disp_prt_2d outputs - current system: publish/monitor_points/*_timeseries.csv - -monitor_timeseries_plot - current system: publish/monitor_points/*_timeseries.png - -support_manifest - run_manifest.json, workflow_summary.json, gamma_command_manifest.json, stage summaries -``` - -The Results UI should foreground only the primary and quality products by default. Supporting manifests and stage summaries belong in an "Audit files" section. - -### SBAS-InSAR Results UI - -List view: - -```text -left/top filters: - time range - geographic bbox / map AOI - platform - relative orbit - direction - status / health - has monitor points - -result row/card: - product name - footprint mini-map or bbox text - start/end/reference dates - scene count / pair count - LOS velocity preview thumbnail - sigma health indicator - actions: open details, zoom to map, download primary GeoTIFF -``` - -Detail view: - -```text -map footprint panel -LOS velocity preview -LOS sigma preview -monitoring point curve -key metadata table -asset table grouped by role -quality summary -link back to production Run and 12-node workflow -``` - -Map behavior: - -```text -use stack bbox as initial footprint -prefer GeoTIFF bounds when parsed successfully -show monitor points as point overlays -allow "zoom to result" -allow AOI filter against bbox intersection -``` - -### Backend Work Items - -1. Add `sbas_insar_catalog_service.py`. -2. Register completed SBAS runs into `result_products/result_assets` with `catalog_name = sbas_insar`. -3. Derive `bbox` and `center` from `stack_manifest.scenes[*].bbox`; verify or refine from GeoTIFF metadata when available. -4. Add startup self-maintenance similar to D-InSAR/PsInSAR catalog bootstrapping: - -```text -scan completed SBAS run publish bundles -upsert missing catalog rows -check primary/quality assets exist and are non-empty -record issues for missing bbox, missing GeoTIFF, missing preview, or DEM coverage mismatch -``` - -5. Add API routes: - -```text -GET /api/sbas-insar-products/catalog-status -POST /api/sbas-insar-products/rebuild-catalog -GET /api/sbas-insar-products -GET /api/sbas-insar-products/{product_id} -GET /api/sbas-insar-products/{product_id}/assets/{asset_id} -``` - -6. Extend production run detail to include explicit `geographic_coverage`: - -```json -{ - "center": {"lon": 129.199, "lat": 44.053}, - "bbox": {"min_lon": 128.769, "min_lat": 43.749, "max_lon": 129.629, "max_lat": 44.358}, - "scene_bbox_count": 7, - "dem_covers_stack_bbox": false, - "dem_covers_stack_center": true, - "monitor_points": [{"point_id": "...", "lon": 129.102, "lat": 44.150}] -} -``` - -### Frontend Work Items - -1. Add geographic coverage block to `SbasInsarProductionPanel`. -2. Add new `SbasInsarProductsPanel`. -3. Add API client module for SBAS product catalog. -4. Add navigation entry under result management/result analysis, separate from production management. -5. Reuse existing map overlay patterns from D-InSAR where practical, but keep SBAS asset roles and product semantics independent. diff --git a/docs/SBAS_STACK_DISCOVERY_AOI_DESIGN_20260528.md b/docs/SBAS_STACK_DISCOVERY_AOI_DESIGN_20260528.md deleted file mode 100644 index 40130fa..0000000 --- a/docs/SBAS_STACK_DISCOVERY_AOI_DESIGN_20260528.md +++ /dev/null @@ -1,311 +0,0 @@ -# SBAS-InSAR 序列发现与 AOI 选栈设计 - -## 现状结论 - -当前系统不是限制最多 7 景。对 `D:\LuTan1_Image_Pool` 的检查结果为: - -- LT1 场景目录:1500 个; -- 按当前严格规则分组后:886 个候选序列; -- 最大可用序列:7 个日期。 - -当前发现逻辑的硬分组键为: - -```text -satellite -satellite_mode -receiving_station -relative_orbit -orbit_direction -imaging_mode -polarization -center_bucket -``` - -其中 `center_bucket` 约为 0.1 度经纬度格网。这个规则保守、容易复现,但它不是标准 SBAS 选栈方法。它会把相邻 frame、中心点略有偏移但实际覆盖同一 AOI 的影像拆成不同序列。 - -## 一般 SBAS 选序列方法 - -SBAS 序列选择通常不是先按影像中心点硬分组,而是围绕一个目标区域 AOI 建栈: - -1. 选择目标区域 - AOI 可以是行政区、工程区、多边形、bbox、中心点缓冲区或已有项目范围。 - -2. 选择同一观测几何 - 一般要求同一轨道方向、同一相对轨道、同一成像模式、同一极化、相近视角和足够 footprint 重叠。 - 对 LT1 当前实现,默认仍应保持 LT1A/LT1B 分开;跨星合并只能作为高级实验模式。 - -3. 按 AOI 覆盖筛选影像 - 影像 footprint 需要覆盖 AOI,或者至少满足指定覆盖比例。最终处理范围通常取所有入选影像的公共交集。 - -4. 检查时间密度 - 关注日期数量、最大时间间隔、季节性断档、时间跨度。SBAS 越密越好,但必须保证网络连通。 - -5. 检查轨道和 DEM 可用性 - 精轨缺失的影像可先展示,但默认不进入可生产栈。 - -6. 构建小基线网络 - 不是简单相邻配对。常见做法是根据时间基线和垂直基线构图,选择满足阈值的边,并保证图连通。 - -7. 用处理引擎验证基线 - 真实垂直基线应由 Gamma `base_calc` 或等价步骤计算。元数据阶段只能做预筛选,不能替代最终 baseline audit。 - -## 专家文档关系 - -专家文档没有写自动“找序列”算法,它假设用户已经准备好 `RAW//` 数据,并在运行前手动修改日期、阈值、宽高和种子点等参数。 - -文档中的 `base_calc` 小基线阈值示例类似: - -```text -spatial baseline: -1000 1000 -temporal baseline: 0 120 -``` - -这说明专家链路里真正决定 SBAS 网络的是 base_calc/itab 阶段。系统需要做的是把“人工准备 RAW 日期序列”产品化成可审查的 AOI 选栈和网络计划。 - -## 设计目标 - -1. 保留当前严格模式,作为快速、保守、可复现实验路径。 -2. 增加 AOI 发现模式,按行政区/AOI 查找覆盖同一目标区域的影像。 -3. 把 `center_bucket` 从用户可见的生产条件降级为内部诊断字段。 -4. 把接收站从硬条件降级为软提示,除非后续实测证明必须拆分。 -5. 在创建 Run 前只展示用户需要判断的生产信息:时间范围、景数、覆盖质量、网络质量和风险标签。 -6. 生成可审查的 Stack Manifest v2 和 Pair Network Plan,再交给 Gamma baseline audit 验证。 - -## 发现模式 - -### 1. Strict 模式 - -当前模式,继续保留。 - -适用场景: - -- 快速测试; -- 已经验证能跑通的固定栈; -- 用户希望尽量避免覆盖差异和几何风险。 - -硬分组字段: - -```text -satellite -relative_orbit -orbit_direction -imaging_mode -polarization -center_bucket -``` - -`receiving_station` 建议改为默认软字段,不再强拆。 - -### 2. AOI 模式 - -新推荐模式。 - -输入: - -```text -admin_region -bbox -geojson polygon -center + radius -``` - -处理: - -1. 找到所有 footprint 与 AOI 相交的 LT1 场景; -2. 按观测几何分组; -3. 计算每景 AOI 覆盖比例; -4. 过滤覆盖比例不足的影像; -5. 计算公共交集范围; -6. 统计日期、时间间隔和精轨完整性; -7. 输出候选栈。 - -建议默认阈值: - -```text -min_scenes: 5 -dev_min_scenes: 3 -min_aoi_coverage_ratio: 0.80 -min_common_overlap_ratio: 0.60 -warn_max_gap_days: 120 -hard_fail_max_gap_days: none,改为 warning -``` - -如果 AOI 是行政区且行政区很大,不应要求单景覆盖整个行政区。应允许用户进一步选择 bbox/工程区,或者默认用行政区中心缓冲区进行候选发现。 - -### 3. 内部诊断 - -诊断不是用户入口,也不作为生产模式展示。它只用于日志、运维、自检和开发排查。 - -内部输出: - -```text -raw_scene_count -parsed_scene_count -group_count -top_groups_by_scene_count -top_groups_by_date_count -excluded_by_missing_orbit -excluded_by_geometry -excluded_by_aoi_coverage -excluded_by_common_overlap -``` - -这些信息可以写入 manifest/log,必要时在管理员调试页查看。普通用户不需要看到“为什么只有 7 景”这类开发解释。 - -## 小基线网络设计 - -发现阶段只生成候选网络,最终以 Gamma `base_calc` 为准。 - -建议流程: - -1. 对候选日期生成全部可能 pair; -2. 先按时间基线过滤; -3. 运行或计划 Gamma `base_calc` 得到真实垂直基线; -4. 按垂直基线过滤; -5. 检查网络连通性; -6. 如果断开,允许加入 bridge edge,并标记为超阈值连接; -7. 输出 `itab` 和 pair network summary; -8. 前端要求用户审批。 - -推荐网络策略: - -```text -primary: connected small-baseline graph -fallback: adjacent chain -bridge: allow one or more warning edges when sparse archive causes seasonal gap -``` - -对当前数据尤其重要:如果严格使用 120 天时间阈值,2024-10 到 2025-05 的 224 天断档会导致网络断开。系统应显示风险,而不是静默删除后续年份。 - -## Stack Manifest v2 - -新增字段: - -```text -discovery_mode -aoi -aoi_source -geometry_group_key -hard_group_fields -soft_group_fields -scene_coverage -common_intersection -date_stats -orbit_stats -candidate_pair_network -diagnostics -``` - -每景新增: - -```text -aoi_overlap_ratio -common_intersection_participation -selection_status -selection_reasons -``` - -每个 pair 新增: - -```text -temporal_baseline_days -perpendicular_baseline_m -pair_status -bridge_edge -rejection_reason -``` - -## 前端设计 - -候选序列发现页增加: - -1. 生产区域选择:行政区、bbox、GeoJSON 或中心点缓冲区; -2. 观测条件:轨道方向、相对轨道、极化、时间范围; -3. 高级参数折叠区:覆盖阈值、最小景数、是否要求精轨完整; -4. 候选列表显示: - - 日期数; - - 可生产景数; - - AOI 覆盖率; - - 公共交集面积; - - 最大时间间隔; - - 网络质量; - - 风险标签; - - 推荐/可生产/需确认状态。 - -候选详情显示: - -```text -日期列表 -覆盖范围摘要 -时间跨度和最大间隔 -pair network 摘要 -base_calc 审核结果 -``` - -用户界面不展示原始分组数、center_bucket 分裂原因、解析失败目录等开发诊断。若需要追踪问题,这些信息进入后台日志或管理员自检接口。 - -## 实施步骤 - -### 阶段一:AOI 选栈入口 - -- 增加 AOI discovery mode 参数; -- 支持行政区/bbox/GeoJSON 输入; -- 前端只显示推荐候选和风险标签。 - -### 阶段二:场景覆盖筛选 - -- 使用已有 LT1 bbox 元数据; -- 用 shapely 计算 AOI 交集和覆盖率; -- 输出 AOI candidate stack。 - -### 阶段三:Stack Manifest v2 - -- 记录 AOI、覆盖率、软硬分组字段; -- 创建 Run 时冻结 v2 manifest; -- 保持现有生产链路可读取 scenes 列表。 - -### 阶段四:网络计划升级 - -- 发现阶段生成候选 pair graph; -- baseline audit 阶段用 Gamma `base_calc` 回填真实 Bperp; -- 前端审批连通网络,而不是只审批相邻链。 - -### 阶段五:生产联调 - -- 用当前 1500 景数据池分别测试: - - 严格模式是否仍得到 7 景; - - AOI 模式是否能扩大目标区域候选; - - 扩大后公共交集是否仍足够; - - Gamma coreg/base_calc 是否接受新栈。 - -### 阶段六:内部诊断与自检 - -- 将严格分组统计、排除原因和解析错误写入 discovery log; -- 管理员自检接口可查看诊断摘要; -- 普通生产页面不展示开发诊断细节。 - -## 风险 - -- AOI 模式可能把相邻 frame 合进来,导致公共交集变小。 -- 跨 LT1A/LT1B 合并可能存在几何和相位一致性风险,默认不启用。 -- 接收站是否可合并需要用实测验证;先作为软字段。 -- 时间阈值过严会把稀疏数据切断,过宽会降低反演质量,需要前端显式提示。 - -## 当前建议 - -短期先做 AOI 模式候选发现,不要把“为什么只有几景”的开发诊断放到普通用户页面。 -生产仍默认走可靠可审查的栈,等 AOI 候选经过 baseline audit 和一次完整 Gamma 测试后,再把 AOI 模式设为推荐入口。 - -## 2026-05-28 实施记录 - -本轮已把“生产区域”接入 SBAS 候选发现链路: - -- `/sbas-insar-production/stacks/discover` 支持 `discovery_mode=aoi`、`admin_region`、`aoi_bbox`、`min_aoi_coverage_ratio`、`min_common_overlap_ratio`; -- 后端可把行政区名称解析成 AOI 几何,使用 LT1 元数据 bbox 与 AOI 相交关系筛选场景; -- AOI 模式按观测几何分组,不再把 `center_bucket` 和 `receiving_station` 作为用户生产入口的硬拆分条件; -- 候选结果新增 `discovery_mode`、`aoi`、`common_overlap_ratio`、`aoi_overlap_ratio_mean/min/max`、`hard_group_fields`、`soft_group_fields`; -- `audit_stack` 和 `create_run` 已传递同一套 AOI 参数,确保发现、Manifest、Run 计划冻结的是同一候选序列; -- 前端“候选 SBAS 序列发现”改为“SBAS 生产区域”,用户只输入行政区并查看日期、景数、精轨、公共重叠和覆盖摘要; -- Run 列表不再显示 `center_bucket`,改显示行政区、平台和相对轨道。 - -当前前端只暴露行政区入口;bbox/GeoJSON 可作为下一步高级入口接入,但不应在普通页面展示开发诊断信息。 diff --git a/docs/SENTINEL1_DATA_MANAGEMENT_ADAPTATION_PLAN_20260510.md b/docs/SENTINEL1_DATA_MANAGEMENT_ADAPTATION_PLAN_20260510.md deleted file mode 100644 index a668044..0000000 --- a/docs/SENTINEL1_DATA_MANAGEMENT_ADAPTATION_PLAN_20260510.md +++ /dev/null @@ -1,340 +0,0 @@ -# Sentinel-1 数据管理改造规划 - -日期:2026-05-10 - -本文只规划“让系统能管理哨兵一号 Sentinel-1 数据”的改造范围,不记录已完成实现。当前建议把目标分成两层:先支持 Sentinel-1 源数据入库、检索、预览、配对和分发;真正进入 ISCE2 / PyINT / SARscape 的 Sentinel-1 自动生产链路,作为后续独立阶段处理。 - -## 1. 目标边界 - -本轮建议做: - -1. 扫描并登记 Sentinel-1 SLC `.SAFE` 解包目录。 -2. 解析 Sentinel-1 基础元数据,包括卫星、成像模式、极化、起止时间、绝对轨道、相对轨道、升降轨、footprint。 -3. 管理 Sentinel-1 精密轨道 `.EOF` 文件,并把影像按 acquisition time 匹配到覆盖该时间窗的轨道文件。 -4. 在源数据检索、配对规划、任务批次和数据分发中正确显示和使用 Sentinel-1。 -5. 对 Sentinel-1 配对增加必要的轻量约束,避免只靠 footprint 交叠产生不科学的候选对。 - -本轮不建议做: - -1. 不把 Sentinel-1 自动送入现有 LT1 专用 ISCE2 / PyINT 流水线。 -2. 不做 burst 级精确公共覆盖计算。 -3. 不计算真实垂直基线,不把 footprint 中心距伪装成 SAR 空间基线。 -4. 不强制支持 `.SAFE.zip` 直接入库;优先支持已解包的 `.SAFE` 目录,zip 可作为后续增强。 - -## 2. 当前代码现状 - -已有基础: - -- [backend/app/utils.py](../backend/app/utils.py) 已经有 `parse_s1_radar_filename()` 和 `parse_s1_orbit_filename()` 的雏形。 -- [backend/app/services/data_service.py](../backend/app/services/data_service.py) 的 `scan_radar_data()` 是通用源数据扫描入口,理论上可以扫描 LT1 / S1 / GF3。 -- `radar_data` 已有 `satellite`、`satellite_family`、`imaging_date`、`imaging_mode`、`polarization`、`orbit_direction`、`product_type`、`source_product_token`、`has_orbit_data`、`orbit_file_path`、`geom`、`coverage_polygon` 等通用字段。 -- [backend/app/services/pairing_cache_service.py](../backend/app/services/pairing_cache_service.py) 已经把 `S1A/S1B` 归为 `S1` family,配对层具备同卫星族筛选能力。 -- [backend/app/copier.py](../backend/app/copier.py) 分发逻辑是复制原始产品目录,不再依赖 `envi_import`,这对 `.SAFE` 目录是有利的。 - -主要缺口: - -- Sentinel-1 `.SAFE` 的关键元数据主要在 `manifest.safe` 和 `annotation/*.xml`,当前 `find_xml_file()` / `parse_xml_metadata()` 更偏 LT1 风格 XML,不能可靠解析 S1 footprint、升降轨、相对轨道。 -- 当前精轨模块 [backend/app/services/orbit_converter.py](../backend/app/services/orbit_converter.py) 基本写死 LT1 `LT1A_GpsData_GAS_C_YYYYMMDD.txt`,并会同步/转换到 ENVI、ISCE2 池;Sentinel-1 `.EOF` 不能走这套转换逻辑。 -- 当前配对缓存只要求同升降轨、同 look direction、footprint 相交、同卫星族、同模式/极化等。对 Sentinel-1 来说还缺少“同相对轨道/同轨道方向/同 beam mode/必要时同 slice 或同 burst 覆盖”的约束。 -- 前端卫星组 [frontend/src/config/appConstants.js](../frontend/src/config/appConstants.js) 目前只有 LT-1、GF-3;配对默认 `allowed_satellites` 是 `LT1A/LT1B`,导入 S1 后如果不改,会默认把 S1 排除。 -- 现有 ISCE2 / PyINT 生产脚本明显是 LT1 专用,不能因为管理了 S1 数据就默认允许一键生产。 - -## 3. Sentinel-1 文件名能提供的信息 - -标准 Sentinel-1 SLC SAFE 名称通常类似: - -```text -S1A_IW_SLC__1SDV_20250101T104105_20250101T104132_XXXXXX_YYYYYY_ZZZZ.SAFE -``` - -可直接从文件名得到: - -- `satellite`: `S1A` / `S1B` -- `satellite_family`: `S1` -- `imaging_mode`: `IW` / `EW` / `SM` -- `product_type`: `SLC` -- `product_level`: 可从 `1SDV` 中解析出 level 1 -- `polarization`: `DV`、`DH`、`SV`、`SH` -- `acquisition_start_time_utc` -- `acquisition_stop_time_utc` -- `imaging_date` -- `absolute_orbit`,即文件名中的 6 位轨道号 -- datatake id / product unique id - -文件名通常不能可靠提供: - -- footprint -- orbit direction,升轨/降轨 -- relative orbit / track -- look direction -- burst 范围 -- 精轨覆盖关系 - -因此只靠文件名可以完成“粗入库”,但要做可靠配对,至少还要读取 `manifest.safe` 或 annotation XML。 - -## 4. 建议新增的元数据模型 - -为了控制工作量,不建议把 Sentinel-1 所有元数据拆成大量列。建议采用“少量关键列 + JSON 扩展”的方式。 - -建议新增字段: - -- `acquisition_start_time_utc` -- `acquisition_stop_time_utc` -- `relative_orbit` -- `absolute_orbit` -- `slice_number` -- `sensor_operational_mode`,可与现有 `imaging_mode` 保持一致或作为补充 -- `swath_list`,可用 JSON 或逗号文本保存 `IW1/IW2/IW3` -- `source_format`,例如 `SAFE_DIR`、`SAFE_ZIP`、`LT1_DIR` -- `metadata_json`,保存 Sentinel-1 专用扩展信息 - -如果想更轻量,也可以第一阶段只增加: - -- `relative_orbit` -- `acquisition_start_time_utc` -- `acquisition_stop_time_utc` -- `metadata_json` - -这四个字段已经足够支撑 Sentinel-1 基础管理和更科学的配对过滤。 - -数据库处理要求: - -- 新增 ORM 字段和 Pydantic schema 字段。 -- 新增幂等迁移 `010_sentinel1_source_fields.sql`。 -- 把该迁移加入 [backend/app/db_maintenance.py](../backend/app/db_maintenance.py) 的 `MIGRATION_FILES`。 -- 健康检查里至少确认新增列存在;如果后续扩展精轨库存表,再把库存一致性纳入健康检查。 - -## 5. 入库扫描设计 - -建议把现有解析逻辑拆成“产品类型适配器”: - -```text -SourceProductParser - - LT1 parser - - Sentinel1 SAFE parser - - GF3 parser -``` - -Sentinel-1 parser 负责: - -1. 识别 `.SAFE` 目录。 -2. 从目录名解析粗元数据。 -3. 读取 `manifest.safe`。 -4. 读取 annotation XML 获取或校正: - - pass / orbit direction - - relative orbit - - absolute orbit - - footprint 坐标 - - start / stop time - - polarization / swath -5. 生成 `coverage_polygon`、`geom`、`min/max lon/lat`。 -6. 判断 `insar_source_ready`: - - 必须是 `SLC` - - 必须有 footprint - - 必须有 imaging date / acquisition time - - 必须有 orbit direction - - Sentinel-1 推荐必须有 relative orbit - -兼容策略: - -- 已解包 `.SAFE` 目录优先。 -- `.SAFE.zip` 不在第一阶段直接解析,除非用户明确需要。后续可以在扫描器里只读 zip 内 `manifest.safe`,但这会明显增加实现和测试量。 -- 预览图优先复用 SAFE 内 `preview/quick-look.png` 或其他 quicklook 文件;当前 `find_radar_preview_source()` 已有关键词扫描,可少量适配。 - -## 6. Sentinel-1 精轨管理设计 - -Sentinel-1 精轨文件是 `.EOF`,匹配逻辑不是“卫星 + 日期等于影像日期”,而是: - -```text -orbit.satellite == scene.satellite -and orbit.validity_start <= scene.acquisition_time <= orbit.validity_stop -``` - -建议新增一个 Sentinel-1 orbit inventory 解析路径: - -- 解析 `S1A/S1B` -- 解析轨道类型:`AUX_POEORB` / `AUX_RESORB` -- 解析 validity start / stop -- 记录原始 `.EOF` 路径 - -第一阶段可以不建独立轨道表,仍然把匹配结果写回 `radar_data.has_orbit_data` 和 `radar_data.orbit_file_path`。但代码上要避免把 `.EOF` 送进 LT1 的 txt->xml 转换流程。 - -更稳的设计是后续新增 `orbit_files` 表: - -- `satellite` -- `satellite_family` -- `orbit_type` -- `validity_start_utc` -- `validity_stop_utc` -- `file_path` -- `metadata_json` - -考虑到用户希望初级任务不要太复杂,第一阶段建议先不建表,只做扫描时内存索引和 `radar_data` 回填。 - -## 7. 配对逻辑改造 - -当前配对缓存可继续使用,但 Sentinel-1 应增加轻量硬约束。 - -建议硬约束: - -- 同 `satellite_family = S1` -- 默认允许 `S1A/S1B` 跨星,但必须同 Sentinel-1 family -- 同 `orbit_direction` -- 同 `relative_orbit` -- 同 `imaging_mode` -- 同 `polarization` 或至少主极化兼容 -- footprint 有交叠 -- `insar_source_ready = true` - -建议筛选指标: - -- `time_baseline_days` -- `scene_overlap_ratio` -- `scene_center_distance_meters`,只作为 footprint 中心距,不叫真实空间基线 -- `has_orbit_data` - -不建议第一阶段做: - -- 真实垂直基线计算 -- TOPS burst 级 overlap -- 自动下载 ASF / Copernicus metadata -- Sentinel-1 burst id 图层化管理 - -配对缓存字段建议: - -- 给 `pairing_metric_cache` 增加 `same_relative_orbit` 或通用 `same_track`。 -- 或者在 SQL 硬约束里按 `radar_data.relative_orbit` 直接过滤。 -- 在 `selection_meta_json` 中记录 `relative_orbit`、`orbit_direction`、`source_family`,便于追溯。 - -## 8. 数据分发设计 - -分发层整体可以复用: - -- master 复制完整 `.SAFE` 目录到 `Task_*/master/` -- slave 复制完整 `.SAFE` 目录到 `Task_*/slave/` -- 如果启用精轨分发,则把 `.EOF` 复制到 `Task_*/orbit/` -- `.dinsar_pair.json` 中增加 Sentinel-1 专用元数据,如 relative orbit、acquisition start/stop、orbit EOF 路径 - -需要注意: - -- `.SAFE` 目录可能很大,zip 分发开关已经存在的话可以复用。 -- 不建议在分发阶段裁剪 SAFE 或抽取 measurement 文件,这会把“数据管理”变成“预处理”。 -- 如果源数据是 `.SAFE.zip`,第一阶段可以要求用户先解包;否则分发逻辑要支持复制 zip 并在生产侧再解包。 - -## 9. 前端改造 - -需要处理的点: - -- `SATELLITE_GROUPS` 增加 `S1` / `Sentinel-1`。 -- 源数据检索高级字段增加相对轨道、起止时间、源格式等字段。 -- 配对弹窗默认不要固定 `LT1A/LT1B`;建议默认“不限定卫星”,或者根据当前检索结果自动选择可见卫星。 -- 配对参数文案继续使用“footprint 中心距上限”,不要改回“空间基线”。 -- 对 Sentinel-1 配对显示 `relative_orbit` 和 `orbit_direction`,让用户一眼能看出是不是同轨道。 -- 生产提交页如果任务包是 Sentinel-1,但选择了 LT1 专用 PyINT/ISCE2 profile,应给出阻止或强提示。 - -## 10. 生产链路边界 - -现有生产引擎不应直接宣布支持 Sentinel-1: - -- ISCE2 当前 pipeline 和 profile 明显是 LT1 定制,包含 LT1 wavelength、LT1 输入 resolver、LT1 轨道转换。 -- PyINT / Gamma 当前输入搜索是 `LT1*.tar.gz` / `LT1*.tiff`,也属于 LT1 专用。 -- SARscape 理论上能处理 Sentinel-1,但当前系统侧是否能用 raw SAFE 驱动 SARscape D-InSAR,需要单独验证模板、参数和 task runner。 - -所以建议第一阶段只做到: - -```text -Sentinel-1 数据管理 -> 配对规划 -> 批次保存 -> 数据分发 -``` - -生产执行留到第二阶段: - -```text -Sentinel-1 Task_* -> 指定 Sentinel-1 engine/profile -> 预检 -> 生产 -``` - -## 11. 实施阶段建议 - -### 阶段 1:管理与检索 - -工作内容: - -- 完成 Sentinel-1 SAFE parser。 -- 从 `manifest.safe` / annotation XML 提取 footprint、relative orbit、pass、start/stop time。 -- 支持 `.EOF` 精轨扫描和时间窗匹配。 -- 增加必要数据库字段和迁移。 -- 前端增加 Sentinel-1 过滤和显示。 - -预估工作量:2-4 天。 - -风险:主要在不同 SAFE 版本 XML 结构差异,需要准备 3-5 个真实样本覆盖 S1A/S1B、IW、单双极化。 - -### 阶段 2:配对与分发 - -工作内容: - -- 配对缓存加入 `relative_orbit` 约束。 -- 配对结果、批次 item、`.dinsar_pair.json` 保留 Sentinel-1 扩展元数据。 -- 分发 `.SAFE` + `.EOF`。 -- 前端配对弹窗默认卫星选择调整。 - -预估工作量:1-2 天。 - -风险:如果数据库新增字段后没有正确标 dirty,需要强制重建 pairing cache。 - -### 阶段 3:生产预检保护 - -工作内容: - -- 识别 task source family。 -- Sentinel-1 批次提交到 LT1 专用 engine/profile 时阻止。 -- 在生产面板显示“当前仅支持管理和分发,生产适配待完成”。 - -预估工作量:0.5-1 天。 - -风险:低。 - -### 阶段 4:Sentinel-1 生产适配 - -工作内容: - -- 选择 SARscape、ISCE2 TOPS、GMTSAR 或其他处理链。 -- 设计 Sentinel-1 专用 engine/profile。 -- 做 TOPS 配准、轨道、DEM、burst overlap、输出发布包和 catalog 适配。 - -预估工作量:按引擎不同约 1-3 周,不建议和第一阶段混在一起。 - -## 12. 推荐最小方案 - -如果目标是“先把系统能管理哨兵数据”,推荐最小闭环如下: - -1. 只支持已解包 `.SAFE` 目录入库。 -2. 只解析 SLC 产品。 -3. 只新增 `relative_orbit`、`acquisition_start_time_utc`、`acquisition_stop_time_utc`、`metadata_json` 四类关键字段。 -4. `.EOF` 精轨只做匹配和路径保存,不做转换。 -5. 配对只增加同 relative orbit 约束,不做真实空间基线。 -6. 分发复制完整 SAFE 和 EOF。 -7. 生产页阻止 Sentinel-1 进入 LT1 专用 profile。 - -这个方案对现有 LT1 链路侵入最小,能让 Sentinel-1 数据先进入“可检索、可配对、可分发、可追踪”的状态。 - -## 13. 验收标准 - -最小验收: - -- 扫描一个包含 S1A/S1B `.SAFE` 的目录后,`radar_data` 能看到 S1 数据。 -- 源数据检索可以按 Sentinel-1、IW、SLC、极化、升降轨、相对轨道过滤。 -- 每个 S1 scene 有 footprint,可在地图上显示。 -- 有匹配 `.EOF` 时 `has_orbit_data = true`,`orbit_file_path` 指向 EOF。 -- 配对结果不会跨 relative orbit。 -- 批次保存和数据分发能生成 `Task_*`,其中包含 master/slave SAFE 和可选 orbit EOF。 -- Sentinel-1 批次不会误提交到 LT1 专用生产 profile。 - -## 14. 需要用户确认的输入 - -开工前最好确认: - -1. Sentinel-1 数据池是 `.SAFE` 解包目录,还是 `.SAFE.zip` 为主。 -2. 精轨 `.EOF` 是否已经本地保存,还是希望后续系统自动下载。 -3. 第一阶段是否只做管理/配对/分发,不接生产。 -4. 是否接受新增少量数据库字段和一份幂等迁移。 - diff --git a/docs/SENTINEL1_DINSAR_GAMMA_ISCE2_FEASIBILITY_20260510.md b/docs/SENTINEL1_DINSAR_GAMMA_ISCE2_FEASIBILITY_20260510.md deleted file mode 100644 index a7a5c00..0000000 --- a/docs/SENTINEL1_DINSAR_GAMMA_ISCE2_FEASIBILITY_20260510.md +++ /dev/null @@ -1,257 +0,0 @@ -# Sentinel-1 D-InSAR Gamma / ISCE2 可行性评估 - -日期:2026-05-10 - -本文只评估设计,不涉及代码修改。重点回答:在不使用 ENVI + SARscape 核心的前提下,现有 D-InSAR 生产链路是否适合接入 Sentinel-1,Gamma/PyINT 与 ISCE2 两条路线的实际工作量和风险分别是什么。 - -## 1. 结论 - -Gamma/PyINT 路线适合优先做 Sentinel-1 D-InSAR。 - -`D:\Code\PyINT` 中的专家代码对 Sentinel-1 支持比较完整,尤其是 Sentinel-1 TOPS/IW 的 ZIP 导入、精轨下载、burst 选择、TOPS burst 裁剪、ScanSAR/TOPS 配准、差分干涉、解缠、地理编码等流程。它不是只停留在文件名解析层面,而是已经围绕 GAMMA 命令组织了 Sentinel-1 的实际处理脚本。 - -ISCE2 本身也能处理 Sentinel-1 TOPS 数据,但本项目当前接入的 ISCE2 托管链路是 `lt1_stripmap`,核心是 LT-1 stripmap 的 `stripmapApp.py` 流程,不是 Sentinel-1 TOPS 的 `topsApp.py` 流程。因此 ISCE2 的 Sentinel-1 能力不能直接等价为“当前系统里马上可用”。如果要做,属于新增一个 `s1_tops` 生产 profile,工作量明显大于复用 PyINT/Gamma 的 Sentinel-1 脚本。 - -推荐顺序: - -1. 先做 `s1_gamma_dinsar` profile,复用 `D:\Code\PyINT` 的 Sentinel-1 Gamma 处理路线。 -2. 配对阶段保持轻量,只做同轨、同模式、同方向、同极化、覆盖重叠、时间基线等前置筛选。 -3. 真正的垂直基线、可处理性和失败原因交给 Gamma/PyINT 在 SLC/RSLC 生成后校验和回填。 -4. ISCE2 的 `s1_tops` profile 放到第二阶段,等 Gamma 路线跑通后再做。 - -## 2. 本项目当前 D-InSAR 引擎状态 - -### 2.1 PyINT/Gamma 当前是 LT-1 专用接入 - -本项目现有 PyINT 托管入口主要围绕 `lt1_gamma_dinsar`: - -- `.env` 中当前 PyINT/Gamma D-InSAR 配置说明面向 LT-1。 -- `backend/app/services/pyint_engine.py` 当前只接受 `lt1_gamma_dinsar`。 -- `backend/app/pyint_pipeline/run_lt1_pyint_pipeline.py` 是 LT-1 专用 runner。 -- `backend/app/services/pyint_input_assets_service.py` 负责 LT-1 归档、LT-1 精轨和输入资产准备。 - -这说明当前系统“接入 PyINT”不等于“已经接入 Sentinel-1 PyINT”。要支持 Sentinel-1,合理做法是新增一个 profile,而不是把 LT-1 profile 混改成多卫星逻辑。 - -### 2.2 ISCE2 当前是 LT-1 stripmap 接入 - -本项目现有 ISCE2 托管入口主要围绕 `lt1_stripmap`: - -- `.env` 中配置的是 `ISCE2_PROFILE=lt1_stripmap`。 -- 当前 pipeline 脚本是 `backend/app/isce2_pipeline/run_lt1_dinsar_pipeline.py`。 -- 当前使用的是 `stripmapApp.py`,不是 Sentinel-1 TOPS 常用的 `topsApp.py`。 -- WSL runtime registry 允许的 ISCE2 profile 目前也是 LT-1 stripmap 方向。 - -因此,ISCE2 对 Sentinel-1 的支持在算法生态上成立,但在本项目现有托管实现里还没有落地。 - -## 3. `D:\Code\PyINT` 对 Sentinel-1 的支持证据 - -`D:\Code\PyINT` 的 README 和用户指南明确把 Sentinel-1 作为支持对象。代码中也存在成套 Sentinel-1 处理脚本: - -- `pyint/down2slc_sen.py` - Sentinel-1 ZIP 下载数据转 SLC。它会调用 `eof` 获取精轨,并通过 GAMMA 相关脚本读取 TOPS SLC。 -- `pyint/down2slc_sen_all.py` - 批量发现 `DOWNLOAD/S1*.zip`,按日期调用 Sentinel-1 SLC 生成逻辑。 -- `pyint/slc_sen_cat.py` - 老版本 Sentinel-1 SLC 导入与拼接逻辑,说明 PyINT 对多 swath / 多 burst / 多文件拼接有历史支持。 -- `pyint/extract_s1_bursts.py` - 基于 master/slave 的 burst 参数,裁剪共同 burst 区间。 -- `pyint/coreg_s1_gamma.py` - 使用 GAMMA `ScanSAR_coreg.py` 做 Sentinel-1 TOPS/ScanSAR 配准。 -- `pyint/raw2ifg_s1.py` - Sentinel-1 单对 D-InSAR 的一站式流程:下载数据转 SLC、burst 提取、DEM、配准、差分干涉、解缠、地理编码。 -- `pyint/select_pairs.py` - 基于 GAMMA `base_calc` 计算基线并按 `max_tb`、`max_sb` 等约束选网。 - -这批脚本说明 PyINT 的 Sentinel-1 支持不是临时拼出来的文件名适配,而是围绕 GAMMA Sentinel-1 TOPS 处理能力组织出的完整流程。 - -## 4. Gamma/PyINT 接入方案 - -### 4.1 新增 profile - -建议新增独立 profile: - -```text -s1_gamma_dinsar -``` - -不要复用或扩展 `lt1_gamma_dinsar` 的内部假设。两者输入资产、轨道文件、SLC 生成方式、配准方式和模板字段都不同。 - -最小改造对象: - -- 新增 Sentinel-1 输入资产准备逻辑。 -- 新增 Sentinel-1 PyINT runner,例如 `run_s1_pyint_pipeline.py`。 -- 生产引擎 registry 增加 `s1_gamma_dinsar`。 -- 任务 preflight 增加 Sentinel-1 ZIP、EOF、GAMMA 命令、模板字段检查。 -- 输出 catalog 增加 Sentinel-1 结果归档映射。 - -### 4.2 输入数据约定 - -PyINT 现有 Sentinel-1 脚本默认从: - -```text -$SCRATCHDIR//DOWNLOAD/S1*.zip -``` - -发现数据。 - -因此最省事、风险最低的约定是:系统管理 Sentinel-1 时尽量保留原始 `.zip` 产品,并在生产分发时把 master/slave 的 ZIP 放入 PyINT 项目的 `DOWNLOAD` 目录。 - -如果数据池里只有解压后的 `.SAFE` 目录,当前 PyINT 脚本不能直接等价复用。可选方案有三个: - -1. 要求 Sentinel-1 生产任务必须绑定原始 ZIP。 -2. 在分发阶段把 `.SAFE` 目录重新打包成 ZIP。 -3. 改造 PyINT 的 Sentinel-1 导入脚本,让它直接接受 `.SAFE` 路径。 - -推荐第 1 种。第 2 种会增加磁盘和时间成本,第 3 种会扩大对专家代码的修改面。 - -### 4.3 单对 D-InSAR 最小流程 - -对于本系统的 D-InSAR 配对任务,建议先做“系统选出一对,PyINT 处理这一对”,而不是马上做完整时序网。 - -可控 runner 可以按以下顺序执行: - -1. 准备 `$SCRATCHDIR//DOWNLOAD`,放入 master/slave Sentinel-1 ZIP。 -2. 写入 `$TEMPLATEDIR/.template`,包含 masterDate、slaveDate、swath、burst、look、轨道、DEM、网络参数等。 -3. 调用 `down2slc_sen.py `。 -4. 调用 `down2slc_sen.py `。 -5. 调用 `extract_s1_bursts.py `。 -6. 调用 `generate_rdc_dem.py ` 或等价 DEM 生成步骤。 -7. 调用 `coreg_s1_gamma.py `。 -8. 调用 `diff_gamma.py `。 -9. 调用 `unwrap_gamma.py `。 -10. 调用 `geocode_gamma.py -`。 - -也可以参考 `raw2ifg_s1.py` 作为端到端样板,但不建议生产系统直接无控制地调用全流程脚本。托管系统应该显式控制每一步、日志、失败原因、输出发布和清理策略。 - -### 4.4 精轨处理 - -PyINT 的 Sentinel-1 脚本会使用 `eof` / OPOD 目录获取精轨。系统前面已经设计了 Sentinel-1 精轨管理和分发逻辑,因此两者可以分工: - -- 数据管理层负责发现、缓存、匹配和可选分发 EOF。 -- PyINT runner 负责把 EOF 目录暴露给 PyINT/GAMMA。 -- preflight 负责确认 master/slave 覆盖时段都有可用 EOF。 - -这样可以避免每次生产任务都临时联网下载精轨,也能保证任务可复现。 - -## 5. ISCE2 接入方案 - -ISCE2 Sentinel-1 TOPS 路线理论上可行,但本项目当前没有现成托管实现。 - -如果做 ISCE2,需要新增: - -- `s1_tops` profile。 -- `topsApp.py` 路径配置,例如 `ISCE2_TOPS_APP`。 -- Sentinel-1 SAFE/ZIP 输入准备逻辑。 -- EOF 轨道文件挂载逻辑。 -- `topsApp.py` XML 生成器。 -- topsStack 或 topsApp 的执行脚本。 -- Sentinel-1 TOPS 输出目录识别、catalog 发布和日志解析。 -- 与 LT-1 `stripmapApp.py` profile 隔离的健康检查。 - -这条路线的主要风险不是 ISCE2 不支持 Sentinel-1,而是当前系统的 ISCE2 封装抽象是为 LT-1 stripmap 写的。直接把 Sentinel-1 塞进现有 `lt1_stripmap` runner 会形成大量条件分支,后期维护会很差。 - -如果要做,建议完全独立成 `s1_tops`,不要污染 LT-1 runner。 - -## 6. 配对与基线策略 - -Sentinel-1 的配对建议分为两层。 - -第一层是系统内的轻量预筛选: - -- 同一 relative orbit。 -- 同一升降轨方向。 -- 同一 beam mode,例如 IW。 -- 极化兼容,例如 VV 对 VV、VH 对 VH。 -- 覆盖范围有足够重叠。 -- 时间基线在阈值内。 -- 产品级别满足生产要求,优先 SLC。 -- EOF 精轨可获得。 - -第二层是处理引擎内的精确校验: - -- SLC/TOPS 导入是否成功。 -- master/slave 是否有共同 burst。 -- DEM 覆盖是否足够。 -- GAMMA `base_calc` 计算出的垂直基线是否超过阈值。 -- TOPS 配准质量是否达标。 -- 干涉、解缠、地理编码是否生成有效产物。 - -这符合“配对是初级任务,不要太复杂”的目标。系统配对不应该冒充严密的 SAR 处理器;它只需要筛出大概率能跑的候选对。真正的物理基线和可处理性,由 GAMMA/ISCE2 在生成 SLC/RSLC 后确认。 - -注意命名上应避免再把 footprint 质心距离叫作 `spatial_baseline_meters`。对 Sentinel-1 更合理的字段区分是: - -- `centroid_distance_meters`:覆盖 footprint 质心距离,只是几何覆盖近似指标。 -- `temporal_baseline_days`:时间基线。 -- `perpendicular_baseline_meters`:由处理引擎计算或轨道模型计算出的垂直基线。 - -## 7. 工作量评估 - -### Gamma/PyINT 优先路线 - -前提:Sentinel-1 数据管理、ZIP 保留、EOF 匹配和分发机制已经具备。 - -预计工作量: - -- 最小可用单对 D-InSAR:约 4 到 7 个工作日。 -- 加上稳定的前端配置、健康检查、日志归档、失败原因归类和输出发布:约 1 到 2 周。 -- 扩展到时序网、自动 burst 推荐、多 pair 网络:另算,不建议第一阶段做。 - -主要风险: - -- 原始数据是否保留 ZIP。 -- WSL/GAMMA 环境中的 Sentinel-1 命令是否完整。 -- `eof` 工具和 OPOD 目录是否稳定。 -- PyINT 模板字段默认值是否适配当前项目。 -- 大量 ZIP、SLC、RSLC 中间产物带来的磁盘压力。 - -### ISCE2 Sentinel-1 路线 - -预计工作量: - -- 可用 demo:约 1 周。 -- 托管生产级接入:约 2 周或更长。 - -主要风险: - -- 当前 `isce2_engine.py` 和 WSL registry 偏 LT-1 stripmap。 -- 需要新增 topsApp/topsStack 方向的 XML、输入资产和产物识别。 -- 输出结构和错误日志与当前 LT-1 ISCE2 产物不一致。 -- 对系统健康检查和 profile registry 的影响更大。 - -## 8. 推荐实施路线 - -第一阶段只做 Gamma/PyINT Sentinel-1 单对 D-InSAR: - -1. 完成 Sentinel-1 数据管理文档中的数据入库、元数据、EOF 匹配和分发约定。 -2. 新增 `s1_gamma_dinsar` profile。 -3. 新增 Sentinel-1 PyINT runner,显式调用 `D:\Code\PyINT` 中已存在的 Sentinel-1 脚本链。 -4. 生产任务只接受 master/slave 两景 SLC ZIP。 -5. 配对表只保存轻量预筛选指标和处理后回填的真实 `perpendicular_baseline_meters`。 -6. 跑通后再考虑自动 burst 推荐、多 pair 网络和 ISCE2 `s1_tops`。 - -第二阶段再做 ISCE2 Sentinel-1: - -1. 新增 `s1_tops` profile。 -2. 使用 `topsApp.py` 或 topsStack,不复用 LT-1 stripmap runner。 -3. 与 Gamma 路线共享数据管理和 EOF 匹配能力。 -4. 输出 catalog 与生产日志保持同一前端体验。 - -## 9. 验收标准 - -Gamma/PyINT Sentinel-1 接入的第一阶段验收建议如下: - -- 同一对 Sentinel-1 ZIP 可以从任务分发目录进入 PyINT `DOWNLOAD`。 -- preflight 能明确报告 ZIP、EOF、DEM、GAMMA 命令和模板字段是否齐全。 -- master/slave 能成功生成 SLC。 -- 能识别共同 burst 并完成裁剪。 -- 能完成 TOPS 配准并生成 RSLC。 -- 能生成差分干涉图、解缠结果和地理编码结果。 -- 生产日志能展示每一步命令、耗时、退出码和失败原因。 -- 结果 catalog 能发布核心产物。 -- 配对记录能回填真实 `perpendicular_baseline_meters`,同时保留原始 `centroid_distance_meters`。 - -## 10. 总体判断 - -不使用 ENVI + SARscape 核心没有问题。对 Sentinel-1 D-InSAR 来说,本项目更应该优先利用 Gamma/PyINT 的专家代码路线。 - -Gamma/PyINT 是“已有专家脚本,需要做系统托管适配”;ISCE2 是“算法生态支持,但本项目需要新增 tops profile”。所以第一阶段选择 Gamma/PyINT 更稳、更快,也更符合“配对不要做得过度复杂,但生产尽可能有精度”的目标。 diff --git a/docs/SENTINEL1_SYSTEM_ENHANCEMENT_MASTER_PLAN_20260510.md b/docs/SENTINEL1_SYSTEM_ENHANCEMENT_MASTER_PLAN_20260510.md deleted file mode 100644 index bba0618..0000000 --- a/docs/SENTINEL1_SYSTEM_ENHANCEMENT_MASTER_PLAN_20260510.md +++ /dev/null @@ -1,347 +0,0 @@ -# Sentinel-1 系统增强总纲 - -日期:2026-05-10 - -本文作为 Sentinel-1 增强工作的主维护文档。后续如果继续推进 Sentinel-1 数据管理、D-InSAR 配对、Gamma/PyINT 生产和结果归档,优先维护本文;更细的调研材料保留为参考: - -- [SENTINEL1_DATA_MANAGEMENT_ADAPTATION_PLAN_20260510.md](SENTINEL1_DATA_MANAGEMENT_ADAPTATION_PLAN_20260510.md) -- [SENTINEL1_DINSAR_GAMMA_ISCE2_FEASIBILITY_20260510.md](SENTINEL1_DINSAR_GAMMA_ISCE2_FEASIBILITY_20260510.md) - -## 1. 总体目标 - -让当前系统能够管理 Sentinel-1 数据,并把 Sentinel-1 D-InSAR 生产纳入现有生产管理和结果管理体系。 - -推荐边界是: - -- 系统负责数据管理、元数据解析、精轨匹配、配对、任务分发、生产调度、日志、结果归档和前端展示。 -- Gamma + PyINT 负责 Sentinel-1 D-InSAR 的处理内核,包括 SLC 导入、burst 裁剪、TOPS 配准、差分干涉、解缠和地理编码。 -- ISCE2 Sentinel-1 TOPS 作为后续可选 profile,不作为第一阶段主线。 - -第一阶段不要求系统自己实现 SAR 核心算法,也不要求配对阶段计算真实垂直基线。 - -## 2. 总体架构 - -建议新增一条独立 Sentinel-1 Gamma 生产链: - -```text -Sentinel-1 数据池 - -> 数据扫描与元数据入库 - -> EOF 精轨管理与匹配 - -> D-InSAR 轻量配对 - -> 任务批次保存 - -> Task 目录分发 - -> s1_gamma_dinsar profile - -> PyINT + Gamma 处理 - -> 结果扫描与 catalog 发布 - -> 生产管理 / 结果管理前端展示 -``` - -关键原则: - -- Sentinel-1 不混进 LT-1 专用的 `lt1_gamma_dinsar` 或 `lt1_stripmap`。 -- 新增 `s1_gamma_dinsar` profile 作为第一阶段生产入口。 -- 真实 `perpendicular_baseline_meters` 由 Gamma/PyINT 在生成 SLC/RSLC 后计算或回填。 -- footprint 质心距离只能叫 `centroid_distance_meters`,不能再叫空间基线。 - -## 3. 数据管理 - -### 3.1 支持的数据形态 - -第一阶段建议以 Sentinel-1 SLC ZIP 为生产标准输入。 - -系统可以管理两类形态: - -- `.SAFE` 目录:适合浏览、入库、解析元数据。 -- `.zip` 原始产品:适合 PyINT/Gamma 生产分发。 - -如果数据池只有 `.SAFE` 目录,也可以入库和配对;但进入 PyINT/Gamma 生产时,建议要求关联原始 ZIP。原因是 `D:\Code\PyINT` 的 Sentinel-1 脚本默认从 `DOWNLOAD/S1*.zip` 发现和导入数据。 - -### 3.2 元数据解析 - -文件名可以提供粗信息: - -- `S1A` / `S1B` -- `IW` / `EW` / `SM` -- `SLC` -- 极化组合,例如 `DV`、`DH`、`SV`、`SH` -- 起止时间 -- absolute orbit -- datatake id / product id - -可靠配对还需要从 `manifest.safe` 或 annotation XML 中解析: - -- relative orbit -- 升降轨方向 -- footprint -- swath / burst 相关信息 -- 更完整的极化和产品结构 - -建议采用“关键列 + JSON 扩展”的模型。关键列服务检索、筛选和配对;JSON 保存 Sentinel-1 专有细节,避免一开始改出大量窄字段。 - -### 3.3 精轨管理 - -Sentinel-1 EOF 不走 LT-1 的精轨转换链路。 - -建议新增 Sentinel-1 EOF 管理逻辑: - -- 扫描 `.EOF` 文件。 -- 解析 mission、validity start、validity stop、generation time。 -- 按影像 acquisition time 匹配覆盖该时段的 EOF。 -- 允许把匹配到的 EOF 复制到 Task 目录。 -- PyINT runner 将 EOF 目录作为 `OPOD_DIR` 暴露给 PyINT/GAMMA。 - -这样可以避免生产时临时联网下载精轨,也便于任务复现。 - -## 4. D-InSAR 配对 - -Sentinel-1 配对适合嵌入现有 D-InSAR 配对体系,但应使用独立策略: - -```text -sentinel1_dinsar_pairing -``` - -第一阶段配对只做轻量预筛选: - -- 同一卫星族:`S1A` / `S1B` 可以互配,不按字面同卫星硬卡死。 -- 同一成像模式:优先只支持 `IW`。 -- 同一产品类型:优先只支持 `SLC`。 -- 同一升降轨方向。 -- 同一 relative orbit。 -- 极化兼容,例如 `VV` 对 `VV`。 -- footprint 重叠率达到阈值。 -- 时间基线不超过阈值。 -- master/slave 都能匹配 EOF。 - -不在配对阶段强制完成: - -- 真实垂直基线计算。 -- burst 级精确公共覆盖计算。 -- TOPS 配准质量判断。 -- 解缠可行性判断。 - -这些应交给 Gamma/PyINT 的处理链路完成。配对阶段只负责筛出大概率可跑的候选对。 - -## 5. 任务分发 - -Sentinel-1 D-InSAR 任务分发需要输出一个可复现的 Task 目录。 - -建议包含: - -```text -Task/ - manifest.json - pair.json - input/ - master/ - - slave/ - - orbit/ - - dem/ - - pyint/ - .template -``` - -分发开关建议: - -- 是否复制 EOF 到 Task 目录。 -- 是否输出 ZIP 压缩包。 -- 是否保留中间 SLC/RSLC 产物。 -- 是否只发布核心结果。 - -如果生产目标是 PyINT/Gamma,Task 到 PyINT scratch 目录之间可以由 runner 再做一次结构化投放,把 master/slave ZIP 放入: - -```text -$SCRATCHDIR//DOWNLOAD/ -``` - -## 6. Gamma/PyINT 生产 - -第一阶段新增 profile: - -```text -s1_gamma_dinsar -``` - -它应独立于当前 `lt1_gamma_dinsar`。 - -推荐 runner 行为: - -1. 读取 Task manifest 和 pair 信息。 -2. 创建 PyINT project 目录。 -3. 复制或链接 master/slave Sentinel-1 ZIP 到 `DOWNLOAD/`。 -4. 准备 EOF / OPOD 目录。 -5. 写入 PyINT template。 -6. 调用 Sentinel-1 PyINT 脚本链。 -7. 收集每一步日志和退出码。 -8. 扫描输出产物并发布到系统结果 catalog。 - -可复用的 `D:\Code\PyINT` Sentinel-1 脚本包括: - -- `pyint/down2slc_sen.py` -- `pyint/down2slc_sen_all.py` -- `pyint/extract_s1_bursts.py` -- `pyint/coreg_s1_gamma.py` -- `pyint/raw2ifg_s1.py` -- `pyint/select_pairs.py` - -生产系统不建议直接无控制地调用完整 app,而应显式控制步骤、日志、失败原因和产物发布。 - -## 7. 结果管理 - -PyINT/Gamma 生产结束后,系统负责结果归档。 - -第一阶段建议发布: - -- pair manifest -- PyINT template -- 全量日志 -- preflight 报告 -- SLC/RSLC 生成状态 -- 共同 burst 检查结果 -- interferogram -- coherence -- unwrapped phase -- geocoded result -- quicklook -- 真实 `perpendicular_baseline_meters` - -结果管理应复用现有生产结果 catalog,不新建一套孤立页面。前端仍然从生产管理和结果管理入口查看,只是引擎 profile 显示为 `s1_gamma_dinsar`。 - -## 8. 前端入口 - -前端需要在现有页面中增强,而不是新增一个割裂的 Sentinel-1 子系统。 - -建议改动范围: - -- 数据管理页面支持 Sentinel-1 过滤、详情和 EOF 匹配状态。 -- 配对页面新增 Sentinel-1 策略和参数。 -- 配对结果中显示 relative orbit、升降轨、极化、时间基线、重叠率和 EOF 状态。 -- 生产管理中支持 `s1_gamma_dinsar` profile。 -- 任务分发 UI 中增加 EOF 复制和 ZIP 导出开关。 -- 结果详情中显示 PyINT/Gamma 日志、核心产物和真实垂直基线。 - -第一阶段不需要让用户配置所有 PyINT template 字段。可以只暴露少量参数,其余使用系统默认值。 - -## 9. 数据库和自检 - -本系统有数据库自维护和系统自检机制,Sentinel-1 增强必须纳入这两部分。 - -需要评估的数据库变更: - -- Sentinel-1 关键元数据字段是否已有通用列可复用。 -- relative orbit、acquisition start/stop、source archive path、orbit match status 是否需要新增列。 -- Sentinel-1 专有元数据是否放入 JSON 扩展字段。 -- pairing cache 是否需要保存 `centroid_distance_meters` 与 `perpendicular_baseline_meters` 的区分。 -- 生产结果 catalog 是否需要新增 engine profile 或产品类型枚举。 - -自检需要增加: - -- Sentinel-1 数据根目录是否存在。 -- EOF 根目录是否存在。 -- Sentinel-1 解析器是否可用。 -- `s1_gamma_dinsar` profile 是否启用。 -- PyINT 路径是否存在。 -- GAMMA 环境是否可用。 -- `eof` 工具是否可用。 -- 关键 GAMMA/PyINT 命令是否可调用。 -- DEM 配置是否完整。 - -任何 schema 改动都必须同步数据库自维护逻辑,避免启动时 schema check 报错。 - -## 10. 分阶段实施 - -### 阶段 1:数据管理和配对 - -目标: - -- Sentinel-1 SAFE / ZIP 可扫描入库。 -- 基础元数据和 footprint 可解析。 -- EOF 可扫描、匹配和显示。 -- Sentinel-1 配对可生成候选对。 -- 配对不进入生产也能保存批次。 - -验收: - -- 同一 relative orbit 的 S1 数据能被正确筛出。 -- 不同方向、不同 relative orbit、不同模式的数据不会误配。 -- footprint 重叠率和时间基线显示正确。 -- EOF 状态清晰可见。 - -### 阶段 2:任务分发 - -目标: - -- Sentinel-1 pair 可分发为 Task 目录。 -- 可选复制 EOF。 -- 可选导出 ZIP。 -- manifest 可复现输入数据和参数。 - -验收: - -- Task 目录包含 master/slave、EOF、pair manifest、profile 参数。 -- ZIP 导出可直接交给生产或转移归档。 - -### 阶段 3:Gamma/PyINT 生产 - -目标: - -- 新增 `s1_gamma_dinsar` profile。 -- 托管调用 PyINT/Gamma 完成单对 Sentinel-1 D-InSAR。 -- 生产日志进入系统。 - -验收: - -- 至少一对 Sentinel-1 SLC ZIP 能跑通到 geocoded 结果。 -- 失败时能看到明确步骤和原因。 -- 真实垂直基线可以回填或记录。 - -### 阶段 4:结果管理 - -目标: - -- PyINT/Gamma 产物进入现有结果 catalog。 -- 前端可以查看日志、manifest、quicklook 和核心产物。 - -验收: - -- 用户不需要进入 PyINT 工作目录即可查看结果。 -- 结果与 pair、task、engine profile 可追踪。 - -### 阶段 5:ISCE2 Sentinel-1 可选增强 - -目标: - -- 新增 `s1_tops` profile。 -- 使用 ISCE2 `topsApp.py` 或 topsStack。 -- 与 Gamma 路线共享数据管理和 EOF 管理。 - -验收: - -- 不污染现有 LT-1 `lt1_stripmap` runner。 -- Sentinel-1 TOPS 生产有独立健康检查、日志和结果发布。 - -## 11. 主要风险 - -- 数据池只保留 `.SAFE`,没有原始 ZIP,会增加 PyINT/Gamma 接入复杂度。 -- EOF 匹配不稳定会导致生产不可复现。 -- Sentinel-1 annotation XML 解析不完整会影响 relative orbit、方向和 footprint。 -- 中间产物体积大,SLC/RSLC 保留策略要可配置。 -- PyINT 专家代码可用,但生产系统仍要做日志、失败分类和输出扫描。 -- ISCE2 Sentinel-1 虽然可行,但当前系统没有 tops profile,不能当作低成本改动。 - -## 12. 当前推荐决策 - -建议确认以下产品决策后再进入代码实现: - -1. Sentinel-1 生产第一阶段要求保留原始 SLC ZIP。 -2. 配对策略命名为 `sentinel1_dinsar_pairing`。 -3. 生产 profile 命名为 `s1_gamma_dinsar`。 -4. 第一阶段只支持 `IW + SLC + 同 relative orbit + 同方向 + 兼容极化`。 -5. `centroid_distance_meters` 与 `perpendicular_baseline_meters` 严格区分。 -6. 真实垂直基线由 Gamma/PyINT 处理后回填。 -7. ISCE2 Sentinel-1 TOPS 放到第二阶段。 - -这套边界可以把系统改造控制在数据管理、配对、分发、生产托管和结果管理范围内,把 SAR 处理精度交给 Gamma/PyINT,避免在配对阶段过度复杂化。 diff --git a/docs/TIMESERIES_LEGACY_DEPRECATION_20260521.md b/docs/TIMESERIES_LEGACY_DEPRECATION_20260521.md deleted file mode 100644 index 68a80b7..0000000 --- a/docs/TIMESERIES_LEGACY_DEPRECATION_20260521.md +++ /dev/null @@ -1,184 +0,0 @@ -# Legacy Time-Series InSAR Deprecation Record - -Date: 2026-05-21 - -## Background - -The project previously kept an ISCE2 + MintPy time-series/SBAS production path under: - -```text -experiments/isce2_sbas_timeseries -``` - -That experiment directory has been removed. The active SBAS-InSAR direction is now the independent Gamma production workflow documented in: - -```text -docs/SBAS_INSAR_PRODUCTION_PIPELINE_DESIGN_20260519.md -docs/GAMMA_IPTA_LT1_SBAS_TRIAL_RUNBOOK_20260518.md -``` - -Before this cleanup, startup validation still generated warnings for missing legacy paths: - -```text -TIMESERIES_EXPERIMENT_ROOT -TIMESERIES_STACK_PREP_SCRIPT -TIMESERIES_MATERIALIZE_SCRIPT -TIMESERIES_PREPARE_DEM_SCRIPT -TIMESERIES_STACK_RUNNER_SCRIPT -TIMESERIES_MINTPY_SBAS_SCRIPT -TIMESERIES_EXPORT_PUBLISH_SCRIPT -``` - -Those warnings were misleading because they referred to the abandoned ISCE2/MintPy line, not the current Gamma SBAS-InSAR production line. - -## Decision - -The ISCE2/MintPy time-series production chain is deprecated and disabled by default. - -The current SBAS-InSAR production authority is: - -```text -Frontend view: sbas_insar_production -Backend route: /api/sbas-insar-production -Engine: Gamma -Workflow: DIFF + IPTA SBAS -``` - -The legacy code is not physically deleted yet. It remains only as compatibility and historical-reference code until the Gamma SBAS workflow can be tested end to end and historical result access is confirmed. - -## Changes Made - -Backend configuration: - -- `backend/app/config.py` - - `TIMESERIES_ENABLED` default changed from `true` to `false`. - - Legacy `TIMESERIES_*` experiment/script defaults are now populated only when `TIMESERIES_ENABLED=true`. - - `ensure_dirs()` no longer creates `TIMESERIES_WORK_ROOT` unless the legacy chain is explicitly enabled. - - Runtime validation now reports an info line instead of warning about missing legacy experiment scripts when the chain is disabled. - -Environment example: - -- `.env.example` - - `TIMESERIES_ENABLED=false` - - `TIMESERIES_EXPERIMENT_ROOT=` is blank. - - `TIMESERIES_DEFAULT_PROCESSOR_CODE=legacy_isce2_stack_mintpy` - -Frontend production management: - -- `frontend/src/config/appConstants.js` - - Removed production workspace views: - - `timeseries_runs` - - `timeseries_products` - - Legacy route aliases now map to the Gamma SBAS page: - - `ps_production -> sbas_insar_production` - - `ps_products -> sbas_insar_production` - -- `frontend/src/ProductionWorkspace.jsx` - - Removed lazy imports and render branches for: - - `TimeseriesProductionPanel` - - production-management `PsinsarCatalogPanel` - - Updated production workspace text to describe Gamma SBAS as an independent entry. - -- `frontend/src/components/app/AppSidePanel.jsx` - - Updated production-management description to state that the old ISCE2/MintPy time-series entry is disabled. - -Documentation: - -- `docs/SBAS_INSAR_PRODUCTION_PIPELINE_DESIGN_20260519.md` - - Added the legacy-chain deprecation decision. - -- `docs/FRONTEND_NAVIGATION_ARCHITECTURE.md` - - Updated production-management internal views to: - - `dinsar_runs` - - `sbas_insar_production` - - `dinsar_products` - -## Current Behavior - -After backend restart, deployment validation should no longer warn about the removed `experiments/isce2_sbas_timeseries` path. - -Expected validation line: - -```text -[INFO] Legacy ISCE2/MintPy timeseries pipeline is disabled; current SBAS-InSAR production uses the Gamma /sbas-insar-production workflow. -``` - -The production-management page should show: - -```text -D-InSAR 运行 -SBAS-InSAR Production -D-InSAR 产物 -``` - -It should no longer expose: - -```text -时序InSAR 运行 -时序InSAR 产物 -``` - -## Verification - -Completed on 2026-05-21: - -- Frontend build passed: - -```text -npm run build -``` - -- Runtime configuration check passed: - -```text -scripts/check_runtime_config.py -``` - -Observed output included: - -```text -[INFO] Legacy ISCE2/MintPy timeseries pipeline is disabled; current SBAS-InSAR production uses the Gamma /sbas-insar-production workflow. -[OK] Deployment configuration check passed. -``` - -- Backend config syntax was checked with Python AST parsing. - -`python -m py_compile backend/app/config.py` was not used as the final check because Windows denied replacement of an existing `__pycache__` file. This was a local cache-permission issue, not a syntax failure. - -## Retained Compatibility Code - -The following code is intentionally retained for now: - -```text -backend/app/routers/timeseries_production.py -backend/app/services/timeseries_service.py -frontend/src/TimeseriesProductionPanel.jsx -frontend/src/api/timeseriesProduction.js -frontend/src/components/PsinsarCatalogPanel.jsx -``` - -Database tables and historical product catalog structures such as `ps_timeseries_runs` are also retained. - -`PsinsarCatalogPanel` may still be useful outside production management, especially for analysis/result browsing. Do not delete it until those usages are audited. - -## Re-Enabling Legacy Chain - -Re-enabling the legacy ISCE2/MintPy chain is not part of the current production plan. - -If it must be revived for a controlled comparison, the operator must explicitly set: - -```text -TIMESERIES_ENABLED=true -``` - -and provide valid values for all legacy experiment/script paths. The removed `experiments/isce2_sbas_timeseries` directory is no longer assumed to exist. - -## Follow-Up Cleanup Criteria - -Physical deletion of the legacy chain should wait until all of the following are true: - -- Gamma SBAS production has completed an end-to-end run from stack discovery to published LOS velocity/sigma products. -- Historical `ps_timeseries_runs` and old time-series product records have a clear migration or read-only archival plan. -- Frontend navigation, route aliases, and analysis pages have been audited for remaining dependencies. -- Backend callers of `/api/timeseries-production` have either been removed or explicitly marked as legacy-only. -- Test coverage or manual regression notes confirm that D-InSAR production, SBAS production, product browsing, and task monitoring still work. diff --git a/docs/TIMESERIES_SBAS_SARSCAPE_INTEGRATION_DESIGN_20260429.md b/docs/TIMESERIES_SBAS_SARSCAPE_INTEGRATION_DESIGN_20260429.md deleted file mode 100644 index 42043a6..0000000 --- a/docs/TIMESERIES_SBAS_SARSCAPE_INTEGRATION_DESIGN_20260429.md +++ /dev/null @@ -1,586 +0,0 @@ -# Time-Series SBAS And SARscape Integration Design - -## 1. Problem Statement - -The current time-series route can find and run a scene stack, but the system does not yet treat SBAS as a first-class production input. The main gaps are: - -- `find-ps-timeseries` returns scenes, not a durable SBAS network. -- `PsTaskBatch` is used as a production input even though it is a thin list of paths. -- Planning context is partly duplicated in `PsTaskItem.remark`. -- `copy-ps-stack` copies source folders, but does not create a stack-level production package. -- The managed time-series runner reconstructs input state at run time. -- SARscape is currently integrated only as a D-InSAR pair processor. - -The design goal is to make one immutable stack manifest the source of truth for every SBAS run, then let ISCE2/MintPy and SARscape consume the same contract. - -## 2. Target Workflow - -```text -AOI + filters - -> time-series stack search - -> SBAS network plan - -> user review and commit - -> immutable stack package - -> processor workflow - -> publish bundle - -> psinsar catalog -``` - -The stack plan and the production package are separate states. A plan is a previewable proposal; a package is a committed production input. - -## 3. Planning Contract - -### 3.1 Search API - -Add or evolve the current `find-ps-timeseries` route toward: - -```text -POST /timeseries/plans/search -``` - -Core request fields: - -- AOI source: uploaded shapefile, region geometry, or GeoJSON. -- Scene compatibility filters: satellite, orbit direction, imaging mode, polarization, date range. -- Scene thresholds: `initial_overlap_threshold`, `final_overlap_threshold`. -- Network thresholds: `time_baseline_min`, `time_baseline_max`, `spatial_baseline_max_meters`, later `perpendicular_baseline_max_meters`. -- Network policy: `strategy`, `num_connections`, `reference_image_id`. -- Processor hint: optional `processor_target`, for example `isce2_stack_mintpy` or `sarscape_sbas`. - -### 3.2 Plan Tables - -Existing: - -- `timeseries_stack_plans` -- `timeseries_stack_plan_items` - -New: - -- `timeseries_stack_plan_edges` - -The edge table stores the selected SBAS graph: - -- plan reference -- master/slave plan item references -- master/slave radar scene references -- optional `pairing_metric_cache` reference -- temporal baseline -- spatial/perpendicular baseline -- scene overlap ratio -- AOI pair overlap ratio -- selection reason and score -- enabled flag - -This lets the system answer: which pairs were selected, why were they selected, and what graph was actually submitted. - -## 4. Production Input Package - -Committed production input is represented by a prepared stack manifest. In the -current backend this file is: - -```text -backend/runtime/timeseries_work//input/selected_stack_manifest.json -``` - -This file is not the same thing as a `TimeseriesStackPlan`. The plan is the -candidate pool and audit graph. The prepared stack is the smaller frozen set -submitted to a processor. - -Schema: - -```json -{ - "schema": "insar.timeseries-stack/v1", - "prepared_stack_schema": "insar.prepared-sbas-stack/v1", - "manifest_role": "prepared_sbas_stack", - "mode": "sbas", - "plan_id": "tsp_...", - "prepared_stack_id": "pss_...", - "source_plan_id": "tsp_...", - "source_batch_id": "...", - "processor_code": "sarscape_sbas", - "aoi": {}, - "candidate_pool_source": {}, - "selection_params": {}, - "scenes": [], - "network_edges": [], - "reference_date": "YYYYMMDD", - "production_contract": { - "input_policy": "prepared_stack_only", - "catalog_scan_allowed_after_prepare": false, - "scene_selection_frozen": true - }, - "artifacts": { - "selected_network_edges_path_windows": "..." - }, - "prepared_stack_validation": {}, - "prepared_at_utc": "...", - "manifest_checksum": "..." -} -``` - -Rules: - -- A production run consumes the prepared manifest, not `PsTaskItem.remark` and - not a fresh scan of the full radar catalog. -- The manifest is immutable after `prepare` completes, except for explicit - retry/re-prepare workflows. -- Processor-specific materialization is recorded in a separate processor manifest. -- Source data copying must include the manifest and graph. - -### 4.1 Layered SBAS Input Model - -The production model is now four layers: - -1. Full radar inventory - - The long-lived scene catalog and pairing metric cache. - - It can be large and dirty/rebuilt over time. - -2. Candidate time-series pool - - `TimeseriesStackPlanORM`, plan items, and plan edges. - - This is the large pool selected by AOI, date, orbit, baseline, overlap, - and network policy. - - It records why each scene and edge was selected. - -3. Prepared SBAS stack - - `selected_stack_manifest.json` with - `prepared_stack_schema=insar.prepared-sbas-stack/v1`. - - Contains only the frozen scenes for this run. - - Writes `input/selected_network_edges.json` as a standalone artifact. - - Records validation results for scene files, graph count/date consistency, - DEM availability when required, and the no-catalog-scan production policy. - -4. Processor execution - - SARscape `wf_sbas` consumes the prepared scene stack. - - System `network_edges` are mandatory as the planning/audit graph, but the - native `wf_sbas` path may rebuild the executable graph internally. - - When SARscape's actual graph can be extracted, it should be saved as - `actual_network_edges.json` and compared with `selected_network_edges.json`. - -Backend enforcement: - -- `prepare_run()` creates the prepared stack contract and validates it. -- `build_sarscape_processor_preflight()` refuses non-prepared manifests. -- `run_sarscape_sbas()` refuses non-prepared manifests and missing - `selected_network_edges.json`. -- `execute_template_workflow()` in the SARscape service has a second guard so - lower-level execution cannot accidentally run from a candidate pool. - -## 5. Processor Boundary - -Introduce a time-series processor interface: - -```text -TimeseriesProcessor - check_available() - preflight(manifest) - build_workflow(run) - prepare_inputs(run) - execute_step(run, step_id) - export_publish_bundle(run) -``` - -Processor codes: - -- `isce2_stack_mintpy` -- `sarscape_sbas` - -The existing `timeseries_service` can remain the orchestration service, but processor-specific logic should move behind this interface. - -## 6. SARscape SBAS Processor - -SARscape SBAS should be a stack-level processor, not an extension of the D-InSAR pair engine. - -Suggested steps: - -1. `sarscape_preflight` - - Check ENVI, SARscape, taskengine, license, DEM, orbit pool, and output roots. - - Enumerate available SARscape SBAS/E-SBAS task names via `envipyengine`. - -2. `sarscape_import` - - Import LT-1 scenes. - - Write `sarscape_import_manifest.json`. - -3. `sarscape_connection_graph` - - Prefer the system-selected `network_edges`. - - If SARscape internally rebuilds the graph, export the actual graph as `actual_network_edges.json`. - -4. `sarscape_interferogram_generation` - -5. `sarscape_inversion` - - Generate time-series, velocity, coherence, and quality products. - -6. `sarscape_geocode_export` - -7. `export_publish_bundle` - -8. `register_psinsar_product` - -## 7. Result Contract - -One SBAS run registers one `psinsar` product bundle. - -Required bundle roles: - -- stack manifest -- processor manifest -- selected network edges -- actual network edges if processor modified them -- velocity product -- time-series product -- temporal coherence or equivalent quality product -- geocoded rasters -- quicklooks -- logs -- processor reports -- product manifest - -The catalog registers the publish manifest, not the transient work directory. - -## 8. Delivery Phases - -### Phase 1: Planning Boundary - -- Stop auto-creating PS batches after search. -- Persist `TimeseriesStackPlanEdge`. -- Return edges from `/timeseries-plans/{plan_id}`. -- Add network thresholds to `PsRequest` with backward-compatible defaults. - -### Phase 2: Manifest Boundary - -- Add committed stack package creation. -- Generate immutable `stack_manifest.json`. -- Make the existing ISCE2/MintPy route consume the manifest. - -### Phase 3: SARscape Discovery - -- Add a SARscape SBAS task verifier script. -- Capture task names and required parameters per installed SARscape version. -- Add `sarscape_sbas` preflight endpoint. - -Initial implementation points: - -- `scripts/verify_sarscape_sbas_tasks.py` -- `POST /idl/inspect/sarscape-sbas` -- `POST /timeseries-production/sarscape-sbas/preflight` -- `python -m backend.app.services.envi_runner_cli --inspect-sarscape-sbas` - -These entry points must stay read-only. They instantiate ENVI task definitions -and inspect parameters, but do not execute SBAS processing. - -The time-series SARscape preflight endpoint builds a processor manifest from -the committed PS batch/stack plan context. It reports the selected network -edges, the SARscape task sequence, required publish roles, and current blockers. -At this phase it must return `ready_for_pipeline_design=true` when ENVI/SARscape -is discoverable, but `ready_for_execution=false` until a checked-in parameter -template and job handler are implemented. - -Current implementation status: - -- `sarscape_sbas` is a selectable time-series processor. -- The production UI defaults to `ENVI/SARscape SBAS` with `Preflight only`. -- `POST /timeseries-production/runs` accepts `processor_code` and - `execution_mode`. -- SARscape runs use workflow `psinsar_sarscape_sbas_chain`. -- Preflight-only SARscape runs execute `prepare` plus - `sarscape_processor_preflight`, then complete the task without launching the - long SARscape stack execution. -- Full execution is gated by `SARSCAPE_SBAS_ALLOW_EXECUTION=true` and a - `validated=true` parameter template at - `SARSCAPE_SBAS_PARAMETER_TEMPLATE_PATH`. -- The checked-in template at - `backend/templates/sarscape_sbas_parameter_template.example.json` is a - placeholder contract and is intentionally not executable. - -Observed on the target workstation: - -- Lightweight `Engine.tasks()` discovery succeeds. -- Static `.task` extraction succeeds without starting taskengine. The extractor is: - - `scripts/extract_sarscape_sbas_task_templates.py` -- The installed SARscape exposes native workflow metatasks: - - `wf_sbas` - - `wf_esbas` -- `wf_sbas` is an ENVI metatask at - `C:\Program Files\Harris\ENVI56\user_custom_code\wf_sbas.task`. - It is not listed by `Engine.tasks()` on this workstation, but - `Engine("ENVI").task("wf_sbas")` can instantiate it successfully. Discovery - therefore combines `Engine.tasks()` with static `.task` file detection. - It contains an embedded 11-node DAG: - - `SARscape_setting_output_folders` - - `SARsLoadPreferences` - - `SARsImportSarSelector` - - `ENVIEXTRACTELEMENTSFROMARRAYTASK` - - `SARscapeSuggestLooks` - - `SARsInSARStackSBASGenerateConnectionGraph` - - `SARsInSARStackSBASInterferogramGeneration` - - `SARsInSARStackSBASInversionStep1` - - `SARsInSARStackSBASInversionStep2` - - `SARsInSARStackSBASGeocode` - - `SARscapeEnviuriToShape` -- The static `wf_sbas.task` file contains 18 parameter entries including the - embedded `DAG` default. Live taskengine `QueryTask` exposes 17 callable - parameters; it does not require the caller to pass `DAG`. -- The core production inputs are: - - `INPUT_FILE_LIST` - - `SARSCAPE_PREFERENCE` - - `DEM_SARSCAPEDATA` - - `OUTPUT_FOLDER` - - `GEOCODE_RG_GRID_SIZE` - - `ESTIMATE_RESIDUAL_HEIGHT` - - `DISPLACEMENT_MODEL_TYPE` - - `OUTPUT_ENVI_CARTOGRAPHIC_SYSTEM` -- `wf_sbas` returns SBAS product handles: - - `DISPLACEMENT_SARSCAPEDATA` - - `DEM_OUT_SARSCAPEDATA` - - `CORRECTION_H_SARSCAPEDATA` - - `COHERENCE_SARSCAPEDATA` - - `ALOS_SARSCAPEDATA` - - `ILOS_SARSCAPEDATA` - - `VELOCITY_SARSCAPEDATA` - - `OUTPUT_SHAPES` -- The installed SARscape also exposes these stack tasks: - - `SARsInSARStackSBASGenerateConnectionGraph` - - `SARsInSARStackSBASInterferogramGeneration` - - `SARsInSARStackSBASInversionStep1` - - `SARsInSARStackSBASInversionStep2` - - `SARsInSARStackSBASGeocode` - - `SARsInSARStackSBASVariogram` - - `SARsInSARStackESBASInterferogramGeneration` - - `SARsInSARStackESBASInversion` - - `SARsInSARStackESBASGeocode` - - `SARsInSARConnectionGraphESBAS` -- Reading `.parameters` for stack SBAS tasks can hang taskengine. Parameter - discovery must therefore be optional, subprocess-isolated, and timeout-bound. - Processor implementation should use a checked-in task template or SARscape - help/SML-derived parameter contract rather than relying on live parameter - introspection at run time. -- Timeout cleanup must remove only taskengine processes spawned by the timed-out - inspection subprocess. Existing user-launched ENVI/taskengine sessions should - not be killed by name. -- SARscape/taskengine can create zero-byte `env_*.xyz` and `IDL*.tmp` files in - the process current working directory. ENVI runner cwd and temp variables must - point at `backend/runtime/idl_worker/envi_cwd`, not the repository root. - Root-level `env_*.xyz` and `IDL*.tmp` are disposable taskengine leftovers. - -### Phase 3.5: SARscape Native Workflow Strategy - -The short-term production strategy is to integrate SARscape through `wf_sbas`. -This is the lowest-risk ENVI/SARscape path because SARscape already wires import, -connection graph generation, interferogram generation, inversion, geocoding, and -shape export in one metatask DAG. - -The backend template contract now supports two execution strategies: - -- `native_workflow_metatask` - - Preferred first implementation. - - Executes `wf_sbas` once with the committed stack manifest converted into - `INPUT_FILE_LIST`, configured DEM, output folder, and basic SBAS options. - - Does not directly consume the system-selected `network_edges`. - - Requires post-run extraction of SARscape's actual connection graph for audit. - -- `explicit_stack_tasks` - - Future controllable implementation. - - Executes `SARsInSARStackSBASGenerateConnectionGraph`, - `InterferogramGeneration`, `InversionStep1`, `InversionStep2`, and - `Geocode` as separate tasks. - - May allow tighter control of graph settings, but direct injection of the - system-selected edge list is not verified yet. - -Current rule: - -- `network_edges` remain mandatory in the stack manifest because they are the - system planning decision and task-dispatch audit record. -- When using `wf_sbas`, SARscape may rebuild the graph internally. The output - bundle must therefore contain both: - - `selected_network_edges.json` - - `actual_network_edges.json`, when it can be extracted from SARscape outputs - -Current code points: - -- `backend/app/services/envi_service.py` - - Discovers `wf_sbas`, `wf_esbas`, support tasks, and stack tasks. - - Cleans up only newly spawned `taskengine.exe` PIDs on timeout. - - Runs subprocess and in-process envipyengine calls from - `backend/runtime/idl_worker/envi_cwd` so taskengine temp files do not pollute - the project root. -- `backend/app/services/sarscape_sbas_service.py` - - Builds processor manifests with `execution_strategy`. - - Reports both native and explicit strategy availability. - - Requires `insar.prepared-sbas-stack/v1` before execution. - - Executes `native_workflow_metatask` only when the template is validated and - execution is explicitly enabled. -- `backend/app/services/timeseries_service.py` - - Treats `TimeseriesStackPlan` as the candidate pool. - - Creates `selected_stack_manifest.json` as the prepared stack in - `prepare_run()`. - - Writes `input/selected_network_edges.json` before SARscape preflight or - execution. - - Refuses SARscape preflight/execution when the prepared stack validation - fails. -- `backend/templates/sarscape_sbas_parameter_template.example.json` - - Records the `wf_sbas` parameter contract and DAG summary. - - Keeps `validated=false` until a controlled run validates parameters and - output capture. -- `scripts/extract_sarscape_sbas_task_templates.py` - - Regenerates the static parameter report from installed `.task` files. - -Open engineering items: - -- Confirm `wf_sbas.INPUT_FILE_LIST` accepts the same LT-1 `*.meta.xml` list used - by current SARscape import tasks. -- Confirm whether `DAG` must be passed explicitly or SARscape uses the embedded - default from `wf_sbas.task`. -- Locate SARscape's written connection graph or auxiliary processing file and - convert it into `actual_network_edges.json`. -- Map `VELOCITY_SARSCAPEDATA`, `DISPLACEMENT_SARSCAPEDATA`, - `COHERENCE_SARSCAPEDATA`, and `OUTPUT_SHAPES` into the unified `psinsar` - publish bundle. -- Decide later whether to invest in `explicit_stack_tasks` for strict graph - injection, depending on whether SARscape exposes a supported graph import or - connection-list parameter. - -Smoke test on 2026-04-30: - -- Applied the non-destructive `008_timeseries_stack_plan_edges.sql` migration. -- Backfilled two edges for test plan `tsp_d89bfc5bded744e6bf9b60c1` from - `pairing_metric_cache` because the plan was created before the edge table - existed. -- Ran SARscape SBAS preflight for batch - `e240a63a-5941-4a86-8aae-182a6bc95dae`. -- Result: - - `scene_count=3` - - `network_edge_count=2` - - `ready_for_pipeline_design=true` - - `ready_for_execution=false` - - `execution_strategy=native_workflow_metatask` - - `missing_required_tasks=[]` - - blockers are only `Template is not marked validated=true` and - `SARSCAPE_SBAS_ALLOW_EXECUTION is false`. -- Created a `preflight_only` run - `b7c2df45-a891-4ff7-b106-013e8d285fbd` and executed its `prepare` plus - `sarscape_processor_preflight` steps. This wrote - `selected_stack_manifest.json` and `sarscape_sbas_processor_manifest.json` - without launching the full SARscape SBAS pipeline. -- Dispatch verification for workflow - `eeaf1d82-7268-490c-9fb5-911a00a475c6` exposed a real workflow bug: - `workflow_service.mark_step_completed()` advanced downstream steps to - `READY`, but the database session has `autoflush=False`, so the immediate - `enqueue_ready_steps()` query did not see the new `READY` status. - `sarscape_processor_preflight` therefore stayed `READY` without a job. -- Fixed the dispatcher by flushing after `_advance_ready_steps()` and before - `enqueue_ready_steps()`. -- Verified the dispatcher fix in a rollback-only two-step workflow regression - check: completing step `a` immediately advanced step `b` to `RUNNING` and - created its queued job. -- Re-ran the controlled dispatch path for only this workflow: - - `TIMESERIES_PREPARE`: `COMPLETED` - - `TIMESERIES_SARSCAPE_PREFLIGHT`: `COMPLETED` - - workflow status: `COMPLETED` - - task status: `COMPLETED`, progress `100` - - run status: `PREPARED` - - no `TIMESERIES_RUN_SARSCAPE_SBAS` job or `run_sarscape_sbas` step was - created because execution mode was `preflight_only`. -- Root-level taskengine leftovers after the run: - - `env_*.xyz`: `0` - - `IDL*.tmp`: `0` - ENVI status now reports runner cwd as - `backend/runtime/idl_worker/envi_cwd`. - -Parameter template validation on 2026-04-30: - -- Initial live `Engine("ENVI").task("wf_sbas")` parameter inspection failed - with `ENVITASK: No task matches: wf_sbas`, even though the static - `wf_sbas.task` file was present. -- Root cause: SARscape installed `wf_sbas.task` under - `C:\Program Files\Harris\ENVI56\user_custom_code`, while taskengine only - auto-loads deployed custom tasks from `ENVI_CUSTOM_CODE`, the ENVI - `custom_code` directory, the application user directory, or IDL packages. -- Backend runner now sets `ENVI_CUSTOM_CODE` to the discovered SARscape - `user_custom_code` directory. This is process-local to the runner and does - not modify the machine-level environment. -- After the fix, live `wf_sbas` parameter inspection succeeds: - - `available=true` - - `parameter_count=17` - - required inputs: `INPUT_FILE_LIST` - - outputs: `OUTPUT_SHAPES`, `DISPLACEMENT_SARSCAPEDATA`, - `DEM_OUT_SARSCAPEDATA`, `CORRECTION_H_SARSCAPEDATA`, - `COHERENCE_SARSCAPEDATA`, `ALOS_SARSCAPEDATA`, - `ILOS_SARSCAPEDATA`, `VELOCITY_SARSCAPEDATA` -- Added repeatable validation script: - `scripts/validate_sarscape_sbas_template.py`. -- Validation report: - `backend/runtime/sarscape_sbas_template_validation_latest.json`. -- Current 3-scene validation result: - - `ok=true` - - validation scope: template contract only, no `task.execute()` - - manifest scene count: `3` - - network edge count: `2` - - `INPUT_FILE_LIST_count=3` - - scene `meta_path`, `tiff_path`, and folders all exist - - DEM base, `.sml`, and `.hdr` all exist - - remaining execution gate issue: checked-in template is still - `validated=false` - -Prepared stack boundary implementation on 2026-04-30: - -- Added `prepared_stack_schema=insar.prepared-sbas-stack/v1` to - `selected_stack_manifest.json`. -- Added `prepared_stack_id`, `source_plan_id`, `source_batch_id`, - `candidate_pool_source`, and `production_contract`. -- Added `input/selected_network_edges.json` as the frozen planning/audit graph - artifact. -- Added prepared stack validation for: - - scene count and dates - - required scene folder, TIFF, and metadata XML paths - - zero-size source files - - network edge count and edge date consistency - - SARscape DEM dependency when SARscape is the selected processor - - missing `selected_network_edges.json` -- SARscape processor preflight and execution now reject manifests that are not - prepared stacks. The lower-level SARscape executor repeats this guard before - calling any ENVI task. - -Prepared stack UI/API update on 2026-04-30: - -- Added read-only backend summary endpoint: - `GET /timeseries-production/runs/{run_id}/prepared-stack`. -- The endpoint reads only existing run artifacts and does not trigger catalog - scans, preflight, or SARscape execution. -- The summary reports: - - prepared stack state - - `prepared_stack_id` - - manifest and selected network edge artifact paths - - scene count and network edge count - - prepared stack validation result - - SARscape processor manifest readiness and blockers -- `TimeseriesProductionPanel` now shows a dedicated `Prepared SBAS Stack` - section in run details. -- The SARscape preflight card now states that batch preflight is against the - candidate pool, while production freezes a prepared stack before processor - execution. -- `usePairingLogic` now marks created PS batches as candidate time-series pools - in the planning context and logs that production will freeze a prepared SBAS - stack during `prepare`. - -### Phase 4: SARscape Execution - -- Implement the SARscape SBAS processor steps. -- Serialize taskengine execution through the existing ENVI lock. -- Persist step manifests and logs. - -Initial execution skeleton is in place: - -- `TIMESERIES_SARSCAPE_PREFLIGHT` -- `TIMESERIES_RUN_SARSCAPE_SBAS` -- `backend/app/services/sarscape_sbas_service.py` - -The execution handler resolves template macros and calls `execute_envi_task` -only after the template is readable, structurally valid, marked -`validated=true`, required tasks are discoverable, and execution is explicitly -enabled. - -### Phase 5: Unified Result Management - -- Normalize ISCE2/MintPy and SARscape outputs into the same publish bundle roles. -- Keep processor-specific files as secondary assets. -- Show products by role in the UI, not by processor-specific filenames. diff --git a/docs/archive/AGENTS.md b/docs/archive/AGENTS.md deleted file mode 100644 index 071803e..0000000 --- a/docs/archive/AGENTS.md +++ /dev/null @@ -1,49 +0,0 @@ -# Repository Guidelines - -## Agent Behavior -- Before modifying any code or files (including config/scripts), explicitly ask the user for permission. -- Do not change the database initialization/check/generation flow used by `start_system.bat` and `scripts/start_app.ps1`; any database-related changes must preserve that startup logic. -- Keep documentation (especially `docs/DEPLOYMENT.md`) updated when introducing new changes or features. -- Any newly written or updated Chinese text in code/config/docs must be saved with UTF-8 encoding (no mojibake/garbled characters). -- For map overlay architecture decisions, prioritize long-term runtime stability and smoothness over short-term implementation speed. -- For source radar preview overlay, prefer backend pre-corrected/georeferenced cache generation (Scheme B) and keep frontend rendering lightweight; avoid heavy per-frame frontend geometric transforms as the primary approach. - -## Project Structure & Module Organization -- `backend/`: FastAPI app and services. Core entrypoint is `backend/app/main.py`, router in `backend/app/api.py`, and domain logic in `backend/app/services/`. -- `backend/migrations/`: SQL migrations (including spatial functions used at startup). -- `backend/Point/` and `backend/colormaps/`: spatial inputs and colormap assets used by the backend. -- `frontend/`: React + Vite app. Source lives in `frontend/src/`, public assets in `frontend/public/`, and production build output in `frontend/dist/`. -- `image_cache/`: generated thumbnails. Filenames use `ID_{id}_{name}.webp`. -- `scripts/`, `start_system.bat`, `run_backend.py`: local helper scripts. - -## Build, Test, and Development Commands -- Python runtime: use Conda env `InSAR` only. Prefer `D:\anaconda3\Scripts\conda.exe run -n InSAR `; do not call system `python`. -- Backend dev server: `cd backend && D:\anaconda3\Scripts\conda.exe run -n InSAR python -m uvicorn app.main:app --reload` (runs on port 8000). -- Alternate backend start: `D:\anaconda3\Scripts\conda.exe run -n InSAR python run_backend.py` (wrapper used by docs/scripts). -- Frontend dev server: `cd frontend && npm install && npm run dev` (runs on port 5173). -- Frontend build: `cd frontend && npm run build`. -- Frontend lint: `cd frontend && npm run lint`. - -## Coding Style & Naming Conventions -- Python: follow PEP 8 (4-space indentation). Keep service logic in `backend/app/services/` and API routes in `backend/app/api.py`. -- Frontend: use 2-space indentation in `.jsx` and `.css` to match existing files; React components use `PascalCase` filenames (e.g., `StatisticsDashboard.jsx`). -- Image cache naming: `ID_{id}_{name}.webp` is required for cache lookups. -- Linting: ESLint is configured in `frontend/eslint.config.js` (run via `npm run lint`). - -## Testing Guidelines -- No dedicated test framework or test directory is present in this workspace. If you add tests, document the runner and add a `test` script in `frontend/package.json` or a backend test command. -- Prefer naming tests `test_*.py` (pytest-style) or `*.test.jsx` if a JS test runner is introduced. - -## Commit & Pull Request Guidelines -- Git metadata is not available in this workspace, so commit message conventions cannot be inferred. Use your team standard (e.g., Conventional Commits) and keep messages concise. -- PRs should include: a short description, linked issue/ticket if applicable, screenshots for UI changes, and clear testing notes. - -## Configuration & Environment Notes -- Database is PostgreSQL with PostGIS. Set `DATABASE_URL` (format: `postgresql+asyncpg://user:pass@host/dbname`). -- IDL automation requires a local IDL/ENVI install (Windows) and is managed in `backend/app/idl_service.py`. -- Frontend expects backend at `http://localhost:8000`; update CORS in the backend if ports change. -- Conda environment for this workspace: `InSAR` (Python 3.10). On this machine, Conda executable is `D:\anaconda3\Scripts\conda.exe`. -- For `scripts/start_app.ps1`, prefer Conda-native startup config in `.env`: - - `CONDA_EXE=D:\anaconda3\Scripts\conda.exe` - - `CONDA_ENV_NAME=InSAR` - - If `CONDA_ENV_NAME` is set, startup resolves the target env `python.exe` via Conda, then uses that interpreter for DB check/init/backend/worker. diff --git a/docs/archive/AI_ANALYSIS_REDESIGN.md b/docs/archive/AI_ANALYSIS_REDESIGN.md deleted file mode 100644 index fd92489..0000000 --- a/docs/archive/AI_ANALYSIS_REDESIGN.md +++ /dev/null @@ -1,769 +0,0 @@ -# AI 分析模块重构设计文档 - -**版本**: v2.0 -**日期**: 2026-03-02 -**作者**: System Architect - ---- - -## 1. 背景与目标 - -### 1.1 当前问题 - -- **架构混乱**: 同步/异步接口混用,用户体验不一致 -- **Prompt 硬编码**: 无法灵活调整诊断策略 -- **模型选择僵化**: 用户无法指定模型 -- **结果存储不规范**: 诊断报告存在 `task.message`,无法检索和分析 -- **缺少历史管理**: 无法查看、对比、导出历史诊断 - -### 1.2 设计目标 - -1. **统一异步架构**: 所有 AI 任务进队列,前端轮询状态 -2. **配置化 Prompt**: 模板化管理,支持自定义 -3. **灵活模型选择**: 用户可选模型,记住偏好 -4. **规范化存储**: 独立表管理诊断记录 -5. **完整历史管理**: 查询、筛选、导出、对比 - ---- - -## 2. 数据库设计 - -### 2.1 核心表:`ai_diagnosis` - -存储所有 AI 诊断记录。 - -```sql -CREATE TABLE ai_diagnosis ( - id SERIAL PRIMARY KEY, - - -- 关联信息 - result_id INTEGER NOT NULL REFERENCES dinsar_results(id) ON DELETE CASCADE, - task_id VARCHAR(50), -- 关联 system_tasks,用于追踪任务状态 - - -- 模型与配置 - model_name VARCHAR(100) NOT NULL, -- 如 'qwen3-vl:8b' - prompt_template VARCHAR(50) NOT NULL, -- 'quick'/'standard'/'detailed'/'custom' - prompt_text TEXT, -- 实际使用的完整 prompt(用于审计) - - -- 诊断结果 - diagnosis_markdown TEXT NOT NULL, -- Markdown 格式的诊断报告 - risk_level VARCHAR(20), -- 'LOW'/'MEDIUM'/'HIGH'/NULL - confidence_score FLOAT, -- 0.0-1.0,模型自评置信度(可选) - - -- 上下文信息(快照,避免关联查询) - result_name VARCHAR(255), - date_range VARCHAR(100), -- 如 '20240101-20240115' - quality_score FLOAT, -- 当时的 ai_score - hazards_found INTEGER DEFAULT 0, -- 覆盖范围内的隐患点数量 - hazards_snapshot JSONB, -- 隐患点详情快照 [{name, type, location}] - - -- 元数据 - created_at TIMESTAMP DEFAULT NOW(), - duration_seconds FLOAT, -- 诊断耗时 - error_message TEXT, -- 如果失败,记录错误 - - -- 索引 - INDEX idx_result_id (result_id), - INDEX idx_created_at (created_at DESC), - INDEX idx_risk_level (risk_level) -); -``` - -**设计要点**: -- `prompt_text` 存储实际 prompt,便于审计和复现 -- `hazards_snapshot` 用 JSONB 存快照,避免隐患点被删除后无法回溯 -- `duration_seconds` 用于性能分析 -- `error_message` 支持失败记录(不删除,便于调试) - -### 2.2 配置表:`ai_prompt_templates`(可选) - -如果需要 UI 管理 Prompt 模板,可建此表。否则用配置文件即可。 - -```sql -CREATE TABLE ai_prompt_templates ( - id SERIAL PRIMARY KEY, - name VARCHAR(50) UNIQUE NOT NULL, -- 'quick'/'standard'/'detailed' - display_name_zh VARCHAR(100), - display_name_en VARCHAR(100), - template_text TEXT NOT NULL, - is_system BOOLEAN DEFAULT FALSE, -- 系统内置模板不可删除 - created_at TIMESTAMP DEFAULT NOW(), - updated_at TIMESTAMP DEFAULT NOW() -); -``` - -**初始数据**: -```sql -INSERT INTO ai_prompt_templates (name, display_name_zh, display_name_en, template_text, is_system) VALUES -('quick', '快速诊断', 'Quick Diagnosis', '你是 InSAR 专家,用 200 字简述...', TRUE), -('standard', '标准诊断', 'Standard Diagnosis', '你是资深专家,按 4 步分析...', TRUE), -('detailed', '详细诊断', 'Detailed Diagnosis', '深度分析,包含地形、历史对比...', TRUE); -``` - -### 2.3 用户偏好表:`user_ai_preferences`(可选) - -存储用户的 AI 偏好设置。 - -```sql -CREATE TABLE user_ai_preferences ( - user_id INTEGER PRIMARY KEY REFERENCES users(id), - preferred_model VARCHAR(100), -- 如 'qwen3-vl:8b' - default_template VARCHAR(50), -- 'standard' - auto_diagnose BOOLEAN DEFAULT FALSE, -- 新结果自动诊断 - updated_at TIMESTAMP DEFAULT NOW() -); -``` - ---- - -## 3. 后端 API 设计 - -### 3.1 路由结构 - -新建 `backend/app/routers/ai_diagnosis.py`,遵循 RESTful 规范。 - -#### 3.1.1 诊断记录管理 - -**列表查询** -```http -GET /api/ai-diagnosis?page=1&limit=20&result_id=123&risk_level=HIGH&sort=-created_at -``` - -**Query 参数**: -- `page`, `limit`: 分页 -- `result_id`: 筛选特定结果的诊断 -- `risk_level`: 筛选风险等级(`LOW`/`MEDIUM`/`HIGH`) -- `model_name`: 筛选模型 -- `date_from`, `date_to`: 时间范围 -- `sort`: 排序字段(`-created_at` 表示倒序) - -**响应**: -```json -{ - "total": 156, - "page": 1, - "limit": 20, - "items": [ - { - "id": 42, - "result_id": 123, - "result_name": "Task_001_20240101_20240115", - "model_name": "qwen3-vl:8b", - "prompt_template": "standard", - "risk_level": "HIGH", - "hazards_found": 3, - "created_at": "2026-03-02T10:30:00Z", - "duration_seconds": 45.2 - } - ] -} -``` - -**详情查询** -```http -GET /api/ai-diagnosis/{id} -``` - -**响应**: -```json -{ - "id": 42, - "result_id": 123, - "result_name": "Task_001_20240101_20240115", - "model_name": "qwen3-vl:8b", - "prompt_template": "standard", - "prompt_text": "你是一位拥有 20 年经验...", - "diagnosis_markdown": "## 形态学分析\n\n观察到...", - "risk_level": "HIGH", - "confidence_score": 0.85, - "hazards_found": 3, - "hazards_snapshot": [ - {"name": "XX滑坡", "type": "滑坡", "location": "XX县"} - ], - "quality_score": 0.72, - "created_at": "2026-03-02T10:30:00Z", - "duration_seconds": 45.2 -} -``` - -**创建诊断任务** -```http -POST /api/ai-diagnosis -Content-Type: application/json - -{ - "result_id": 123, - "model_name": "qwen3-vl:8b", // 可选,不传则自动选择 - "prompt_template": "standard" // 'quick'/'standard'/'detailed'/'custom' - "custom_prompt": "..." // 仅当 template='custom' 时需要 -} -``` - -**响应**: -```json -{ - "diagnosis_id": 42, // 预创建的记录 ID(状态为 PENDING) - "task_id": "task_abc123", - "message": "诊断任务已进入队列" -} -``` - -**删除记录** -```http -DELETE /api/ai-diagnosis/{id} -``` - -**批量导出** -```http -POST /api/ai-diagnosis/export -Content-Type: application/json - -{ - "ids": [42, 43, 44], - "format": "markdown" // 'markdown'/'pdf'/'json' -} -``` - -**响应**:返回文件流或下载链接。 - -#### 3.1.2 模型管理 - -**获取可用模型** -```http -GET /api/ai/models -``` - -**响应**: -```json -{ - "ollama_online": true, - "models": [ - { - "name": "qwen3-vl:8b", - "size": "8.5 GB", - "modified_at": "2026-03-01T12:00:00Z", - "capabilities": ["vision", "text"], - "recommended": true - }, - { - "name": "qwen2-vl:72b", - "size": "72 GB", - "capabilities": ["vision", "text"] - } - ], - "default_model": "qwen3-vl:8b" -} -``` - -**预热模型** -```http -POST /api/ai/models/{model_name}/warmup -``` - -#### 3.1.3 Prompt 模板管理 - -**列表** -```http -GET /api/ai/prompt-templates -``` - -**响应**: -```json -{ - "templates": [ - { - "name": "quick", - "display_name": "快速诊断", - "is_system": true - }, - { - "name": "custom_001", - "display_name": "我的自定义模板", - "is_system": false - } - ] -} -``` - -**详情** -```http -GET /api/ai/prompt-templates/{name} -``` - -**创建/更新**(仅非系统模板) -```http -POST /api/ai/prompt-templates -PUT /api/ai/prompt-templates/{name} - -{ - "name": "custom_001", - "display_name": "我的模板", - "template_text": "..." -} -``` - ---- - -## 4. 任务处理流程 - -### 4.1 异步任务架构 - -**流程图**: -``` -用户点击"诊断" - ↓ -前端 POST /api/ai-diagnosis - ↓ -后端创建 diagnosis 记录(状态 PENDING)+ system_task + system_job - ↓ -返回 diagnosis_id + task_id - ↓ -前端轮询 GET /api/tasks/{task_id} - ↓ -job_worker 执行 _handle_ai_analyze - ↓ -调用 Ollama VLM - ↓ -更新 diagnosis 记录(状态 COMPLETED,写入 markdown) - ↓ -更新 system_task(状态 COMPLETED) - ↓ -前端检测到完成,跳转到诊断详情页 -``` - -### 4.2 Handler 改造 - -`backend/app/services/job_handlers.py` 中的 `_handle_ai_analyze` 改为: - -```python -async def _handle_ai_analyze(job: SystemJobORM) -> None: - payload = job.payload or {} - diagnosis_id = payload.get("diagnosis_id") - - if not diagnosis_id: - raise ValueError("AI_ANALYZE requires diagnosis_id") - - async with AsyncSessionLocal() as db: - # 1. 查询 diagnosis 记录 - diag = await db.get(AiDiagnosisORM, diagnosis_id) - if not diag: - raise ValueError(f"Diagnosis {diagnosis_id} not found") - - # 2. 查询关联的 result - result = await db.get(DinsarResultORM, diag.result_id) - - # 3. 查询隐患点(PostGIS) - hazards = await db.execute( - select(HazardPointORM).where(ST_Covers(result.geom, HazardPointORM.geom)) - ) - hazards = hazards.scalars().all() - - # 4. 准备图片 - img_path = data_service.get_dinsar_cache_path(result.id, result.name) - img_base64 = _load_image_as_base64(img_path) - - # 5. 构建 prompt - prompt = _build_prompt(diag.prompt_template, diag.prompt_text, result, hazards) - - # 6. 调用 VLM - start_time = time.time() - try: - analysis = await analyze_map_with_vlm( - images_base64=[img_base64], - prompt=prompt, - model_name=diag.model_name - ) - duration = time.time() - start_time - - # 7. 解析风险等级(从 Markdown 中提取) - risk_level = _extract_risk_level(analysis) - - # 8. 更新 diagnosis 记录 - diag.diagnosis_markdown = analysis - diag.risk_level = risk_level - diag.hazards_found = len(hazards) - diag.hazards_snapshot = [ - {"name": h.hazard_name, "type": h.hazard_type, "location": f"{h.city}{h.county}"} - for h in hazards - ] - diag.duration_seconds = duration - await db.commit() - - # 9. 更新 task 状态 - await task_service.update_task( - job.task_id, - status="COMPLETED", - message=f"诊断完成,风险等级: {risk_level}", - progress=100 - ) - except Exception as e: - diag.error_message = str(e) - await db.commit() - await task_service.update_task( - job.task_id, - status="FAILED", - message=f"诊断失败: {str(e)}" - ) -``` - ---- - -## 5. 前端设计 - -### 5.1 组件结构 - -``` -src/ -├── panels/ -│ └── AiAnalysisPanel.jsx # 新增:AI 分析主面板 -├── components/ -│ ├── AiDiagnosisTable.jsx # 诊断历史表格 -│ ├── AiDiagnosisDetailModal.jsx # 诊断详情 Modal -│ ├── AiModelSelector.jsx # 模型选择器 -│ ├── AiPromptTemplateEditor.jsx # Prompt 模板编辑器 -│ └── AiQuickDiagnoseCard.jsx # 快速诊断卡片 -├── api/ -│ └── aiDiagnosis.js # API 封装 -└── hooks/ - └── useAiDiagnosis.js # 自定义 Hook -``` - -### 5.2 AiAnalysisPanel 布局 - -```jsx -
- {/* 顶部状态卡片 */} -
- - -
- - {/* 快速诊断 */} - - - {/* 诊断历史 */} -
-
- - - -
- -
-
-``` - -### 5.3 核心交互流程 - -**快速诊断**: -1. 用户在下拉框选择 D-InSAR 结果 -2. 选择诊断模式(快速/标准/详细) -3. 点击"开始诊断" -4. 前端 POST `/api/ai-diagnosis`,获得 `task_id` -5. 显示进度条,轮询 `/api/tasks/{task_id}` -6. 完成后自动打开详情 Modal - -**查看历史**: -1. 表格显示所有诊断记录(分页) -2. 点击行打开详情 Modal -3. Modal 显示完整 Markdown 报告(支持复制、导出) - -**模型管理**: -1. 顶部卡片显示当前选中模型 -2. 点击"切换模型"打开下拉列表 -3. 选择后保存到 localStorage(或用户配置) - ---- - -## 6. 配置管理 - -### 6.1 Prompt 模板文件 - -`backend/app/ai_prompts.py`: - -```python -PROMPT_TEMPLATES = { - 'quick': { - 'name_zh': '快速诊断', - 'name_en': 'Quick Diagnosis', - 'template': """ -你是 InSAR 专家。请用 200 字简述以下形变图的关键发现: -- 是否存在明显形变区? -- 与已知隐患点是否吻合? -- 风险等级(低/中/高)? - -背景:{context} -""", - }, - 'standard': { - 'name_zh': '标准诊断', - 'name_en': 'Standard Diagnosis', - 'template': """ -你是拥有 20 年经验的 InSAR 专家。请按以下步骤分析: -1. 形态学分析 -2. 时空一致性 -3. 风险演化预测 -4. 综合风险评级 - -背景:{context} -隐患点:{hazards} -""", - }, - 'detailed': { - 'name_zh': '详细诊断', - 'name_en': 'Detailed Diagnosis', - 'template': """ -深度分析,包含: -- 地形地貌分析 -- 历史形变趋势 -- 潜在触发因素 -- 监测建议 - -背景:{context} -""", - }, -} - -def build_prompt(template_name: str, context: dict) -> str: - template = PROMPT_TEMPLATES.get(template_name, PROMPT_TEMPLATES['standard']) - return template['template'].format(**context) -``` - -### 6.2 环境变量 - -`.env` 新增: -```bash -# Ollama 配置 -OLLAMA_API_URL=http://127.0.0.1:11434/api/generate -OLLAMA_TIMEOUT=600 -OLLAMA_DEFAULT_MODEL=qwen3-vl:8b - -# AI 诊断配置 -AI_DIAGNOSIS_AUTO_RETRY=true -AI_DIAGNOSIS_MAX_RETRIES=2 -AI_DIAGNOSIS_STREAM_OUTPUT=false # 未来支持流式 -``` - ---- - -## 7. 成熟方案参考 - -### 7.1 Prompt 管理 - -参考 **LangChain PromptTemplate**: -- 支持变量插值 `{variable}` -- 支持条件渲染 `{% if condition %}` -- 支持模板继承 - -可引入 `Jinja2` 作为模板引擎: -```python -from jinja2 import Template - -template = Template(PROMPT_TEMPLATES['standard']['template']) -prompt = template.render(context=context, hazards=hazards) -``` - -### 7.2 任务队列 - -当前使用自研的 `job_queue_service`,可考虑迁移到: -- **Celery** + Redis:成熟的分布式任务队列 -- **Dramatiq**:轻量级替代方案 -- **ARQ**:基于 asyncio 的现代方案 - -### 7.3 诊断报告导出 - -- **Markdown → PDF**:使用 `weasyprint` 或 `pdfkit` -- **Markdown → Word**:使用 `python-docx` + `markdown` 解析 -- **模板化报告**:使用 `Jinja2` + HTML 模板 - -### 7.4 前端状态管理 - -如果 AI 功能复杂度增加,可考虑: -- **Zustand**(已使用):继续用 `useAiStore` -- **React Query**:管理服务端状态(诊断列表、模型列表) - ---- - -## 8. 迁移计划 - -### 8.1 数据迁移 - -**Step 1**:创建新表 -```bash -alembic revision -m "add_ai_diagnosis_table" -alembic upgrade head -``` - -**Step 2**:迁移历史数据(可选) -从 `system_tasks` 的 `message` 字段提取历史诊断,写入新表: -```python -# 迁移脚本 -async def migrate_old_diagnoses(): - tasks = await db.execute( - select(SystemTaskORM).where( - SystemTaskORM.task_type == 'AI_ANALYZE', - SystemTaskORM.status == 'COMPLETED' - ) - ) - for task in tasks.scalars(): - msg = json.loads(task.message) - diagnosis = AiDiagnosisORM( - result_id=msg['result_id'], - diagnosis_markdown=msg['analysis'], - created_at=task.completed_at, - ... - ) - db.add(diagnosis) - await db.commit() -``` - -### 8.2 API 兼容性 - -**向后兼容**: -- 保留旧接口 `/ai/analyze-result/{id}` 3 个月 -- 内部重定向到新接口 -- 响应格式保持一致 - -**废弃通知**: -```json -{ - "message": "诊断任务已进入队列", - "task_id": "...", - "diagnosis_id": 42, - "_deprecated": "此接口将在 2026-06-01 废弃,请使用 POST /api/ai-diagnosis" -} -``` - ---- - -## 9. 性能优化 - -### 9.1 缓存策略 - -- **模型列表缓存**:`GET /api/ai/models` 结果缓存 5 分钟(Redis) -- **Prompt 模板缓存**:启动时加载到内存 -- **诊断列表分页**:使用游标分页(`cursor-based`)而非 offset - -### 9.2 并发控制 - -- **Ollama 并发限制**:同时最多 2 个 VLM 任务(避免显存溢出) -- **任务优先级**:用户手动触发 > 自动诊断 - -### 9.3 超时与重试 - -```python -@retry( - stop=stop_after_attempt(3), - wait=wait_exponential(multiplier=1, min=4, max=60), - retry=retry_if_exception_type(httpx.TimeoutException) -) -async def call_ollama_with_retry(...): - ... -``` - ---- - -## 10. 安全考虑 - -### 10.1 输入验证 - -- **Prompt 注入防护**:限制自定义 prompt 长度(8000 字符) -- **模型名称白名单**:只允许已知模型 -- **结果 ID 权限检查**:确保用户有权访问该结果 - -### 10.2 输出过滤 - -- **敏感信息脱敏**:诊断报告中的坐标、地址模糊化 -- **XSS 防护**:Markdown 渲染时使用 `DOMPurify` - ---- - -## 11. 监控与日志 - -### 11.1 关键指标 - -- **诊断成功率**:`completed / (completed + failed)` -- **平均耗时**:按模型、模板统计 -- **模型使用分布**:哪个模型最受欢迎 -- **风险等级分布**:高/中/低风险占比 - -### 11.2 日志记录 - -```python -logger.info( - "AI diagnosis completed", - extra={ - "diagnosis_id": diag.id, - "result_id": diag.result_id, - "model": diag.model_name, - "duration": diag.duration_seconds, - "risk_level": diag.risk_level, - } -) -``` - ---- - -## 12. 未来扩展 - -### 12.1 多模态输入 - -- 支持上传多张图片(时序对比) -- 支持附加文本描述(用户观察) - -### 12.2 对比分析 - -- 同一结果的多次诊断对比 -- 不同模型的诊断结果对比 - -### 12.3 自动化工作流 - -- 新结果自动触发诊断 -- 高风险自动发送邮件/钉钉通知 - -### 12.4 Fine-tuning - -- 收集用户反馈(诊断是否准确) -- 定期 fine-tune 模型 - ---- - -## 13. 总结 - -### 13.1 核心改进 - -| 维度 | 当前 | 重构后 | -|------|------|--------| -| 架构 | 同步/异步混用 | 统一异步任务 | -| Prompt | 硬编码 | 模板化 + 可自定义 | -| 模型 | 自动选择 | 用户可选 + 偏好记忆 | -| 存储 | task.message | 独立表 + 完整字段 | -| 历史 | 无法查询 | 完整 CRUD + 导出 | - -### 13.2 实施优先级 - -**P0(必须)**: -- 创建 `ai_diagnosis` 表 -- 改造 `_handle_ai_analyze` -- 实现基础 CRUD API -- 创建 `AiAnalysisPanel` 前端面板 - -**P1(重要)**: -- Prompt 模板管理 -- 模型选择器 -- 诊断详情 Modal -- 导出功能 - -**P2(可选)**: -- 用户偏好保存 -- 对比分析 -- 自动化工作流 - ---- - -**文档结束** diff --git a/docs/archive/ANALYSIS_FRONTEND_LOCKING_2026-03-05.md b/docs/archive/ANALYSIS_FRONTEND_LOCKING_2026-03-05.md deleted file mode 100644 index 52648f7..0000000 --- a/docs/archive/ANALYSIS_FRONTEND_LOCKING_2026-03-05.md +++ /dev/null @@ -1,389 +0,0 @@ -# 前端锁定问题分析(2026-03-05) - -## 问题描述 - -用户报告在执行以下操作时前端没有锁定: -1. D-InSAR 结果扫描 -2. 数据分发(复制任务) - -## 根本原因分析 - -### 问题 1:扫描任务前端未锁定 - -**原因**:任务执行太快(< 3 秒),前端轮询间隔(3 秒)来不及捕获。 - -**证据**: -``` -[性能] Footprint 提取耗时: 1622.1ms (文件: Task_20250120_20250217_2_geo_disp) -``` -单个文件处理只需 1.6 秒,如果只有少量文件,整个扫描任务可能在 2-3 秒内完成。 - -**前端轮询逻辑**: -```javascript -// useGlobalTaskControl.js -useEffect(() => { - const interval = setInterval(() => { - syncActiveTasks(); // 每 3 秒轮询一次 - }, 3000); - return () => clearInterval(interval); -}, []); -``` - -**时间线**: -``` -T=0s: 用户点击"扫描"按钮 -T=0.1s: 后端创建 SystemTask (status=PENDING) -T=0.2s: job_worker 开始执行,更新为 RUNNING -T=2.0s: 扫描完成,更新为 COMPLETED -T=3.0s: 前端第一次轮询,任务已经完成 -``` - -**结论**:前端来不及捕获 RUNNING 状态,直接看到 COMPLETED。 - ---- - -### 问题 2:数据分发任务前端未锁定 - -**根本原因**:`DataCopierPanel` 没有调用全局锁定机制。 - -**证据**: - -1. **App.jsx 中的调用对比**: - -```javascript -// IDLAutomationPanel - 有 onJobQueued 回调 ✅ - handleTaskStart(taskId, '任务已入队,等待处理...')} -/> - -// DataMonitorPanel - 有 onTaskStart 回调 ✅ - - -// DataCopierPanel - 没有任何回调 ❌ - -``` - -2. **DataCopierPanel 内部逻辑**: - -```javascript -// DataCopierPanel.jsx:97-133 -const handleStartCopy = async () => { - // ... 验证逻辑 ... - - setIsUploading(true); - setLogs([]); - setStatus('RUNNING'); // 只更新组件内部状态 - - try { - const response = await axios.post(endpoint, { - batch_id: selectedBatchId, - dest_dir: destDir, - copy_statuses: copyStatuses, - }, { withCredentials: true }); - setTaskId(response.data.task_id); // 只保存 task_id - // ❌ 没有调用 onJobQueued 或 onTaskStart - } catch (error) { - // ... 错误处理 ... - } finally { - setIsUploading(false); - } -}; -``` - -3. **组件自己管理状态**: - -`DataCopierPanel` 使用自己的状态管理(`status`、`taskId`、`logs`),通过轮询 `/tools/copy-status/${taskId}` 获取进度,**完全独立于全局任务控制系统**。 - -```javascript -// DataCopierPanel.jsx:82-95 -const fetchLogs = async () => { - if (!taskId) return; - try { - const response = await axios.get(`${apiEndpoint}/tools/copy-status/${taskId}`, { withCredentials: true }); - setLogs(response.data.logs); - const nextStatus = normalizeStatus(response.data.status); - if (nextStatus && nextStatus !== 'UNKNOWN') { - setStatus(nextStatus); // 只更新组件内部状态 - } - } catch (error) { - console.error('Failed to load logs:', error); - } -}; -``` - -**结论**:`DataCopierPanel` 是一个独立的任务管理系统,不与全局锁定机制集成。 - ---- - -## 架构对比 - -### 全局锁定机制(IDLAutomationPanel、DataMonitorPanel) - -``` -用户操作 → 组件调用 API → 后端创建 SystemTask - ↓ - 组件调用 onJobQueued(taskId) - ↓ - App.jsx 调用 handleTaskStart(taskId) - ↓ - setPendingTaskIds([...prev, taskId]) - ↓ - useGlobalTaskControl 轮询 /tasks/active - ↓ - 检测到 RUNNING 任务 → setIsGlobalLocked(true) - ↓ - 任务完成 → setIsGlobalLocked(false) -``` - -### 独立任务管理(DataCopierPanel) - -``` -用户操作 → 组件调用 API → 后端创建 SystemTask - ↓ - 组件保存 taskId(内部状态) - ↓ - 组件轮询 /tools/copy-status/${taskId} - ↓ - 更新组件内部状态(status、logs) - ↓ - ❌ 不触发全局锁定 -``` - ---- - -## 影响评估 - -### 扫描任务未锁定 - -**影响等级**:🟢 低 - -**原因**: -- 扫描任务通常很快(< 3 秒) -- 扫描是只读操作,不会修改数据 -- 即使用户在扫描时操作,也不太可能造成冲突 - -**是否需要修复**:可选 -- 如果希望用户明确感知到扫描正在进行,可以修复 -- 如果接受"快速任务不锁定"的行为,可以不修复 - ---- - -### 数据分发任务未锁定 - -**影响等级**:🟡 中等 - -**原因**: -- 数据分发任务可能耗时较长(几分钟到几十分钟) -- 用户可能在分发过程中启动其他任务 -- 可能导致资源竞争(磁盘 I/O、网络带宽) - -**潜在问题**: -1. 用户可能同时启动多个分发任务,导致磁盘 I/O 饱和 -2. 用户可能在分发过程中启动 ENVI 工作流,导致系统卡顿 -3. 用户可能不知道分发任务正在后台运行 - -**是否需要修复**:建议修复 -- 数据分发是长时间运行的任务 -- 应该与全局锁定机制集成 -- 提升用户体验和系统稳定性 - ---- - -## 修复方案 - -### 方案 A:集成到全局锁定机制(推荐) - -**修改文件**: -1. `App.jsx` -2. `DataCopierPanel.jsx` - -**步骤**: - -1. **在 App.jsx 中传递回调**: - -```javascript -{leftPanelTab === 'copier' && ( -
- handleTaskStart(taskId, '数据分发任务已入队')} // 添加此行 - /> -
-)} -``` - -2. **在 DataCopierPanel.jsx 中接收并调用回调**: - -```javascript -// 修改组件签名 -const DataCopierPanel = ({ apiEndpoint, readOnly = false, onJobQueued }) => { - // ... 现有代码 ... - - const handleStartCopy = async () => { - // ... 验证逻辑 ... - - setIsUploading(true); - setLogs([]); - setStatus('RUNNING'); - - try { - const response = await axios.post(endpoint, { - batch_id: selectedBatchId, - dest_dir: destDir, - copy_statuses: copyStatuses, - }, { withCredentials: true }); - - const taskId = response.data.task_id; - setTaskId(taskId); - - // 调用全局锁定回调 - if (onJobQueued) { - onJobQueued(taskId); - } - } catch (error) { - // ... 错误处理 ... - } finally { - setIsUploading(false); - } - }; -}; -``` - -**优点**: -- 与现有架构一致 -- 最小改动 -- 复用全局锁定机制 - -**缺点**: -- 组件仍然保留自己的状态管理(有一定冗余) - ---- - -### 方案 B:完全重构为全局任务管理 - -**修改范围**:大 - -**步骤**: -1. 移除 `DataCopierPanel` 的内部状态管理 -2. 使用全局 `activeTasks` 和 `ActiveTasksOverlay` 显示进度 -3. 移除组件内的轮询逻辑 - -**优点**: -- 架构统一 -- 减少代码冗余 - -**缺点**: -- 改动较大 -- 可能影响现有功能 -- 需要充分测试 - ---- - -### 方案 C:保持现状,添加警告提示 - -**修改文件**:`DataCopierPanel.jsx` - -**步骤**: -在任务运行时显示警告提示: - -```javascript -{status === 'RUNNING' && ( -
- ⚠️ 数据分发任务正在后台运行,请勿关闭浏览器或启动其他耗时任务 -
-)} -``` - -**优点**: -- 改动最小 -- 不影响现有架构 - -**缺点**: -- 不解决根本问题 -- 用户仍可能启动冲突任务 - ---- - -## 扫描任务修复方案(可选) - -### 方案 A:前端立即锁定 - -在提交扫描任务后立即锁定,不等待轮询: - -```javascript -// DataManagementPanel.jsx -const handleScanDinsarResults = async () => { - try { - // 立即锁定前端 - if (onTaskStart) { - onTaskStart(null, 'SCAN_DINSAR', '正在扫描 D-InSAR 结果...'); - } - - const response = await scanDinsarResults(selectedDirs); - const taskId = response.data.task_id; - - // 更新任务 ID - if (onTaskStart) { - onTaskStart(taskId, 'SCAN_DINSAR', '正在扫描 D-InSAR 结果...'); - } - - addLog('info', `扫描任务已提交: ${taskId}`); - } catch (error) { - // 解锁前端 - setIsGlobalLocked(false); - addLog('error', `扫描失败: ${error.message}`); - } -}; -``` - -### 方案 B:后端添加最小执行时间 - -```python -# job_handlers.py -async def _handle_scan_dinsar(job: SystemJobORM) -> None: - start_time = time.time() - - # ... 执行扫描 ... - - # 确保任务至少运行 2 秒 - elapsed = time.time() - start_time - if elapsed < 2.0: - await asyncio.sleep(2.0 - elapsed) -``` - ---- - -## 推荐修复顺序 - -1. **优先修复数据分发任务**(方案 A) - - 影响较大 - - 修改简单 - - 风险低 - -2. **可选修复扫描任务**(方案 A) - - 影响较小 - - 提升用户体验 - - 风险低 - -3. **长期考虑重构**(方案 B) - - 统一架构 - - 减少冗余 - - 需要充分测试 - ---- - -## 总结 - -- **扫描任务未锁定**:任务太快,轮询来不及捕获(影响小,可选修复) -- **数据分发任务未锁定**:组件独立管理状态,未集成全局锁定(影响中,建议修复) -- **推荐方案**:为 `DataCopierPanel` 添加 `onJobQueued` 回调,集成到全局锁定机制 diff --git a/docs/archive/DINSAR_ENHANCEMENT_TASKS.md b/docs/archive/DINSAR_ENHANCEMENT_TASKS.md deleted file mode 100644 index 6b1a6ac..0000000 --- a/docs/archive/DINSAR_ENHANCEMENT_TASKS.md +++ /dev/null @@ -1,426 +0,0 @@ -# D-InSAR 增强 Task 文档 - -更新日期:2026-03-13 - -## 1. 文档目的 - -这份文档用于统一管理本轮 D-InSAR 系统重构任务,覆盖以下两大方向: - -- D-InSAR 多引擎生产系统增强 -- D-InSAR 结果管理系统增强 - -文档定位是“任务总表 + 实施边界 + 验收口径”,用于后续分阶段实施、联调和运维验收。 - -## 2. 背景 - -当前系统的 D-InSAR 生产能力主要绑定在 ENVI + SARscape 链路上,前端、后端任务模型、运行监控和结果入库都默认只有一个处理引擎。这会带来两个直接问题: - -- 客户如果没有 ENVI/SARscape license,D-InSAR 核心生产能力会直接失效。 -- 后续接入 ISCE2、LANDSAR 时,只能继续堆在现有 IDL/ENVI 语义上,导致架构持续恶化。 - -同时,当前 D-InSAR 结果管理能力偏轻,更多是“结果浏览 + 缓存管理”,还不足以支撑多引擎、多版本、多产物、多轮生产的正式管理需求。 - -## 3. 本轮目标 - -### 3.1 生产系统目标 - -- 将 D-InSAR 生产系统升级为“多引擎生产中心” -- 正式支持两类可执行引擎: - - `sarscape`:ENVI + SARscape - - `isce2`:WSL 内部署的 ISCE2 -- 预留第三类引擎: - - `landsar`:仅保留接口和前端占位,不实现算法 -- 将运维自检升级为“系统级 + D-InSAR 专项级”双层检查 - -### 3.2 结果管理目标 - -- 将 D-InSAR 结果从“单文件记录”升级为“可追踪、可治理、可比对”的结果资产 -- 支持来源追踪、版本管理、多产物管理、审核状态和运维治理 -- 让结果管理适配多引擎场景,而不是继续依赖单一文件命名和单一路径假设 - -## 4. 范围与非范围 - -### 4.1 本轮范围 - -- 多引擎 D-InSAR 后端抽象层 -- 新 D-InSAR 生产中心前端架构 -- ENVI/SARscape 的兼容接入 -- ISCE2 的 WSL 环境校验与运行接口 -- 运维自检与 D-InSAR 专项健康检查 -- D-InSAR 结果管理增强 - -### 4.2 明确不在本轮范围 - -- LANDSAR 算法实现 -- 对现有配对算法本身做大幅重写 -- 对 AI 诊断能力做独立重构 -- 一次性替换全部旧接口 - -## 5. 现状要点 - -### 5.1 ENVI 现有处理模式 - -当前 ENVI 侧应明确建模为 `sarscape` 引擎下的两种 profile: - -- `metatask` -- `custom6` - -其中 `custom6` 的 6 步链路为: - -1. Interferogram Generation -2. Filtering and Coherence -3. Orbital Trend Removal -4. Phase Unwrapping -5. GCP Generation + Refinement -6. Phase to Displacement + Geocoding - -### 5.2 ISCE2 的特殊约束 - -ISCE2 不走本机 Windows 直接执行链路,默认按 WSL 方式部署与运行,因此必须把 WSL 环境校验设计成正式能力,而不是上线前人工检查事项。 - -### 5.3 当前结果管理主要短板 - -- 结果记录字段偏少,缺少引擎来源、运行来源、版本、产物类型等核心字段 -- 结果入库逻辑仍偏向单文件扫描,不适合多引擎多版本 -- 前端结果面板偏重展示,缺少治理、比对、版本和来源追踪能力 -- 运维自检尚未覆盖结果资产层面的异常 - -## 6. Task 总览 - -| Task ID | 主题 | 目标 | -|---|---|---| -| T1 | 多引擎生产基座 | 建立统一的 D-InSAR 引擎抽象、调度和兼容层 | -| T2 | 前端生产中心 | 将现有 IDL 自动化面板升级为多引擎生产中心 | -| T3 | SARSCAPE 兼容接入 | 将现有 ENVI 链路适配到新抽象层 | -| T4 | ISCE2 + WSL 接入 | 建立 ISCE2 的 WSL 检查、运行和日志链路 | -| T5 | 运维自检增强 | 增加多引擎专项健康检查和自愈入口 | -| T6 | 结果管理增强 | 将 D-InSAR 结果升级为正式结果资产管理体系 | - -## 7. 任务拆分 - -### T1. 多引擎生产基座 - -#### 目标 - -- 将 D-InSAR 生产从 `IDL/ENVI 单引擎假设` 中抽离 -- 为 `sarscape`、`isce2`、`landsar` 建立统一抽象 - -#### 关键任务 - -- 定义统一引擎接口 -- 建立引擎注册表 -- 建立统一运行记录模型 -- 建立统一产物收集与入库接口 -- 保留旧 `/idl/...` 路由兼容转发能力 - -#### 目标结构 - -- `dinsar_engines/base.py` -- `dinsar_engines/registry.py` -- `dinsar_engines/sarscape_engine.py` -- `dinsar_engines/isce2_engine.py` -- `dinsar_engines/landsar_engine.py` -- `dinsar_orchestrator.py` - -#### 验收标准 - -- 新生产调度层可按 `engine_code` 分发 -- `sarscape`、`isce2`、`landsar` 均可在注册表中查询到 -- 旧 ENVI 生产功能不回归 - -### T2. 前端生产中心 - -#### 目标 - -- 用统一生产中心替代当前单引擎 `IDLAutomationPanel` -- 支持多引擎选择、运行监控、日志查看和结果入库操作 - -#### 前端信息架构 - -- 引擎状态区 - - SARSCAPE - - ISCE2 - - LANDSAR -- 生产提交区 - - 批次选择 - - 引擎选择 - - profile 选择 - - 输入/输出目录 - - 参数模板 -- 运行监控区 - - 当前任务 - - 步骤进度 - - 运行日志 - - 历史记录 -- 产物处理区 - - 提取 - - 入库 - - 重扫 - - 缓存重建 - -#### 关键任务 - -- 新建 `DinsarProductionPanel` -- 废弃前端对 `/idl/...` 语义的直接依赖 -- 增加引擎状态卡和 WSL 状态卡 -- 运行记录中展示 `engine`、`profile`、`run_id` - -#### 验收标准 - -- 前端可以清晰区分三类引擎状态 -- `landsar` 显示为预留,不可执行 -- 当 ENVI 不可用但 ISCE2 可用时,页面仍可提交 ISCE2 任务 - -### T3. SARSCAPE 兼容接入 - -#### 目标 - -- 将现有 ENVI 链路正式纳入新生产体系 -- 明确 `metatask` 与 `custom6` 是同一引擎下的两个 profile - -#### 关键任务 - -- 封装现有 ENVI 运行逻辑为 `sarscape_engine` -- 将 `metatask` 和 `custom6` 暴露为 profile -- 保留进度文件、日志、超时监控和历史运行能力 - -#### 验收标准 - -- 旧 ENVI 作业仍可执行 -- 新系统中可选择 `sarscape/metatask` 与 `sarscape/custom6` -- 原有任务状态、日志和结果提取行为保持兼容 - -### T4. ISCE2 + WSL 接入 - -#### 目标 - -- 建立 ISCE2 运行的正式环境检查、任务执行和输出接入能力 - -#### WSL 环境校验任务 - -- 检查 WSL 是否安装 -- 检查是否支持 WSL2 -- 检查目标 distro 是否存在 -- 检查目标 distro 是否可启动 -- 检查 `bash -lc` 是否可执行 -- 检查 Python 是否可执行 -- 检查 ISCE2 是否可 import -- 检查目标入口命令是否可执行 -- 检查 DEM 路径在 WSL 内是否可读 -- 检查轨道目录在 WSL 内是否可读 -- 检查输出目录在 WSL 内是否可写 -- 检查 Windows 路径到 WSL 路径转换是否正确 -- 增加可选 smoke test - -#### 建议配置项 - -- `ISCE2_ENABLED` -- `ISCE2_WSL_DISTRO` -- `ISCE2_PYTHON` -- `ISCE2_PROFILE` -- `ISCE2_DEM_PATH` -- `ISCE2_ORBIT_DIR` -- `ISCE2_WORK_ROOT` -- `ISCE2_OUTPUT_ROOT` -- `ISCE2_SMOKE_TEST_ENABLED` - -#### 关键任务 - -- 新建 `wsl_service.py` -- 新建 `isce2_engine.py` -- 建立 WSL 执行和日志封装 -- 将 ISCE2 输出纳入统一结果收集和入库流程 - -#### 验收标准 - -- 前端可见 ISCE2 环境状态 -- WSL 校验结果可解释、可定位故障点 -- ISCE2 至少有一个正式 profile 可提交并跑通 - -### T5. 运维自检增强 - -#### 目标 - -- 将当前“系统健康检查”升级为“系统级 + D-InSAR 专项级”双层运维自检 - -#### 关键任务 - -- 保留现有 `/health` 简版接口 -- 增加管理员详细健康接口 -- 增加多引擎专项检查 -- 增加 WSL 专项检查 -- 增加 D-InSAR 队列、存储、结果一致性检查 -- 增加自愈动作入口 - -#### 专项健康检查项 - -- 核心服务状态 - - 数据库 - - schema - - worker - - queue -- 引擎状态 - - `sarscape` - - `isce2` - - `landsar` -- WSL 状态 -- DEM/轨道/输出目录可用性 -- D-InSAR 运行任务积压 -- stale run 检测 -- 输出未入库检测 -- 入库但源文件缺失检测 -- 缓存一致性检测 - -#### 自愈入口建议 - -- 重扫结果目录 -- 重建缓存 -- 重试入库 -- 重置 stale run -- 标记失效结果 - -#### 验收标准 - -- 运维面板能区分系统级故障与引擎级故障 -- 当 ENVI 不可用但 ISCE2 可用时,总体 D-InSAR 服务状态应为 `degraded` 或 `ok`,不能直接判全系统失败 -- WSL 故障能定位到具体检查项 - -### T6. 结果管理增强 - -#### 目标 - -- 将 D-InSAR 结果从“扫描到的一条文件记录”升级为“正式结果资产” - -#### 核心设计 - -- `Run` - - 一次真实生产执行 -- `Artifact` - - 一次运行产出的具体文件 -- `Managed Result` - - 面向前端与业务的主结果对象 - -#### 关键任务 - -- 扩充结果模型字段 -- 增加结果来源追踪 -- 增加版本管理 -- 增加多产物管理 -- 增加结果生命周期状态 -- 增加批量治理能力 -- 增加多引擎结果对比能力 - -#### 结果管理能力清单 - -- 来源追踪 - - engine - - profile - - batch - - pair - - run -- 版本管理 - - 当前版本 - - 历史版本 - - 最新成功版本 -- 多产物管理 - - disp - - coherence - - unwrapped phase - - geotiff - - browse - - log -- 生命周期管理 - - `NEW` - - `INGESTED` - - `QC_PENDING` - - `QC_PASSED` - - `QC_REJECTED` - - `PUBLISHED` - - `ARCHIVED` -- 批量操作 - - 发布 - - 归档 - - 重扫 - - 重建缓存 - - 重绑定来源 - - 导出 -- 检索增强 - - 按引擎 - - 按批次 - - 按任务名 - - 按状态 - - 按 AOI - - 按版本 - -#### 前端结果管理架构建议 - -- 结果列表视图 -- 结果详情视图 -- 来源与版本视图 -- 治理与运维视图 - -#### 运维治理项 - -- 入库记录存在但源文件缺失 -- run 存在但 artifact 缺失 -- 当前版本指针异常 -- 同名结果冲突 -- 缓存与主文件不一致 -- 已发布结果未完成审核 - -#### 验收标准 - -- 同一对影像允许保留多引擎结果 -- 同一任务允许保留多版本 -- 结果详情页能追踪到来源 run 和主产物列表 -- 运维面板可识别结果资产层异常 - -## 8. 实施顺序 - -### Phase 1 - -- 完成多引擎与结果管理设计冻结 -- 完成接口契约 -- 完成数据模型草案 - -### Phase 2 - -- 实现后端抽象层 -- 完成 SARSCAPE 兼容接入 -- 保持旧接口兼容 - -### Phase 3 - -- 实现新前端生产中心 -- 实现引擎状态、运行监控和运维自检增强 - -### Phase 4 - -- 接入 ISCE2 + WSL -- 打通首条正式生产链 - -### Phase 5 - -- 实现结果管理增强 -- 补齐版本、来源、治理与自愈 - -## 9. 交付顺序建议 - -建议按照以下顺序落地: - -1. 先重构生产抽象层,不先碰最终结果治理细节 -2. 先让 `sarscape` 在新体系下跑通 -3. 再接入 `isce2` -4. 最后再把结果管理全面升级 - -这样可以避免在“生产链尚未稳定”时提前锁死结果模型细节。 - -## 10. 当前结论 - -本轮不应被理解为“给当前 ENVI 面板再加一个 ISCE2 按钮”,而应被理解为: - -- D-InSAR 生产系统平台化 -- D-InSAR 结果资产化 -- 运维自检从系统健康升级为多引擎专项健康 - -后续所有开发任务、联调任务和运维验收,均以本 Task 文档为总入口。 diff --git a/docs/archive/ENVI_Integration_Notes.md b/docs/archive/ENVI_Integration_Notes.md deleted file mode 100644 index 6352772..0000000 --- a/docs/archive/ENVI_Integration_Notes.md +++ /dev/null @@ -1,150 +0,0 @@ -# IDL/ENVI + 系统集成技术备忘录 - -> 更新日期: 2026-02-23 - -## 一、架构概览 - -``` -前端 (React) → FastAPI → Job Queue (PostgreSQL) → Job Worker → 子进程 (envi_runner_cli) → envipyengine → ENVI/SARscape -``` - -- 前端提交任务 → 创建 Task + Job 记录 → Worker 领取 Job → 启动独立 Python 子进程执行 ENVI 工作流 -- 子进程通过 progress JSON 文件向父进程报告进度 -- 父进程监控子进程存活状态,同时通过 `_task_keepalive` 协程每 30 秒刷新 Task 的 `updated_at` 防止僵尸检测误杀 -- 前端有两层锁: IDLAutomationPanel 内的黄色横幅 + App.jsx 的全局蓝色遮罩 - -## 二、已踩的坑与解决方案 - -### 1. subprocess.PIPE 缓冲区死锁 - -**现象**: 子进程挂起,不再输出 -**原因**: Windows 上 `subprocess.Popen(stdout=PIPE)` 的管道缓冲区约 4KB,envipyengine 输出超过后阻塞 -**方案**: 使用 `tempfile.mkstemp()` 创建临时文件接收 stdout/stderr,子进程退出后读取 - -### 2. Windows TemporaryFile 句柄不可继承 - -**现象**: `Popen` 无法写入 `tempfile.TemporaryFile` 创建的文件 -**原因**: Windows 上 `TemporaryFile` 创建的句柄默认不可被子进程继承 -**方案**: 改用 `tempfile.mkstemp()` + `os.close(fd)` + 手动 `os.unlink()` 清理 - -### 3. envipyengine 提前返回(文件未写完) - -**现象**: `task.execute()` 返回后,ENVI 仍在写输出文件(特别是 step 6 的 `_rsp_disp` 文件) -**原因**: envipyengine 在 taskengine 返回后立即返回 Python,但 ENVI 后台仍在写大文件 -**方案**: -- 在 step 6 后(无论成功/异常)调用 `_wait_for_disp_stable()` 等待 `*_rsp_disp` 文件出现并稳定 -- 最后调用 `_wait_files_stable()` 确认所有文件写完 -- 稳定判定: 连续 3 轮(每轮 15 秒)文件大小不变 - -### 4. envipyengine 报错但文件已生成 - -**现象**: `task.execute()` 抛出 "outputs not generated" 异常,但实际文件已正确生成 -**原因**: envipyengine 的输出验证逻辑与 SARscape 实际行为不一致(特别是 step 3 Orbital Trend Removal) -**方案**: except 块中扫描输出目录,按文件名模式(如 `*ISARRRPF*.sml`)查找已生成的文件,构建 SARSCAPEDATA 字典继续后续步骤 - -### 5. envipyengine 永久挂起 - -**现象**: `task.execute()` 永远不返回,即使 ENVI 已完成处理 -**原因**: envipyengine 内部的 taskengine 进程未正确退出 -**方案**: 使用 `ThreadPoolExecutor` 包装 `task.execute()`,设置超时 `ENVI_TASK_TIMEOUT_SECONDS=14400`(4小时),超时后抛出异常,由文件扫描逻辑兜底 - -### 6. 子进程文件活动监控误杀 - -**现象**: 前端提前解锁,ENVI 进程仍在运行 -**原因**: `job_handlers.py` 的监控循环检测到进度文件和输出目录超过 `ENVI_FILE_STALE_SECONDS` 无变化,kill 了子进程。某些步骤(如 Phase Unwrapping)长时间在内存中计算不写文件 -**方案**: 将 `ENVI_FILE_STALE_SECONDS` 从 600 增大到 14400,与任务超时一致 - -### 7. Task 僵尸检测误杀(根本原因) - -**现象**: 前端在 ENVI 处理过程中解锁 -**原因**: `task_service.get_active_tasks()` 内置僵尸检测——每次前端轮询时检查 RUNNING 任务的 `updated_at`,超过 `TASK_TIMEOUT_MINUTES=60` 分钟未更新则标记为 FAILED。子进程运行期间 handler 没有更新 task 的 `updated_at`,而单个 step 可能超过 60 分钟 -**方案**: 在 `_run_envi_workflow_job` 中添加 `_task_keepalive` 协程,每 30 秒读取进度文件并调用 `task_service.update_task()` 刷新 `updated_at`,同时更新进度百分比(step 映射到 10%-90%)和步骤信息 - -### 8. 强制解锁按钮被全局遮罩遮挡 - -**现象**: 强制解锁按钮写在 IDLAutomationPanel 的黄色横幅里,但全局蓝色遮罩(`App.jsx` 的 `global-task-overlay`)盖住了整个页面,用户无法点击 -**原因**: 系统有两层锁——IDLAutomationPanel 内部的 `isLocked` 控制按钮禁用,App.jsx 的 `isGlobalLocked` 控制全屏蓝色遮罩。强制解锁只加在了内层 -**方案**: 在 `App.jsx` 的全局遮罩上添加「管理员强制解锁」按钮(仅管理员可见),点击后展开密码输入框,确认后取消所有活跃任务并解锁。同时保留 IDLAutomationPanel 内的强制解锁作为备用 - -## 三、当前超时/保护参数 - -| 参数 | 值 | 位置 | 作用 | -|------|-----|------|------| -| `ENVI_TASK_TIMEOUT_SECONDS` | 14400 (4h) | `.env` | 单步 `task.execute()` 最大等待 | -| `ENVI_FILE_STALE_SECONDS` | 14400 (4h) | `.env` | 子进程无文件活动的 kill 阈值 | -| `JOB_WORKER_STALE_RUNNING_SECONDS` | 7200 (2h) | `.env` | Job 心跳超时(每 5s 更新) | -| `TASK_TIMEOUT_MINUTES` | 60 | `task_service.py` | 僵尸任务检测阈值(keepalive 每 30s 刷新) | -| `ENVI_STABILITY_CHECK_INTERVAL` | 15s | `.env` | 文件稳定检查间隔 | -| `ENVI_STABILITY_ROUNDS` | 3 | `.env` | 连续稳定轮数 | -| `ENVI_STABILITY_MAX_WAIT` | 3600 (1h) | `.env` | 文件稳定等待上限 | -| `IDL_JOB_MAX_ATTEMPTS` | 1 | `.env` | 不重试,失败即终止 | - -## 四、自定义 D-InSAR 6 步流程 - -| 步骤 | Task 名称 | 典型耗时 | 已知问题 | -|------|-----------|----------|----------| -| 1. 干涉图生成 | `SARsInSARInterferogramGeneration` | ~900s | 无 | -| 2. 滤波+相干性 | `SARsInSARFilterAndCoherence` | ~400s | 无 | -| 3. 轨道趋势去除 | `SARsInSARRemoveResidualPhaseFrequency` | ~65s | 报 "outputs not generated" 但文件已生成 | -| 4. 相位解缠 | `SARsInSARPhaseUnwrapping` | ~1000s | 无 | -| 5a. GCP 生成 | Python (rasterio+geopandas) | <1s | 无 | -| 5b. 精化再平化 | `SARsInSARRefinementAndReflattening` | ~30s | 无 | -| 6. 位移+地理编码 | `SARsInSARPhaseToDisplacement` | ~7600s | envipyengine 可能提前返回或挂起;第二个 Task 对曾出现 "SARscape process unexpectedly terminated" | - -单个 Task 对总耗时约 2.8 小时(测试数据)。两个 Task 对串行处理约 3.8 小时。 - -## 五、前端锁机制 - -### 两层锁结构 - -1. **全局蓝色遮罩** (`App.jsx` → `global-task-overlay`) - - `App.jsx` 每 3 秒轮询 `GET /tasks/active` - - 有任何活跃任务 → `isGlobalLocked=true` → 全屏蓝色遮罩覆盖整个 UI - - 显示所有活跃任务的名称、进度条、状态消息 - - 管理员可见「管理员强制解锁」按钮 → 输入密码 → 取消所有任务 → 遮罩消失 - - 任务完成后自动刷新页面数据 - -2. **IDLAutomationPanel 内部锁** (`IDLAutomationPanel.jsx`) - - 每 10 秒轮询 `GET /tasks/active`,查找 IDL 类型任务 - - 有活跃 IDL 任务 → `isLocked=true` → 按钮禁用 + 黄色横幅 - - 横幅内也有强制解锁按钮(作为备用入口) - -### 强制解锁 API - -- `POST /tasks/{task_id}/force-cancel` -- 需要管理员 JWT + 请求体中的密码双重验证 -- 将任务标记为 CANCELLED,前端下次轮询时解锁 - -## 六、关键文件清单 - -| 文件 | 职责 | -|------|------| -| `backend/app/services/envi_service.py` | ENVI 工作流核心(6 步流程、GCP 生成、文件稳定等待) | -| `backend/app/services/envi_runner_cli.py` | 子进程入口(加载 .env、调用 run_workflow) | -| `backend/app/services/job_handlers.py` | Job handler(子进程监控、keepalive、文件稳定检查) | -| `backend/app/services/job_worker.py` | Worker 主循环(心跳、stale 检测) | -| `backend/app/services/task_service.py` | Task 管理(僵尸检测在 get_active_tasks 中) | -| `backend/app/routers/tasks.py` | Task API(含 force-cancel 端点) | -| `backend/app/routers/idl.py` | IDL 路由(Import/D-InSAR 任务提交) | -| `frontend/src/App.jsx` | 全局遮罩锁 + 强制解锁 UI | -| `frontend/src/IDLAutomationPanel.jsx` | SARscape 面板(内部锁 + 备用强制解锁) | -| `frontend/src/api/idl.js` | 前端 API 函数 | -| `.env` | 所有超时和处理参数配置 | - -## 七、待测试内容 - -1. **keepalive 机制验证**: 确认前端在整个处理过程中保持锁定(蓝色遮罩不消失),进度和步骤信息正确显示 -2. **双 Task 对完整流程**: 两个 Task 对串行处理,全部完成后前端才解锁 -3. **全局遮罩强制解锁**: 管理员在蓝色遮罩上输入密码后能正确取消任务并解锁 -4. **进度显示**: 遮罩上的进度条和步骤信息随 ENVI 处理实时更新(Step 1/6 → Step 6/6, 10% → 90%) -5. **异常恢复**: envipyengine 报错时文件扫描兜底逻辑是否可靠 -6. **metatask 模式**: 确认默认模式不受自定义模式改动影响 -7. **多用户场景**: 管理员操作锁定时,只读用户仍可正常浏览(只读用户看不到强制解锁按钮) -8. **第二个 Task 对 step 6 失败**: 上次测试中第二个 Task 对的 step 6 报 "SARscape process unexpectedly terminated",需确认是数据问题还是系统问题 - -## 八、最近一次测试结果 (2026-02-23) - -- Task_20250309_20250112: 全部 6 步成功,总耗时 10104.9s -- Task_20250310_20250113_9: 步骤 1-5b 成功,step 6 失败 ("SARscape process unexpectedly terminated",耗时 1853s) -- 第一个 Task 的 `_wait_for_disp_stable` 修复生效(成功路径也等待 disp 文件) -- 前端锁在第一个 Task 处理期间因僵尸检测被误杀(已通过 keepalive 修复,待验证) diff --git a/docs/archive/FIX_FRONTEND_LOCKING_2026-03-05.md b/docs/archive/FIX_FRONTEND_LOCKING_2026-03-05.md deleted file mode 100644 index ef011cd..0000000 --- a/docs/archive/FIX_FRONTEND_LOCKING_2026-03-05.md +++ /dev/null @@ -1,265 +0,0 @@ -# 前端锁定修复总结(2026-03-05) - -## 修复概述 - -按照系统设计理念(管理员操作时全局锁定),修复了两个前端锁定问题: - -1. ✅ **数据分发任务锁定**(P0 高优先级) -2. ✅ **扫描任务锁定**(P1 中优先级) - ---- - -## 修复 1:数据分发任务锁定 - -### 问题 -`DataCopierPanel` 是独立的任务管理系统,不与全局锁定机制集成。 - -### 修改文件 -1. `frontend/src/App.jsx` -2. `frontend/src/DataCopierPanel.jsx` - -### 修改内容 - -**App.jsx (1334 行)**: -```javascript -// 修改前 - - -// 修改后 - handleTaskStart(taskId, '数据分发任务已入队,正在处理...')} -/> -``` - -**DataCopierPanel.jsx (14 行)**: -```javascript -// 修改前 -const DataCopierPanel = ({ apiEndpoint, readOnly = false }) => { - -// 修改后 -const DataCopierPanel = ({ apiEndpoint, readOnly = false, onJobQueued }) => { -``` - -**DataCopierPanel.jsx (119-131 行)**: -```javascript -// 修改前 -try { - const response = await axios.post(endpoint, { - batch_id: selectedBatchId, - dest_dir: destDir, - copy_statuses: copyStatuses, - }, { withCredentials: true }); - setTaskId(response.data.task_id); -} catch (error) { - -// 修改后 -try { - const response = await axios.post(endpoint, { - batch_id: selectedBatchId, - dest_dir: destDir, - copy_statuses: copyStatuses, - }, { withCredentials: true }); - const taskId = response.data.task_id; - setTaskId(taskId); - - // 触发全局锁定 - if (onJobQueued) { - onJobQueued(taskId); - } -} catch (error) { -``` - -### 效果 -- 数据分发任务启动时,前端立即锁定 -- 任务完成后,前端自动解锁 -- 与其他任务(IDL 自动化、数据监控)行为一致 - ---- - -## 修复 2:扫描任务锁定 - -### 问题 -扫描任务执行太快(< 3 秒),前端轮询间隔(3 秒)来不及捕获 RUNNING 状态。 - -### 修改文件 -`frontend/src/DataMonitorPanel.jsx` - -### 修改内容 - -**DataMonitorPanel.jsx (168-198 行)**: -```javascript -// 修改前 -const handleRunNow = async (target) => { - // ... 验证逻辑 ... - setLoading(true); - setMessage(`正在触发${targetMap[target] || '全部'}手动扫描...`); - try { - const url = target ? `${apiEndpoint}/monitor/run-now?target=${target}` : `${apiEndpoint}/monitor/run-now`; - const res = await fetch(url, { - method: 'POST', - credentials: 'include' - }); - const data = await res.json(); - if (res.ok) { - setMessage(data.message); - if (onTaskStart) onTaskStart(data.task_id, `已触发${targetMap[target] || '全部'}手动扫描...`); - } - // ... - } -}; - -// 修改后 -const handleRunNow = async (target) => { - // ... 验证逻辑 ... - setLoading(true); - setMessage(`正在触发${targetMap[target] || '全部'}手动扫描...`); - - // 立即触发全局锁定(在 API 调用之前) - if (onTaskStart) { - onTaskStart(null, `正在触发${targetMap[target] || '全部'}手动扫描...`); - } - - try { - const url = target ? `${apiEndpoint}/monitor/run-now?target=${target}` : `${apiEndpoint}/monitor/run-now`; - const res = await fetch(url, { - method: 'POST', - credentials: 'include' - }); - const data = await res.json(); - if (res.ok) { - setMessage(data.message); - // 更新任务 ID - if (onTaskStart) onTaskStart(data.task_id, `已触发${targetMap[target] || '全部'}手动扫描...`); - } - // ... - } -}; -``` - -### 关键改进 -1. **在 API 调用之前立即锁定**:`onTaskStart(null, message)` -2. **API 成功后更新任务 ID**:`onTaskStart(taskId, message)` -3. **失败时自动解锁**:全局轮询检测到没有活跃任务时自动解锁 - -### 效果 -- 用户点击"扫描"按钮后,前端立即锁定 -- 即使任务执行很快(< 3 秒),用户也能看到锁定状态 -- 任务完成后,前端自动解锁 - ---- - -## 测试验证 - -### 测试 1:数据分发任务锁定 -1. 登录管理员账号 -2. 进入"数据分发" Tab -3. 选择批次和目标目录 -4. 点击"开始复制" -5. **预期**:前端立即锁定,显示"数据分发任务已入队,正在处理..." -6. 等待任务完成 -7. **预期**:前端自动解锁 - -### 测试 2:扫描任务锁定 -1. 登录管理员账号 -2. 进入"数据管理" Tab -3. 点击"扫描雷达数据"或"扫描 D-InSAR 结果" -4. **预期**:前端立即锁定,显示"正在触发...手动扫描..." -5. 等待任务完成(可能很快) -6. **预期**:前端自动解锁 - -### 测试 3:多任务冲突 -1. 启动数据分发任务 -2. **预期**:前端锁定,无法启动其他任务 -3. 尝试点击其他操作按钮 -4. **预期**:按钮被禁用或操作被阻止 - ---- - -## 技术细节 - -### handleTaskStart 函数行为 - -```javascript -// hooks/useDinsarOperations.js:123-129 -const handleTaskStart = (taskId, message) => { - if (taskId) { - setPendingTaskIds(prev => [...prev, taskId]); - } - setIsGlobalLocked(true); // 总是锁定 - if (message) addLog('info', message); -}; -``` - -**关键点**: -- `taskId` 可以为 `null`(立即锁定,稍后更新 ID) -- `setIsGlobalLocked(true)` 总是执行(无论 taskId 是否为 null) -- 这允许我们在 API 调用之前就锁定前端 - -### 解锁机制 - -前端通过 `useGlobalTaskControl` hook 自动解锁: - -```javascript -// hooks/useGlobalTaskControl.js:56-64 -if (hasRunningTasks !== isGlobalLockedRef.current) { - setIsGlobalLocked(hasRunningTasks); - if (!hasRunningTasks) { - addLog('success', '后台任务已完成,正在同步最新数据...'); - setTimeout(() => { - initializeAppDataRef.current?.({ refreshRadarSearch: true }); - }, 500); - } -} -``` - -**工作原理**: -1. 每 3 秒轮询 `/tasks/active` -2. 检测到没有活跃任务时,自动解锁 -3. 不需要手动调用解锁 - ---- - -## 修改总结 - -### 修改文件 -1. `frontend/src/App.jsx` - 1 处修改(添加 onJobQueued 回调) -2. `frontend/src/DataCopierPanel.jsx` - 2 处修改(接收回调 + 调用回调) -3. `frontend/src/DataMonitorPanel.jsx` - 1 处修改(提前锁定) - -### 代码行数 -- 新增:约 10 行 -- 修改:约 5 行 -- 删除:0 行 - -### 风险评估 -- 🟢 **低风险**:改动小,逻辑清晰 -- 🟢 **向后兼容**:不影响现有功能 -- 🟢 **易于回滚**:修改集中,容易撤销 - ---- - -## 相关文档 - -- `ANALYSIS_FRONTEND_LOCKING_2026-03-05.md` - 问题分析 -- `SECURITY_FIX_PROGRESS.md` - 修复进度跟踪 -- `HOTFIX_PROJ_CONFLICT_2026-03-05.md` - PROJ 冲突修复 - ---- - -## 下一步 - -1. ✅ 代码修改完成 -2. ⏳ 前端构建测试 -3. ⏳ 功能测试验证 -4. ⏳ 用户验收测试 - ---- - -**修复完成时间**:2026-03-05 -**修复人**:Claude Opus 4.6 -**状态**:✅ 代码修改完成,等待测试验证 diff --git a/docs/archive/GAMMA_WSL2_INTEGRATION_PLAN.md b/docs/archive/GAMMA_WSL2_INTEGRATION_PLAN.md deleted file mode 100644 index 5c3dfb2..0000000 --- a/docs/archive/GAMMA_WSL2_INTEGRATION_PLAN.md +++ /dev/null @@ -1,163 +0,0 @@ -# GAMMA + WSL2 双引擎集成方案 - -## Context - -当前系统使用 ENVI/SARscape 做 D-InSAR 处理,速度慢(单对处理需数十分钟到数小时)。用户有 GAMMA License,希望在 Windows 服务器的 WSL2 中部署 GAMMA 作为第二处理引擎。ENVI 保留不动,用户可在前端选择用哪个引擎。 - -GAMMA 通过 WSL2 调用完全可行:`subprocess.run(['wsl', '-d', 'Ubuntu', 'bash', '-c', 'gamma_cmd ...'])`,与现有 ENVI 的 subprocess 模式一致。 - ---- - -## 架构对比 - -``` -ENVI 链路(保留不动): - POST /idl/jobs/dinsar (engine=envi) - → SystemJob(IDL_RUN_DINSAR) → envi_runner_cli 子进程 - → envi_dinsar.run_dinsar_custom_workflow() → 6步 SARscape - → envipyengine → taskengine.exe - -GAMMA 链路(新增): - POST /idl/jobs/dinsar (engine=gamma) - → SystemJob(GAMMA_RUN_DINSAR) → gamma_runner_cli 子进程 - → gamma_dinsar.run_dinsar_workflow() → 9步 GAMMA CLI - → subprocess.run(['wsl', ...]) → GAMMA 原生命令 -``` - -两条链路共享:SystemTask/SystemJob 任务队列、progress.json 进度机制、job_worker 监控逻辑。 - ---- - -## 新增文件 - -| 文件 | 职责 | -|------|------| -| `backend/app/services/gamma_service.py` | WSL2 执行器、路径转换(win↔wsl)、配置读取、环境检查、进度写入 | -| `backend/app/services/gamma_dinsar.py` | GAMMA D-InSAR 9步工作流 | -| `backend/app/services/gamma_runner_cli.py` | 子进程 CLI 入口(对标 envi_runner_cli.py) | -| `backend/app/services/gamma_import.py` | LT-1 原始数据 → GAMMA SLC 格式导入 | -| `scripts/check_gamma_wsl.py` | GAMMA + WSL2 环境诊断脚本 | - -## 修改文件 - -| 文件 | 修改内容 | -|------|----------| -| `.env` | 新增 `GAMMA_ENABLED`、`GAMMA_WSL_DISTRO`、`GAMMA_HOME`、`GAMMA_ENV_SCRIPT`、`GAMMA_DEM_FILE` 等配置 | -| `backend/app/config.py` | Settings 类新增 GAMMA 配置字段 | -| `backend/app/services/job_handlers.py` | 新增 `GAMMA_RUN_DINSAR` handler,注册到 `_HANDLERS` | -| `backend/app/routers/idl.py` | `DinsarJobRequest` 新增 `engine` 字段(envi/gamma),路由分发 | -| `frontend/src/IDLAutomationPanel.jsx` | Step 2 新增引擎选择器(radio:ENVI / GAMMA),GAMMA 不可用时灰显 | -| `frontend/src/api/idl.js` | `queueDinsarJob` payload 新增 `engine` | - ---- - -## GAMMA D-InSAR 9步工作流 - -每步通过 `gamma_service.gamma_exec(cmd, work_dir)` → `wsl -d {distro} bash -c 'source gamma_env.sh && {cmd}'` 执行。 - -| 步骤 | GAMMA 命令 | 功能 | 对应 ENVI 步骤 | -|------|-----------|------|---------------| -| 1 | LT-1 导入(自定义) | 原始数据 → .slc + .slc.par | SARsImportLuTan1 | -| 2 | `gc_map` | DEM 准备 + lookup table | (ENVI 内置在 Step1) | -| 3 | `create_offset` + `offset_pwr` + `offset_fit` + `SLC_interp` | 配准 | InterferogramGeneration 内部 | -| 4 | `SLC_intf` + `multi_look` | 干涉图生成 | InterferogramGeneration | -| 5 | `adf` | 自适应滤波(Goldstein) | FilterAndCoherence | -| 6 | `cc_wave` | 相干性估计 | FilterAndCoherence | -| 7 | `mcf` | 相位解缠 | PhaseUnwrapping | -| 8 | `dispmap` | 相位→位移 | PhaseToDisplacement | -| 9 | `geocode_back` + `data2geotiff` | 地理编码 + 转 GeoTIFF | PhaseToDisplacement | - ---- - -## 关键设计 - -### 路径转换 -```python -def win_to_wsl(win_path: str) -> str: - # Z:\Test_data\Task_001 → /mnt/z/Test_data/Task_001 - # D:\SRTM\dem.tif → /mnt/d/SRTM/dem.tif -``` - -### 进度跟踪 -写入与 ENVI 完全相同格式的 `backend/runtime/idl_worker/job_{id}_progress.json`,job_handler 的 keepalive 无需修改。GAMMA 的 `total_steps=9`(ENVI 为 6),进度公式 `(pair_index-1+step/total)/total_pairs` 天然适配。 - -### 输出格式统一 -GAMMA 最终输出转为 GeoTIFF(`data2geotiff`),同时生成 ENVI `.hdr`,使 `extract_disp_results()` 和 D-InSAR 结果扫描能统一识别。 - -### 前端引擎选择 -页面加载时调 `GET /gamma/status` 检查 GAMMA 是否可用。可用时显示 radio 选择器,不可用时 GAMMA 选项灰显标注"(未配置)"。 - ---- - -## .env 配置项 - -```ini -# ============ GAMMA + WSL2 配置 ============ -# 总开关(false 则前端不显示 GAMMA 选项) -GAMMA_ENABLED=false - -# WSL2 发行版名称(wsl -l -v 查看) -GAMMA_WSL_DISTRO=Ubuntu-22.04 - -# GAMMA 安装路径(Linux 路径,WSL2 内部) -GAMMA_HOME=/opt/GAMMA_SOFTWARE-20230101 - -# GAMMA 环境初始化脚本(source 此文件后 GAMMA 命令可用) -GAMMA_ENV_SCRIPT=/opt/GAMMA_SOFTWARE-20230101/gamma_env.sh - -# DEM 文件路径(Windows 路径,自动转为 WSL 路径) -GAMMA_DEM_FILE=D:\SRTM30m\srtm_dem.tif - -# 处理参数 -GAMMA_GEOCODE_PIXEL_SIZE_M=10.0 -GAMMA_ADF_WINDOW_SIZE=32 -GAMMA_MCF_COH_THRESHOLD=0.3 -GAMMA_RANGE_LOOKS=4 -GAMMA_AZIMUTH_LOOKS=1 -``` - ---- - -## 分阶段实施 - -### Phase 1:基础设施 + 环境验证 -- `.env` / `config.py` 配置层 -- `gamma_service.py`:`win_to_wsl()`、`wsl_exec()`、`gamma_exec()`、`check_gamma_environment()` -- `scripts/check_gamma_wsl.py` 诊断脚本 -- `GET /gamma/status` 端点 -- 手动验证:`wsl -d Ubuntu bash -c 'source gamma_env.sh && which par_S1_SLC'` - -### Phase 2:LT-1 数据导入(技术风险最高) -- `gamma_import.py`:解析 LT-1 `.meta.xml` → 构建 GAMMA `.slc.par` 参数文件 -- **关键验证**:对一个 Task_*/master/ 执行导入,检查 .slc.par 参数是否正确 -- 如果 GAMMA 内置 LT-1 支持,直接调用;否则需手工构建参数文件 - -### Phase 3:D-InSAR 核心工作流 -- `gamma_dinsar.py`:9步工作流 -- `gamma_runner_cli.py`:CLI 入口 -- 逐步调试,每步验证输入输出 - -### Phase 4:任务调度 + 前端集成 -- `job_handlers.py`:新增 GAMMA handler -- `idl.py`:engine 字段分发 -- `IDLAutomationPanel.jsx`:引擎选择器 -- 端到端测试 - ---- - -## 风险点 - -1. **LT-1 导入**(最大风险):GAMMA 是否内置 LT-1 读取器需要安装后确认。Plan B:用 GDAL 读 TIFF + 手工构建 .slc.par -2. **WSL2 跨文件系统 I/O**:`/mnt/z/` 比 WSL 原生文件系统慢 ~5x。初期可接受,后续可优化为先拷贝到 WSL 内部处理 -3. **GAMMA License**:WSL2 中需配置 FlexLM `LM_LICENSE_FILE`,在 `gamma_env.sh` 中统一设置 - ---- - -## 验证 - -1. `py_compile` 所有新增/修改的 .py 文件 -2. `npm run build` 前端构建通过 -3. `python scripts/check_gamma_wsl.py` 环境诊断通过 -4. 前端选 GAMMA 引擎 → 提交任务 → 进度正常更新 → 输出 GeoTIFF 位移图 -5. 前端选 ENVI 引擎 → 行为与改造前完全一致(回归测试) -6. GAMMA 未配置时 → 前端 GAMMA 选项灰显,无法选择 diff --git a/docs/archive/HARDCODE_AUDIT_2026-03-05.md b/docs/archive/HARDCODE_AUDIT_2026-03-05.md deleted file mode 100644 index 6b5e136..0000000 --- a/docs/archive/HARDCODE_AUDIT_2026-03-05.md +++ /dev/null @@ -1,278 +0,0 @@ -# 硬编码审计报告(2026-03-05) - -## 审计目的 - -确保系统可以在不同环境(开发机、生产环境、客户现场)部署,所有配置必须通过 `.env` 文件管理,不允许硬编码。 - -## 审计范围 - -- 后端代码(`backend/`) -- 启动脚本(`run_backend.py`, `scripts/`) -- 配置文件(`.env`, `config.py`) - -## 发现的硬编码问题 - -### 🔴 高优先级(必须修复) - -#### 1. Ollama API 地址硬编码 - -**位置**: -- `backend/app/ai_service.py:231` -- `backend/app/services/health_service.py:115` - -**问题**: -```python -# 硬编码 Ollama API 地址 -resp = await client.get("http://127.0.0.1:11434/api/tags") -``` - -**影响**: -- 如果 Ollama 部署在其他机器或端口,无法连接 -- 客户环境可能使用不同的 Ollama 地址 - -**修复方案**: -```python -# 从环境变量读取 -OLLAMA_BASE_URL = os.getenv("OLLAMA_BASE_URL", "http://127.0.0.1:11434") -resp = await client.get(f"{OLLAMA_BASE_URL}/api/tags") -``` - -**`.env` 配置**: -```bash -# Ollama 服务地址(基础 URL) -OLLAMA_BASE_URL=http://127.0.0.1:11434 -``` - ---- - -#### 2. Nginx 健康检查地址硬编码 - -**位置**: -- `backend/app/services/health_service.py:130` - -**问题**: -```python -# 硬编码 Nginx 地址 -resp = await client.get("http://127.0.0.1/") -``` - -**影响**: -- 如果 Nginx 监听其他端口或地址,健康检查失败 -- 客户环境可能使用不同的 Nginx 配置 - -**修复方案**: -```python -# 从环境变量读取 -NGINX_HEALTH_URL = os.getenv("NGINX_HEALTH_URL", "http://127.0.0.1/") -resp = await client.get(NGINX_HEALTH_URL) -``` - -**`.env` 配置**: -```bash -# Nginx 健康检查地址 -NGINX_HEALTH_URL=http://127.0.0.1/ -``` - ---- - -#### 3. 后端绑定地址硬编码 - -**位置**: -- `run_backend.py:29` - -**问题**: -```python -# 硬编码绑定地址 -bind_host = "127.0.0.1" -``` - -**影响**: -- 无法从外部访问(Docker 容器、远程部署) -- 某些部署场景需要绑定 `0.0.0.0` - -**修复方案**: -```python -# 从环境变量读取 -bind_host = os.getenv("BACKEND_BIND_HOST", "127.0.0.1") -``` - -**`.env` 配置**: -```bash -# 后端绑定地址(127.0.0.1 仅本地,0.0.0.0 允许外部访问) -BACKEND_BIND_HOST=127.0.0.1 -``` - ---- - -### 🟡 中优先级(建议修复) - -#### 4. PROJ 数据库路径检查脚本硬编码 - -**位置**: -- `scripts/check_proj.py:62` - -**问题**: -```python -# 硬编码 PostgreSQL PROJ 路径 -pg_proj = r"C:\Program Files\PostgreSQL\17\share\contrib\postgis-3.6\proj\proj.db" -``` - -**影响**: -- 不同 PostgreSQL 版本路径不同 -- 客户环境可能安装在其他位置 - -**修复方案**: -```python -# 从环境变量读取,或自动检测 -pg_proj = os.getenv("POSTGRESQL_PROJ_PATH", r"C:\Program Files\PostgreSQL\17\share\contrib\postgis-3.6\proj\proj.db") -``` - ---- - -### 🟢 低优先级(可选优化) - -#### 5. 默认值中的硬编码 - -**位置**: -- `backend/app/config.py:33-34` - -**问题**: -```python -OLLAMA_API_URL: str = "http://127.0.0.1:11434/api/generate" -DEFAULT_VLM_MODEL: str = "qwen3-vl:8b" -``` - -**说明**: -- 这些是默认值,已经通过 `os.getenv()` 读取 -- 但默认值本身是硬编码的 - -**修复方案**: -- 保持现状(默认值是合理的) -- 或者在文档中明确说明这些默认值 - ---- - -## 修复优先级 - -| 优先级 | 问题 | 影响 | 修复难度 | -|--------|------|------|----------| -| 🔴 P0 | Ollama API 地址 | 高 | 低 | -| 🔴 P0 | Nginx 健康检查地址 | 中 | 低 | -| 🔴 P0 | 后端绑定地址 | 高 | 低 | -| 🟡 P1 | PROJ 路径检查 | 低 | 低 | -| 🟢 P2 | 默认值硬编码 | 低 | 无需修复 | - ---- - -## 修复计划 - -### 阶段 1:立即修复(P0) - -1. **修改 `backend/app/ai_service.py`** - - 添加 `OLLAMA_BASE_URL` 环境变量 - - 修改 API 调用使用动态 URL - -2. **修改 `backend/app/services/health_service.py`** - - 添加 `OLLAMA_BASE_URL` 和 `NGINX_HEALTH_URL` 环境变量 - - 修改健康检查使用动态 URL - -3. **修改 `run_backend.py`** - - 添加 `BACKEND_BIND_HOST` 环境变量 - - 支持配置绑定地址 - -4. **更新 `.env` 文件** - - 添加新的配置项和注释 - -5. **更新文档** - - 在部署文档中说明这些配置项 - -### 阶段 2:建议修复(P1) - -1. **修改 `scripts/check_proj.py`** - - 支持从环境变量读取 PostgreSQL 路径 - - 或自动检测 PostgreSQL 安装路径 - ---- - -## 验证方法 - -### 1. 配置文件验证 - -检查 `.env` 文件是否包含所有必需的配置项: - -```bash -# 必需配置 -DATABASE_URL=... -IDL_EXECUTABLE=... -IDL_DINSAR_DEM_BASE_FILE=... - -# 新增配置 -OLLAMA_BASE_URL=http://127.0.0.1:11434 -NGINX_HEALTH_URL=http://127.0.0.1/ -BACKEND_BIND_HOST=127.0.0.1 -``` - -### 2. 代码审计 - -运行以下命令检查是否还有硬编码: - -```bash -# 检查绝对路径 -grep -r "C:\\\\" backend/ --include="*.py" -grep -r "D:\\\\" backend/ --include="*.py" - -# 检查 IP 地址 -grep -r "127\.0\.0\.1" backend/ --include="*.py" -grep -r "localhost" backend/ --include="*.py" - -# 检查端口号 -grep -r ":11434" backend/ --include="*.py" -grep -r ":8000" backend/ --include="*.py" -``` - -### 3. 部署测试 - -在不同环境测试: - -1. **本地开发环境**:使用默认配置 -2. **Docker 容器**:修改 `BACKEND_BIND_HOST=0.0.0.0` -3. **远程 Ollama**:修改 `OLLAMA_BASE_URL=http://192.168.1.100:11434` -4. **自定义端口**:修改 `PORT=18000` - ---- - -## 部署检查清单 - -在部署到客户环境前,确认: - -- [ ] 所有路径配置在 `.env` 中 -- [ ] 所有 IP 地址/端口配置在 `.env` 中 -- [ ] 没有硬编码的绝对路径 -- [ ] 没有硬编码的 IP 地址 -- [ ] 没有硬编码的端口号 -- [ ] 所有配置项都有默认值(合理的) -- [ ] 所有配置项都有注释说明 -- [ ] 部署文档已更新 - ---- - -## 相关文档 - -- `.env` 配置文件 -- `docs/PROJ_CONFIGURATION.md` - PROJ 数据库配置 -- `docs/DEPLOYMENT.md` - 部署文档(待创建) - ---- - -## 总结 - -**当前状态**: -- ✅ 大部分配置已通过 `.env` 管理 -- ❌ 发现 3 个高优先级硬编码问题 -- ⚠️ 需要立即修复以支持生产部署 - -**修复后**: -- ✅ 所有配置通过 `.env` 管理 -- ✅ 支持多种部署场景 -- ✅ 客户环境可自定义配置 -- ✅ 无需修改代码即可部署 diff --git a/docs/archive/HOTFIX_NGINX_BOM_2026-03-04.md b/docs/archive/HOTFIX_NGINX_BOM_2026-03-04.md deleted file mode 100644 index 677e6fb..0000000 --- a/docs/archive/HOTFIX_NGINX_BOM_2026-03-04.md +++ /dev/null @@ -1,113 +0,0 @@ -# 紧急修复:Nginx UTF-8 BOM 问题(2026-03-04) - -## 问题描述 - -在测试第一阶段修复时,发现 Nginx 启动失败: - -``` -nginx: [emerg] unknown directive "worker_processes" in Z:/Code/Insar_management_system_v2/nginx/nginx.conf:1 -``` - -注意 `worker_processes` 前面有一个不可见字符 ``(UTF-8 BOM)。 - -## 根本原因 - -1. `nginx.conf` 文件包含 UTF-8 BOM(字节序标记:`ef bb bf`) -2. Nginx 无法识别 BOM,将其视为非法字符 -3. PowerShell 的 `Set-Content -Encoding UTF8` 默认会添加 BOM - -## 影响 - -- 🔴 **严重**:Nginx 无法启动,整个系统无法访问 -- 这是第一阶段修复中 1.3 PowerShell 编码问题的延伸 - -## 解决方案 - -### 步骤 1:删除现有文件的 BOM - -```bash -cd "Z:\Code\Insar_management_system_v2\nginx" -tail -c +4 nginx.conf > nginx.conf.tmp -mv nginx.conf.tmp nginx.conf -``` - -**验证**: -```bash -xxd -l 16 nginx.conf -# 应该显示: -# 00000000: 776f 726b 6572 5f70 726f 6365 7373 6573 worker_processes -# 而不是: -# 00000000: efbb bf77 6f72 6b65 725f 7072 6f63 6573 ...worker_proces -``` - -### 步骤 2:修改 PowerShell 脚本 - -**文件**:`scripts/start_app.ps1:388` - -**修改前**: -```powershell -$NewConfContent | Set-Content -LiteralPath "$NginxConfPath" -NoNewline -Encoding UTF8 -``` - -**修改后**: -```powershell -# 使用 UTF8 无 BOM 编码写入 -$Utf8NoBom = New-Object System.Text.UTF8Encoding $false -[System.IO.File]::WriteAllText("$NginxConfPath", $NewConfContent, $Utf8NoBom) -``` - -**说明**: -- `System.Text.UTF8Encoding $false` 创建无 BOM 的 UTF-8 编码器 -- `[System.IO.File]::WriteAllText()` 直接写入文件,不添加 BOM -- 这在 PowerShell 5 和 PowerShell 7+ 中都有效 - -## 验证 - -### 1. 检查文件编码 -```bash -xxd -l 3 nginx/nginx.conf -# 应该不包含 ef bb bf -``` - -### 2. 启动系统 -```powershell -.\scripts\start_app.ps1 -``` - -### 3. 验证 Nginx 启动 -```bash -curl http://localhost:8080 -# 应该返回前端页面 -``` - -## 经验教训 - -1. **PowerShell 编码陷阱**: - - `Set-Content -Encoding UTF8` 会添加 BOM - - 需要使用 `[System.IO.File]::WriteAllText()` 或 PowerShell 6+ 的 `utf8NoBOM` - -2. **Nginx 对 BOM 敏感**: - - Nginx 配置文件必须是纯 ASCII 或无 BOM 的 UTF-8 - - BOM 会导致解析失败 - -3. **测试的重要性**: - - 如果没有实际测试,这个问题不会被发现 - - 代码审查和静态分析无法发现编码问题 - -## 更新的文档 - -- ✅ `SECURITY_FIX_PROGRESS.md` - 记录问题和解决方案 -- ✅ `scripts/start_app.ps1` - 修复编码问题 -- ✅ `nginx/nginx.conf` - 删除 BOM - -## 状态 - -- ✅ 问题已解决 -- ✅ 代码已修改 -- ⏳ 等待重新测试 - -## 下一步 - -1. 重新运行启动脚本 -2. 验证 Nginx 正常启动 -3. 继续第一阶段的其他测试 diff --git a/docs/archive/HOTFIX_PROJ_CONFLICT_2026-03-05.md b/docs/archive/HOTFIX_PROJ_CONFLICT_2026-03-05.md deleted file mode 100644 index 6c95979..0000000 --- a/docs/archive/HOTFIX_PROJ_CONFLICT_2026-03-05.md +++ /dev/null @@ -1,204 +0,0 @@ -# PROJ 数据库版本冲突问题修复(2026-03-05) - -## 问题描述 - -在扫描 D-InSAR 结果时出现 PROJ 错误: - -``` -PROJ: proj_identify: C:\Program Files\PostgreSQL\17\share\contrib\postgis-3.6\proj\proj.db -contains DATABASE.LAYOUT.VERSION.MINOR = 2 whereas a number >= 3 is expected. -It comes from another PROJ installation. -``` - -## 根本原因 - -1. **PROJ 版本冲突**: - - PostgreSQL 17 自带的 PostGIS 3.6 包含旧版 PROJ 数据库(版本 2) - - GDAL/rasterio 期望 PROJ 数据库版本 >= 3 - - 系统在坐标转换时(`Transformer.from_crs`)触发此警告 - -2. **环境变量优先级**: - - Windows 系统可能将 PostgreSQL 的 PROJ 路径添加到 PATH - - GDAL 默认搜索 PATH 中的 PROJ 数据库 - - 找到了 PostgreSQL 的旧版本而非 GDAL 自带的新版本 - -## 影响 - -- 🟡 **中等**:不会导致程序崩溃,但会产生大量警告日志 -- 坐标转换仍然可以工作(PROJ 会回退到兼容模式) -- 日志污染,影响问题排查 - -## 解决方案 - -### 方案 1:设置 PROJ_LIB 环境变量(已实施) - -**文件**:`run_backend.py` - -**修改内容**: -```python -def main() -> None: - # Load environment variables - load_dotenv() - - # Fix PROJ database version conflict - # PostgreSQL's PROJ database is outdated, use GDAL's bundled PROJ data - # This prevents "DATABASE.LAYOUT.VERSION.MINOR = 2 whereas >= 3 is expected" error - if "PROJ_LIB" not in os.environ: - # Try to find GDAL's PROJ data directory - try: - from osgeo import gdal - gdal_data = gdal.GetConfigOption("GDAL_DATA") - if gdal_data: - proj_lib = os.path.join(os.path.dirname(gdal_data), "proj") - if os.path.exists(proj_lib): - os.environ["PROJ_LIB"] = proj_lib - print(f"[*] Set PROJ_LIB to: {proj_lib}") - except Exception as e: - print(f"[WARN] Could not auto-configure PROJ_LIB: {e}") - - port = int(os.getenv("PORT", 8000)) - # ... rest of the code -``` - -**工作原理**: -1. 在启动时检查 `PROJ_LIB` 环境变量是否已设置 -2. 如果未设置,尝试从 GDAL 配置中获取 PROJ 数据目录 -3. 设置 `PROJ_LIB` 指向 GDAL 自带的 PROJ 数据库 - -### 方案 2:手动设置环境变量(备选) - -如果方案 1 不生效,可以在 `.env` 文件中手动设置: - -```bash -# 找到 GDAL 的 PROJ 数据目录 -python -c "from osgeo import gdal; import os; print(os.path.join(os.path.dirname(gdal.GetConfigOption('GDAL_DATA')), 'proj'))" - -# 将输出路径添加到 .env -PROJ_LIB=C:\path\to\gdal\proj -``` - -或者在 PowerShell 启动脚本中设置: - -```powershell -# scripts/start_app.ps1 -$env:PROJ_LIB = "C:\path\to\gdal\proj" -``` - -### 方案 3:抑制 GDAL 警告(不推荐) - -如果只是想隐藏警告而不解决根本问题: - -```python -# 在 run_backend.py 中添加 -from osgeo import gdal -gdal.SetConfigOption('CPL_LOG', 'OFF') # 关闭所有 GDAL 日志 -``` - -**不推荐原因**: -- 会隐藏所有 GDAL 错误和警告 -- 不解决根本问题 -- 可能掩盖其他重要错误 - -## 验证 - -### 1. 检查 PROJ_LIB 是否生效 - -启动后端后,查看日志: -``` -[*] Set PROJ_LIB to: C:\Users\...\site-packages\osgeo\data\proj -``` - -### 2. 运行扫描任务 - -在前端触发 D-InSAR 结果扫描,观察日志: -- ✅ 应该不再出现 PROJ 警告 -- ✅ Footprint 提取正常完成 - -### 3. 验证坐标转换 - -```python -from pyproj import Transformer -transformer = Transformer.from_crs("EPSG:32650", "EPSG:4326", always_xy=True) -x, y = transformer.transform(500000, 3000000) -print(f"Transformed: {x}, {y}") -# 应该不产生警告 -``` - -## 前端锁定问题 - -**问题**:扫描任务运行时前端没有锁定 - -**可能原因**: -1. 扫描任务执行太快(< 3 秒),前端轮询间隔(3 秒)来不及捕获 -2. 任务状态更新有延迟 - -**解决方案**: - -### 方案 A:增加最小锁定时间(推荐) - -在扫描任务开始时立即锁定,结束时延迟解锁: - -```python -# job_handlers.py -async def _handle_scan_dinsar(job: SystemJobORM) -> None: - if not job.task_id: - raise ValueError("SCAN_DINSAR requires task_id for progress tracking.") - - await task_service.start_task(job.task_id, message="正在扫描 D-InSAR 结果...") - - # 确保任务至少运行 2 秒,让前端有时间捕获 - start_time = time.time() - - # ... 执行扫描 ... - - # 确保最小执行时间 - elapsed = time.time() - start_time - if elapsed < 2.0: - await asyncio.sleep(2.0 - elapsed) - - await task_service.complete_task(job.task_id, message="扫描完成") -``` - -### 方案 B:前端立即锁定(更好) - -在提交扫描任务后立即锁定前端,不等待轮询: - -```javascript -// DataManagementPanel.jsx -const handleScanDinsarResults = async () => { - try { - // 立即锁定前端 - onJobQueued?.(null, 'SCAN_DINSAR'); - - const response = await scanDinsarResults(selectedDirs); - const taskId = response.data.task_id; - - // 更新任务 ID - onJobQueued?.(taskId, 'SCAN_DINSAR'); - - addLog('info', `D-InSAR 结果扫描任务已提交: ${taskId}`); - } catch (error) { - // 解锁前端 - setIsGlobalLocked(false); - addLog('error', `扫描失败: ${error.message}`); - } -}; -``` - -## 状态 - -- ✅ PROJ 环境变量修复已实施 -- ⏳ 等待测试验证 -- ⏳ 前端锁定问题待确认是否需要修复 - -## 下一步 - -1. 重启后端服务 -2. 运行 D-InSAR 结果扫描 -3. 观察日志,确认 PROJ 警告消失 -4. 如果前端仍未锁定,实施方案 B - -## 相关文档 - -- `SECURITY_FIX_PROGRESS.md` - 安全修复进度 -- `HOTFIX_NGINX_BOM_2026-03-04.md` - Nginx BOM 修复 diff --git a/docs/archive/IDL_INTEGRATION_STATUS.md b/docs/archive/IDL_INTEGRATION_STATUS.md deleted file mode 100644 index 940dee1..0000000 --- a/docs/archive/IDL_INTEGRATION_STATUS.md +++ /dev/null @@ -1,201 +0,0 @@ -# IDL/ENVI 集成状态文档 - -> 最后更新: 2026-02-21 - -## 1. 架构概述 - -系统的 D-InSAR 生产完全依赖 ENVI 5.6 + IDL 8.8 + SARscape 5.6+。 -当前采用 **envipyengine** 作为唯一执行引擎,通过 Python subprocess 调用 `taskengine.exe`。 - -### 执行链路 - -``` -前端 (IDLAutomationPanel.jsx) - → POST /api/idl/jobs/import 或 /api/idl/jobs/dinsar - → job_handlers.py: subprocess 启动 envi_runner_cli.py - → envi_service.py: 调用 envipyengine → taskengine.exe - → ENVI Task 执行 (SARsImportLuTan1 / SARsMetataskInSARDisplacementGeneration) -``` - -### 核心文件 - -| 文件 | 职责 | -|------|------| -| `backend/app/services/envi_service.py` | 核心服务:工作流、预检查、状态、历史 | -| `backend/app/services/envi_runner_cli.py` | CLI 入口,subprocess 中执行工作流 | -| `backend/app/services/job_handlers.py` | 任务分发,启动 envi_runner_cli 子进程 | -| `backend/app/routers/idl.py` | API 端点 | -| `frontend/src/IDLAutomationPanel.jsx` | 前端面板 (Step 1/2 布局) | -| `frontend/src/api/idl.js` | 前端 API 客户端 | - -## 2. 环境配置 - -### ENVI 安装路径 - -``` -C:\Program Files\Harris\ENVI56\ -├── IDL88\bin\bin.x86_64\idl.exe -├── IDL88\bin\bin.x86_64\idlde.exe -└── IDL88\bin\bin.x86_64\taskengine.exe -``` - -### .env 关键配置 - -```ini -IDL_EXECUTABLE=C:\Program Files\Harris\ENVI56\IDL88\bin\bin.x86_64\idl.exe -IDL_WORKBENCH_PATH=C:\Program Files\Harris\ENVI56\IDL88\bin\bin.x86_64\idlde.exe -IDL_WORKER_RUNTIME_DIR=...\backend\runtime\idl_worker -IDL_WORKER_DEFAULT_TIMEOUT_SECONDS=14400 -IDL_WORKER_MAX_TIMEOUT_SECONDS=43200 -IDL_DINSAR_DEM_BASE_FILE=D:\SRTM30m\SRTMDEM_RSP_SARscape -``` - -### envipyengine 配置 - -```python -# 已通过 envipyengine.config.set() 配置: -engine = r'C:\Program Files\Harris\ENVI56\IDL88\bin\bin.x86_64\taskengine.exe' -``` - -Python 环境: `C:\Users\16216\.conda\envs\InSAR\python.exe` (envipyengine v1.0.9) - -## 3. 根因分析:历史集成失败 - -### 3.1 症状回顾 - -之前尝试了三种引擎均不稳定: -- `idl.exe -e` 直连:进程挂起、管道错误、idl_opserver 崩溃 (0x0000000000000001) -- `taskengine.exe` 直调:内存访问违规 (0xC0000005 / exit code 3221225477) -- `envipyengine`:单独测试通过,集成到项目后崩溃 - -### 3.2 根因定位 - -**`.env` 中 `IDL_PATH` 变量名与 IDL 内置环境变量冲突。** - -- `IDL_PATH` 是 IDL 的保留环境变量,用于指定 `.pro` 文件搜索路径 -- 项目 `.env` 曾使用 `IDL_PATH` 存储 `idl.exe` 的可执行文件路径 -- `load_dotenv()` 将其注入 `os.environ`,子进程继承后 IDL 读到无效的搜索路径 -- `taskengine.exe` 因此崩溃,退出码 `0xC0000005` - -### 3.3 验证过程 - -三态测试确认因果关系: - -| 状态 | IDL_PATH 值 | 结果 | -|------|-------------|------| -| 干净环境 | 未设置 | ✅ 成功 | -| 设置错误值 | `C:\...\idl.exe` | ❌ 崩溃 (0xC0000005) | -| 移除后恢复 | 未设置 | ✅ 成功 | - -### 3.4 修复 - -将 `.env` 中 `IDL_PATH` 重命名为 `IDL_EXECUTABLE`,同步更新所有引用。 - -## 4. 当前架构设计 - -### 4.1 设计决策 - -1. **envipyengine 为唯一执行引擎** — 删除了 idl 直连和 taskengine 直调的全部代码 -2. **Import → D-InSAR 固定流水线** — D-InSAR 自动检测未导入数据,先 Import 再处理(智能串联) -3. **D-InSAR 使用 metatask** — 精细参数 (filter_method 等) metatask 不支持,已移除 -4. **DEM 路径为系统级配置** — 存储在 .env,不暴露到前端 -5. **subprocess 执行模式** — envipyengine 在独立子进程中运行,隔离 FastAPI 主进程 - -### 4.2 ENVI Tasks - -| Task | 用途 | 关键参数 | -|------|------|----------| -| `SARsImportLuTan1` | 导入 LuTan-1 原始数据 | `INPUT_FILE_LIST`, `ROOT_URI_FOR_OUTPUT` | -| `SARsMetataskInSARDisplacementGeneration` | D-InSAR 位移生成 | `REFERENCE_SARSCAPEDATA`, `SECONDARY_SARSCAPEDATA`, `DEM_SARSCAPEDATA`, `OUTPUT_FOLDER` | - -### 4.3 智能串联逻辑 (D-InSAR 工作流) - -``` -对每个 Task_* 文件夹: - 1. 检查 master/slave 是否有 .sml - 2. 没有 → 查找 .meta.xml → 自动执行 Import - 3. Import 完成后验证 .sml 生成 - 4. 执行 D-InSAR metatask -``` - -### 4.4 API 端点 - -``` -GET /api/idl/status — 系统状态 (含 DEM 路径和可用性) -POST /api/idl/launch-workbench — 启动 IDL Workbench -POST /api/idl/inspect/import — Import 预检查 -POST /api/idl/inspect/dinsar — D-InSAR 预检查 (含 Import 状态检测) -POST /api/idl/jobs/import — 提交 Import 任务 -POST /api/idl/jobs/dinsar — 提交 D-InSAR 任务 -GET /api/idl/jobs/recent — 最近运行记录 -``` - -### 4.5 目录结构要求 - -Import 支持两种布局: -``` -# Task_* 结构 (推荐) -root_dir/ -├── Task_001/ -│ ├── master/ → *.meta.xml -│ └── slave/ → *.meta.xml -└── Task_002/ - ├── master/ - └── slave/ - -# 平铺结构 -root_dir/ -├── scene_001/ → *.meta.xml -└── scene_002/ → *.meta.xml -``` - -D-InSAR 仅支持 Task_* 结构。 - -## 5. 测试记录 - -### 5.1 envipyengine 验证 (2026-02-20) - -修复 IDL_PATH 冲突后,envipyengine Import 连续测试: - -| 次数 | 耗时 | 结果 | -|------|------|------| -| 1 | 100.5s | ✅ 成功 | -| 2 | 75.8s | ✅ 成功 | -| 3 | 71.8s | ✅ 成功 | -| 4 | 73.6s | ✅ 成功 | -| 5 | 81.7s | ✅ 成功 | - -测试数据: `Z:\Test_data\Test_IDL_1` - -### 5.2 集成测试 — Step 1: Import (2026-02-21) - -重构后通过前端 → 后端 → envipyengine 完整链路测试。 - -- 状态: ✅ 通过 -- 链路: 前端提交 → job_handlers subprocess → envi_runner_cli → envi_service → envipyengine - -### 5.3 集成测试 — Step 2: D-InSAR (待测试) - -- 状态: ⏳ 待测试 -- 前置条件: DEM 文件 `D:\SRTM30m\SRTMDEM_RSP_SARscape` 需存在于测试机 -- 智能串联 (自动 Import) 待验证 - -## 6. 已删除的遗留代码 - -重构中删除的文件 (2026-02-21): - -| 文件 | 行数 | 说明 | -|------|------|------| -| `backend/app/services/idl_worker_service.py` | 2249 | 三引擎架构的巨型服务 | -| `backend/app/services/idl_runner_cli.py` | 58 | 旧 CLI runner | -| `backend/app/services/envipyengine_runner.py` | 47 | 调试用独立 runner | - -清理的 .env 配置:所有 `IDL_TASKENGINE_*`、`IDL_WORKER_ENGINE`、`IDL_JOB_RUNNER_MODE`、 -`IDL_IDL_*`、`IDL_WORKER_PREFLIGHT_*`、D-InSAR 精细参数 (filter_method 等)。 - -## 7. 已知限制 - -1. **D-InSAR 精细参数不可调** — metatask 不支持 filter_method、unwrapping_coh_threshold 等参数 -2. **DEM 路径固定** — 从 .env 读取,不支持前端动态指定 -3. **单进程串行** — envipyengine 调用 taskengine.exe 是同步阻塞的,同一时间只能执行一个 ENVI Task -4. **仅支持 LuTan-1** — Import task 为 `SARsImportLuTan1`,其他卫星数据需要不同的 Task diff --git a/docs/archive/INDEX.md b/docs/archive/INDEX.md deleted file mode 100644 index 95ac613..0000000 --- a/docs/archive/INDEX.md +++ /dev/null @@ -1,85 +0,0 @@ -# 历史文档索引 - -最后更新:2026-04-26 - -本页用于组织 `docs/archive/` 下的历史文档。 - -原则: - -- 本目录中的材料默认不作为当前系统事实来源。 -- 它们保留用于追溯设计演进、阶段性判断和历史决策背景。 -- 当前真实规则优先看: - - [../../README.md](../../README.md) - - [../INDEX.md](../INDEX.md) - - [../DOCUMENTATION_GOVERNANCE.md](../DOCUMENTATION_GOVERNANCE.md) - -## 1. 2026-04-26 归档批次 - -### 1.1 旧 Gamma 直连方案 - -- [GAMMA_WSL2_INTEGRATION_PLAN.md](GAMMA_WSL2_INTEGRATION_PLAN.md) - -说明: - -- 这份文档描述的是旧的 `/idl/jobs/dinsar` + 独立 Gamma service / status 端点方案。 -- 当前代码和文档已经转向多引擎生产中心、共享 WSL 运行时和 `pyint`/Gamma 集成路径,因此该文档只保留历史参考价值。 - -### 1.2 旧配对重构计划与任务板 - -- [PAIRING_SYSTEM_REFACTOR_PLAN_20260413.md](PAIRING_SYSTEM_REFACTOR_PLAN_20260413.md) -- [DINSAR_ENHANCEMENT_TASKS.md](DINSAR_ENHANCEMENT_TASKS.md) - -说明: - -- 这两份材料保留分阶段计划和当时边界判断的价值,但不再作为当前系统事实来源。 -- 当前 pairing 运行态事实以 `CURRENT_STATUS_20260425.md`、`DATABASE_SELF_MAINTENANCE_AUDIT_20260425.md` 和健康检查为准。 -- 当前 D-InSAR 生产设计以 `PRODUCTION_RESULTS_MULTI_ENGINE_DESIGN_20260423.md`、`ISCE2_MANAGED_DINSAR_IMPLEMENTATION_20260424.md`、`ISCE2_PRODUCTION_RELIABILITY_HARDENING_DESIGN_20260424.md` 为准。 - -## 2. 2026-04-25 归档批次 - -### 2.1 SBAS 阶段文档 - -- [SBAS_FRONTEND_UX_SPEC_20260406.md](SBAS_FRONTEND_UX_SPEC_20260406.md) -- [SBAS_IMPLEMENTATION_TODO_20260406.md](SBAS_IMPLEMENTATION_TODO_20260406.md) -- [SBAS_PROGRESS_STATUS_20260406.md](SBAS_PROGRESS_STATUS_20260406.md) -- [SBAS_RESULT_MANAGEMENT_AND_DISPLAY_SPEC_20260406.md](SBAS_RESULT_MANAGEMENT_AND_DISPLAY_SPEC_20260406.md) -- [SBAS_SYSTEM_CHANGE_RISK_ASSESSMENT_20260406.md](SBAS_SYSTEM_CHANGE_RISK_ASSESSMENT_20260406.md) -- [SBAS_SYSTEM_EMBEDDING_DESIGN_20260406.md](SBAS_SYSTEM_EMBEDDING_DESIGN_20260406.md) -- [SBAS_SYSTEM_INTEGRATION_AUDIT_20260406.md](SBAS_SYSTEM_INTEGRATION_AUDIT_20260406.md) -- [SBAS_TASK_PATH_MODE_EVALUATION_20260406.md](SBAS_TASK_PATH_MODE_EVALUATION_20260406.md) - -说明: - -- 这批文档描述的是时序 InSAR / SBAS 接入初期的阶段方案。 -- 当前仍有效的时序主文档保留在 `docs/` 根目录: - - [../ISCE2_SBAS_TIMESERIES_DESIGN.md](../ISCE2_SBAS_TIMESERIES_DESIGN.md) - - [../ISCE2_SBAS_PRODUCT_SPEC.md](../ISCE2_SBAS_PRODUCT_SPEC.md) - -### 2.2 旧集成方案与汇报材料 - -- [WSL2_ISCE2_MINTPY_SBAS_INTEGRATION_PLAN_20260412.md](WSL2_ISCE2_MINTPY_SBAS_INTEGRATION_PLAN_20260412.md) -- [项目汇报.md](项目汇报.md) - -### 2.3 PyINT 过程型材料 - -- [PYINT_GAMMA_IMPLEMENTATION_TODO_20260418.md](PYINT_GAMMA_IMPLEMENTATION_TODO_20260418.md) -- [PYINT_LT1_COREG_ORBIT_HYPOTHESIS_EXPERIMENT_20260420.md](PYINT_LT1_COREG_ORBIT_HYPOTHESIS_EXPERIMENT_20260420.md) -- [PYINT_LT1_DEM_GEOMETRY_CHAIN_EXPERIMENT_20260420.md](PYINT_LT1_DEM_GEOMETRY_CHAIN_EXPERIMENT_20260420.md) -- [PYINT_LT1_DEM_SOURCE_EXPERIMENT_RESULTS_20260420.md](PYINT_LT1_DEM_SOURCE_EXPERIMENT_RESULTS_20260420.md) -- [PYINT_LT1_PAIR_SELECTION_EXPERIMENT_20260421.md](PYINT_LT1_PAIR_SELECTION_EXPERIMENT_20260421.md) - -说明: - -- 这批文档保留实验和实施过程价值,但不再放在主导航中。 -- 当前仍有导航价值的 PyINT 文档保留在 `docs/` 根目录。 - -## 3. 更早的历史文档 - -本目录还保留以下较早批次的历史材料: - -- 安全审计与修复过程 -- 热修复记录 -- 旧版 AI / ENVI / 日志管理设计 -- 历史 TODO 与阶段性工作单 - -如需判断它们是否仍然有效,以 `docs/INDEX.md` 的“当前有效”区为准。 diff --git a/docs/archive/LOG_MANAGEMENT_ANALYSIS_2026-03-05.md b/docs/archive/LOG_MANAGEMENT_ANALYSIS_2026-03-05.md deleted file mode 100644 index 5b09d54..0000000 --- a/docs/archive/LOG_MANAGEMENT_ANALYSIS_2026-03-05.md +++ /dev/null @@ -1,510 +0,0 @@ -# 系统日志管理分析与优化方案(2026-03-05) - -## 当前日志分布情况 - -### 1. 根目录日志(❌ 不规范) - -``` -Z:\Code\Insar_management_system_v2\ -├── unpacker_activity.log (34KB) - 解包活动日志 -└── unpacker_log.json (8.6KB) - 解包结构化日志 -``` - -**来源**:`scripts/unpack_archives.py` -**问题**: -- 直接保存在项目根目录,不专业 -- 与代码文件混在一起 -- 不便于管理和清理 -- 可能被误提交到 Git - ---- - -### 2. IDL/ENVI 工作日志 - -``` -backend/runtime/idl_worker/ -├── 20260225_022409_dinsar_custom.log -├── 20260225_051527_dinsar_custom.log -├── 20260304_091723_import.log -├── 20260304_091854_import.log -└── dinsar_custom_progress.log -``` - -**来源**:ENVI 工作流执行日志 -**状态**:✅ 已规范化(在 `backend/runtime/` 下) - ---- - -### 3. SARscape 工作日志 - -``` -.idl/sarmap/sarscape-3-6_1_0-idl_8_8/ -├── hwConfig.log -└── sarscape_work/ - ├── Process.log - ├── Process_20260226165004.log - ├── Process_20260226210207.log - └── Process_20260227091802.log -``` - -**来源**:SARscape 软件自动生成 -**状态**:✅ 可接受(第三方软件日志) - ---- - -### 4. 应用日志(缺失) - -**问题**: -- ❌ 没有统一的应用日志目录 -- ❌ 没有日志轮转机制 -- ❌ 没有日志级别配置 -- ❌ 没有结构化日志 - ---- - -## 日志用途分析 - -### unpacker_activity.log - -**用途**:记录解包脚本的活动日志(文本格式) - -**内容示例**: -``` -2026-03-04 20:53:15 - INFO - 开始扫描归档文件... -2026-03-04 20:53:15 - INFO - 找到 5 个归档文件 -2026-03-04 20:53:16 - INFO - 解包完成: archive1.zip -``` - -**问题**: -- 保存在根目录 -- 无日志轮转(会无限增长) -- 无日志级别控制 - ---- - -### unpacker_log.json - -**用途**:记录解包脚本的结构化日志(JSON 格式) - -**内容示例**: -```json -{ - "timestamp": "2026-03-04T20:53:15", - "level": "INFO", - "message": "解包完成", - "archive": "archive1.zip", - "files_extracted": 123, - "duration_seconds": 5.2 -} -``` - -**问题**: -- 保存在根目录 -- 与 activity.log 重复 -- 无清理机制 - ---- - -## 系统中的其他日志 - -### 1. 数据库日志 -- **位置**:PostgreSQL 数据目录 -- **管理**:由 PostgreSQL 管理 - -### 2. Nginx 日志 -- **位置**:`nginx/logs/` (如果配置了) -- **管理**:由 Nginx 管理 - -### 3. 任务日志 -- **位置**:数据库 `system_task` 表的 `logs` 字段 -- **管理**:通过 API 查询 - -### 4. 进度文件 -- **位置**:`backend/runtime/idl_worker/job_{job_id}_progress.json` -- **管理**:任务完成后自动清理 - ---- - -## 日志管理问题 - -### 问题 1:日志分散 - -**现状**: -- 根目录:unpacker 日志 -- backend/runtime/:ENVI 日志 -- .idl/:SARscape 日志 -- 数据库:任务日志 - -**影响**: -- 难以统一查看 -- 难以统一清理 -- 难以统一备份 - ---- - -### 问题 2:无日志轮转 - -**现状**: -- unpacker_activity.log 会无限增长 -- ENVI 日志会累积(每次执行生成新文件) - -**影响**: -- 磁盘空间浪费 -- 日志文件过大影响性能 - ---- - -### 问题 3:无统一日志框架 - -**现状**: -- unpacker 使用自定义日志 -- 后端使用 print() 输出 -- 没有统一的日志级别 - -**影响**: -- 日志格式不一致 -- 难以过滤和搜索 -- 难以集成日志分析工具 - ---- - -## 优化方案 - -### 方案 A:统一日志目录结构(推荐) - -``` -Z:\Code\Insar_management_system_v2\ -├── logs/ # 统一日志目录 -│ ├── app/ # 应用日志 -│ │ ├── backend.log # 后端主日志 -│ │ ├── backend.log.1 # 轮转日志 -│ │ ├── backend.log.2 -│ │ └── ... -│ ├── tasks/ # 任务日志 -│ │ ├── envi/ # ENVI 工作流日志 -│ │ │ ├── 20260304_091723_import.log -│ │ │ └── ... -│ │ └── unpacker/ # 解包任务日志 -│ │ ├── unpacker_20260304.log -│ │ └── ... -│ ├── access/ # 访问日志 -│ │ ├── nginx_access.log -│ │ └── api_access.log -│ └── error/ # 错误日志 -│ ├── nginx_error.log -│ └── api_error.log -├── backend/ -└── frontend/ -``` - -**优点**: -- 所有日志集中管理 -- 便于备份和清理 -- 便于配置 .gitignore - ---- - -### 方案 B:使用 Python logging 模块 - -**配置文件**:`backend/app/logging_config.py` - -```python -import logging -import logging.handlers -import os -from pathlib import Path - -# 日志根目录 -LOG_ROOT = Path(__file__).parent.parent.parent / "logs" -LOG_ROOT.mkdir(exist_ok=True) - -# 应用日志目录 -APP_LOG_DIR = LOG_ROOT / "app" -APP_LOG_DIR.mkdir(exist_ok=True) - -# 任务日志目录 -TASK_LOG_DIR = LOG_ROOT / "tasks" -TASK_LOG_DIR.mkdir(exist_ok=True) - -# 日志配置 -LOGGING_CONFIG = { - "version": 1, - "disable_existing_loggers": False, - "formatters": { - "default": { - "format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s", - "datefmt": "%Y-%m-%d %H:%M:%S", - }, - "detailed": { - "format": "%(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s", - "datefmt": "%Y-%m-%d %H:%M:%S", - }, - "json": { - "()": "pythonjsonlogger.jsonlogger.JsonFormatter", - "format": "%(asctime)s %(name)s %(levelname)s %(message)s", - }, - }, - "handlers": { - "console": { - "class": "logging.StreamHandler", - "level": "INFO", - "formatter": "default", - "stream": "ext://sys.stdout", - }, - "file": { - "class": "logging.handlers.RotatingFileHandler", - "level": "DEBUG", - "formatter": "detailed", - "filename": str(APP_LOG_DIR / "backend.log"), - "maxBytes": 10 * 1024 * 1024, # 10MB - "backupCount": 5, - "encoding": "utf-8", - }, - "error_file": { - "class": "logging.handlers.RotatingFileHandler", - "level": "ERROR", - "formatter": "detailed", - "filename": str(APP_LOG_DIR / "error.log"), - "maxBytes": 10 * 1024 * 1024, # 10MB - "backupCount": 5, - "encoding": "utf-8", - }, - }, - "loggers": { - "": { # root logger - "level": "INFO", - "handlers": ["console", "file", "error_file"], - }, - "uvicorn": { - "level": "INFO", - "handlers": ["console", "file"], - "propagate": False, - }, - "sqlalchemy": { - "level": "WARNING", - "handlers": ["file"], - "propagate": False, - }, - }, -} - -def setup_logging(): - """初始化日志配置""" - import logging.config - logging.config.dictConfig(LOGGING_CONFIG) -``` - -**使用方式**: - -```python -# backend/app/main.py -from .logging_config import setup_logging - -setup_logging() -logger = logging.getLogger(__name__) - -@app.on_event("startup") -async def startup_event(): - logger.info("应用启动") -``` - ---- - -### 方案 C:修复 unpacker 日志位置 - -**修改文件**:`scripts/unpack_archives.py` - -```python -# 修改前 -PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) -LOG_FILE = os.path.join(PROJECT_ROOT, "unpacker_log.json") -ACTIVITY_LOG = os.path.join(PROJECT_ROOT, "unpacker_activity.log") - -# 修改后 -PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) -LOG_DIR = os.path.join(PROJECT_ROOT, "logs", "tasks", "unpacker") -os.makedirs(LOG_DIR, exist_ok=True) - -# 使用日期命名,便于清理 -from datetime import datetime -log_date = datetime.now().strftime("%Y%m%d") -LOG_FILE = os.path.join(LOG_DIR, f"unpacker_{log_date}.json") -ACTIVITY_LOG = os.path.join(LOG_DIR, f"unpacker_{log_date}.log") -``` - ---- - -### 方案 D:添加日志清理机制 - -**定期清理脚本**:`scripts/cleanup_logs.py` - -```python -import os -import time -from pathlib import Path - -PROJECT_ROOT = Path(__file__).parent.parent -LOG_ROOT = PROJECT_ROOT / "logs" - -# 清理策略 -CLEANUP_RULES = { - "logs/app/*.log.*": 30, # 应用日志保留 30 天 - "logs/tasks/envi/*.log": 7, # ENVI 日志保留 7 天 - "logs/tasks/unpacker/*.log": 7, # 解包日志保留 7 天 -} - -def cleanup_old_logs(): - """清理过期日志""" - now = time.time() - for pattern, days in CLEANUP_RULES.items(): - max_age = days * 86400 # 转换为秒 - for log_file in LOG_ROOT.glob(pattern): - if log_file.is_file(): - age = now - log_file.stat().st_mtime - if age > max_age: - print(f"删除过期日志: {log_file} (已存在 {age/86400:.1f} 天)") - log_file.unlink() - -if __name__ == "__main__": - cleanup_old_logs() -``` - -**添加到定时任务**: -```python -# backend/app/main.py -from apscheduler.schedulers.asyncio import AsyncIOScheduler - -scheduler = AsyncIOScheduler() - -@app.on_event("startup") -async def startup_event(): - # 每天凌晨 3 点清理日志 - scheduler.add_job(cleanup_old_logs, "cron", hour=3, minute=0) - scheduler.start() -``` - ---- - -## 实施步骤 - -### 第一步:创建日志目录结构 - -```bash -cd Z:\Code\Insar_management_system_v2 -mkdir -p logs/app -mkdir -p logs/tasks/envi -mkdir -p logs/tasks/unpacker -mkdir -p logs/access -mkdir -p logs/error -``` - -### 第二步:移动现有日志 - -```bash -# 移动 unpacker 日志 -mv unpacker_activity.log logs/tasks/unpacker/unpacker_20260304.log -mv unpacker_log.json logs/tasks/unpacker/unpacker_20260304.json - -# 移动 ENVI 日志(已经在正确位置) -# backend/runtime/idl_worker/ 保持不变 -``` - -### 第三步:修改 unpacker 脚本 - -修改 `scripts/unpack_archives.py`,使用新的日志路径。 - -### 第四步:配置 Python logging - -创建 `backend/app/logging_config.py`,配置统一日志。 - -### 第五步:更新 .gitignore - -```gitignore -# 日志文件 -logs/ -*.log -*.log.* - -# 但保留日志目录结构 -!logs/.gitkeep -!logs/app/.gitkeep -!logs/tasks/.gitkeep -``` - -### 第六步:添加日志清理 - -创建 `scripts/cleanup_logs.py`,配置定时清理。 - ---- - -## 推荐配置 - -### 日志级别 - -```python -# 开发环境 -LOG_LEVEL = "DEBUG" - -# 生产环境 -LOG_LEVEL = "INFO" - -# 错误追踪 -ERROR_LOG_LEVEL = "ERROR" -``` - -### 日志轮转 - -```python -# 按大小轮转 -maxBytes = 10 * 1024 * 1024 # 10MB -backupCount = 5 # 保留 5 个备份 - -# 按时间轮转 -when = "midnight" # 每天午夜轮转 -interval = 1 # 每 1 天 -backupCount = 30 # 保留 30 天 -``` - -### 日志保留 - -```python -# 应用日志:30 天 -# 任务日志:7 天 -# 错误日志:90 天 -# 访问日志:30 天 -``` - ---- - -## 总结 - -### 当前问题 -1. ❌ unpacker 日志保存在根目录(不专业) -2. ❌ 日志分散在多个位置 -3. ❌ 无统一日志框架 -4. ❌ 无日志轮转和清理机制 - -### 推荐方案 -1. ✅ 创建统一的 `logs/` 目录 -2. ✅ 使用 Python logging 模块 -3. ✅ 配置日志轮转(按大小或时间) -4. ✅ 添加定时清理机制 -5. ✅ 更新 .gitignore - -### 优先级 -- **P0(立即)**:移动 unpacker 日志到 logs/ 目录 -- **P1(本周)**:配置 Python logging 模块 -- **P2(两周)**:添加日志轮转和清理 -- **P3(一个月)**:集成日志分析工具(如 ELK) - ---- - -## 下一步 - -需要我开始实施日志管理优化吗?我可以: -1. 创建日志目录结构 -2. 修改 unpacker 脚本 -3. 配置 Python logging -4. 添加日志清理脚本 - -还是你想先看看这个方案,再决定是否实施? diff --git a/docs/archive/LOG_MANAGEMENT_IMPLEMENTATION_2026-03-05.md b/docs/archive/LOG_MANAGEMENT_IMPLEMENTATION_2026-03-05.md deleted file mode 100644 index eaa4a1c..0000000 --- a/docs/archive/LOG_MANAGEMENT_IMPLEMENTATION_2026-03-05.md +++ /dev/null @@ -1,332 +0,0 @@ -# 日志管理功能实施总结(2026-03-05) - -## 实施概述 - -按照你的需求,实现了统一的日志管理功能: -- ✅ 统一日志目录结构 -- ✅ 前端管理模块(放在运维自检) -- ✅ Modal 查看日志 -- ✅ 手动删除(不自动删除) -- ✅ 仅管理员可删除 - ---- - -## 已完成的工作 - -### 1. 创建统一日志目录结构 - -``` -logs/ -├── app/ # 应用日志 -├── tasks/ # 任务日志 -│ ├── envi/ # ENVI 工作流日志 -│ └── unpacker/ # 解包任务日志 -└── error/ # 错误日志 -``` - -### 2. 移动现有日志文件 - -- ✅ `unpacker_activity.log` → `logs/tasks/unpacker/unpacker_20260304.log` -- ✅ `unpacker_log.json` → `logs/tasks/unpacker/unpacker_20260304.json` - -### 3. 修改 unpacker 脚本 - -**文件**:`scripts/unpack_archives.py` - -**修改内容**: -- 使用统一的日志目录 `logs/tasks/unpacker/` -- 使用日期命名日志文件(`unpacker_YYYYMMDD.log`) -- 自动创建日志目录 - -### 4. 更新 .gitignore - -**新增内容**: -```gitignore -# 日志文件 -logs/ -*.log -*.log.* - -# 但保留日志目录结构 -!logs/.gitkeep -!logs/app/.gitkeep -!logs/tasks/.gitkeep -!logs/error/.gitkeep -``` - -### 5. 实现后端 API - -**文件**:`backend/app/routers/logs.py` - -**API 端点**: -1. `GET /logs/list` - 列出所有日志文件 - - 支持按类型过滤(app, task, error) - - 返回文件名、大小、修改时间 - -2. `GET /logs/content/{log_path}` - 读取日志内容 - - 支持分页读取(offset, limit) - - 最多一次读取 10000 行 - - 防止路径遍历攻击 - -3. `DELETE /logs/{log_path}` - 删除日志文件 - - 仅管理员可删除 - - 防止路径遍历攻击 - -**安全特性**: -- 路径安全检查(防止 `../` 攻击) -- 权限控制(删除仅管理员) -- 文件类型验证 - -### 6. 实现前端 API - -**文件**:`frontend/src/api/logs.js` - -**函数**: -- `listLogs(logType)` - 获取日志列表 -- `getLogContent(logPath, offset, limit)` - 获取日志内容 -- `deleteLog(logPath)` - 删除日志 - -### 7. 实现前端日志管理组件 - -**文件**:`frontend/src/LogManagementPanel.jsx` - -**功能**: -- 日志列表展示(表格形式) -- 类型过滤(应用/任务/错误) -- 查看日志(Modal) -- 删除日志(仅管理员,需确认) -- 日志搜索(实时过滤) -- 分页加载(大文件支持) - -**UI 特性**: -- 类型标签(彩色徽章) -- 文件大小格式化(B/KB/MB) -- 修改时间显示 -- 深色代码编辑器风格 -- 响应式布局 - -### 8. 集成到运维自检 - -**修改文件**: -- `frontend/src/HealthCheckPanel.jsx` - 添加日志管理区域 -- `frontend/src/App.jsx` - 传递 currentUser 参数 - ---- - -## 功能特性 - -### 日志列表 - -| 列名 | 说明 | -|------|------| -| 文件名 | 日志文件名(等宽字体) | -| 类型 | 应用/任务/错误(彩色标签) | -| 大小 | 文件大小(自动格式化) | -| 修改时间 | 最后修改时间 | -| 操作 | 查看/删除按钮 | - -**过滤功能**: -- 全部 -- 应用日志 -- 任务日志 -- 错误日志 - -### 日志查看 Modal - -**顶部信息栏**: -- 文件名(等宽字体) -- 文件大小 -- 修改时间 -- 总行数 - -**搜索栏**: -- 实时搜索(过滤日志内容) -- 显示当前行范围 -- 上一页/下一页按钮 - -**日志内容区**: -- 深色背景(#1e1e1e) -- 等宽字体(Consolas, Monaco) -- 自动换行 -- 滚动查看 - -### 删除功能 - -**权限控制**: -- 只有管理员可以看到删除按钮 -- 非管理员点击会提示"只有管理员可以删除日志" - -**确认对话框**: -``` -确定要删除日志文件 "xxx.log" 吗? - -此操作不可恢复! -``` - ---- - -## 技术实现 - -### 后端 - -**路径安全检查**: -```python -def _is_safe_path(file_path: str) -> bool: - """检查路径是否安全(防止路径遍历攻击)""" - try: - requested_path = (LOG_ROOT / file_path).resolve() - return requested_path.is_relative_to(LOG_ROOT) - except (ValueError, RuntimeError): - return False -``` - -**分页读取**: -```python -with open(log_file, "r", encoding="utf-8", errors="replace") as f: - lines = f.readlines() - -total_lines = len(lines) -start = offset -end = min(offset + limit, total_lines) -content_lines = lines[start:end] -``` - -### 前端 - -**分页加载**: -```javascript -const loadLogContent = async (logPath, offset = 0) => { - const data = await getLogContent(logPath, offset, 1000); - setLogContent(data.content); - setTotalLines(data.total_lines); - setCurrentOffset(offset); -}; -``` - -**实时搜索**: -```javascript -const filteredContent = searchTerm - ? logContent.split('\n') - .filter(line => line.toLowerCase().includes(searchTerm.toLowerCase())) - .join('\n') - : logContent; -``` - ---- - -## 使用说明 - -### 查看日志 - -1. 登录系统 -2. 进入"运维自检" Tab -3. 滚动到"日志管理"区域 -4. 点击日志文件的"查看"按钮 -5. 在 Modal 中查看日志内容 -6. 使用搜索框过滤内容 -7. 使用上一页/下一页浏览大文件 - -### 删除日志 - -1. 以管理员身份登录 -2. 进入"运维自检" Tab -3. 找到要删除的日志文件 -4. 点击"删除"按钮 -5. 确认删除操作 -6. 日志文件被永久删除 - -### 过滤日志 - -1. 使用"类型过滤"下拉菜单 -2. 选择"应用日志"、"任务日志"或"错误日志" -3. 列表自动更新 - ---- - -## 文件清单 - -### 后端文件 -- `backend/app/routers/logs.py` - 日志管理 API(新建) -- `backend/app/routers/__init__.py` - 注册日志路由(修改) -- `scripts/unpack_archives.py` - 使用新日志路径(修改) - -### 前端文件 -- `frontend/src/api/logs.js` - 日志 API 封装(新建) -- `frontend/src/LogManagementPanel.jsx` - 日志管理组件(新建) -- `frontend/src/HealthCheckPanel.jsx` - 集成日志管理(修改) -- `frontend/src/App.jsx` - 传递 currentUser(修改) - -### 配置文件 -- `.gitignore` - 忽略日志文件(新建) - -### 目录结构 -- `logs/` - 统一日志目录(新建) -- `logs/app/` - 应用日志目录(新建) -- `logs/tasks/envi/` - ENVI 日志目录(新建) -- `logs/tasks/unpacker/` - 解包日志目录(新建) -- `logs/error/` - 错误日志目录(新建) - ---- - -## 测试清单 - -### 后端测试 -- [ ] 启动后端服务 -- [ ] 访问 `/api/logs/list` 验证 API -- [ ] 访问 `/api/logs/content/{log_path}` 验证内容读取 -- [ ] 测试路径遍历攻击(应该被拒绝) -- [ ] 测试删除权限(非管理员应该被拒绝) - -### 前端测试 -- [ ] 打包前端(`npm run build`) -- [ ] 登录系统 -- [ ] 进入"运维自检" Tab -- [ ] 验证日志列表显示 -- [ ] 点击"查看"按钮验证 Modal -- [ ] 测试搜索功能 -- [ ] 测试分页功能 -- [ ] 测试删除功能(管理员) -- [ ] 测试删除权限(非管理员应该看不到删除按钮) - -### 集成测试 -- [ ] 运行 unpacker 脚本,验证日志保存到新位置 -- [ ] 在前端查看新生成的日志 -- [ ] 删除日志后验证文件确实被删除 - ---- - -## 下一步优化(可选) - -### 短期优化 -1. 添加日志下载功能(如果需要) -2. 添加日志实时刷新(WebSocket) -3. 添加日志高亮(错误/警告) - -### 长期优化 -1. 使用 Monaco Editor 替代 Textarea -2. 添加日志统计(错误数、警告数) -3. 添加日志归档功能 -4. 集成日志分析工具(ELK) - ---- - -## 总结 - -✅ 已完成所有需求: -1. 统一日志管理 ✅ -2. 前端管理模块(放在运维自检)✅ -3. Modal 查看日志 ✅ -4. 手动删除(不自动删除)✅ -5. 仅管理员可删除 ✅ - -**代码修改**: -- 新建文件:5 个 -- 修改文件:4 个 -- 总代码行数:约 400 行 - -**风险评估**:🟢 低风险 -- 不影响现有功能 -- 只读操作无风险 -- 删除操作有权限控制和确认 - -**状态**:✅ 代码完成,等待测试验证 diff --git a/docs/archive/PAIRING_SYSTEM_REFACTOR_PLAN_20260413.md b/docs/archive/PAIRING_SYSTEM_REFACTOR_PLAN_20260413.md deleted file mode 100644 index 79ee15f..0000000 --- a/docs/archive/PAIRING_SYSTEM_REFACTOR_PLAN_20260413.md +++ /dev/null @@ -1,867 +0,0 @@ -# InSAR 配对系统重构方案 - -更新日期:2026-04-13 -状态:待实施 -适用范围:`Z:\Code\Insar_management_system_v2` 当前代码基线 - -## 1. 文档目标 - -这份文档不是抽象算法说明,而是基于当前系统现状给出的可落地重构方案,目标是: - -- 重构 D-InSAR 配对内核,解决当前正确性、稳定性和可维护性问题 -- 尽量复用现有数据库自动维护、root/cursor 自维护、manifest inventory、自检面板 -- 明确开发机条件下的迁移策略 -- 为后续实现提供分阶段执行顺序、表设计、接口边界和验收口径 - -## 2. 当前系统基础与约束 - -### 2.1 当前可直接复用的系统能力 - -当前系统已经具备以下基础设施,重构应直接接入,而不是另起一套平行机制: - -- 数据库启动自维护 - - `backend/app/db_maintenance.py` - - `ensure_database_ready()` 会在启动时创建表、补缺列、应用 SQL migration,并可在开发机上按环境变量执行整库 schema reset -- root 与 cursor 自维护 - - `backend/app/services/root_registry_service.py` - - 启动时会根据 `.env` 自动同步 `managed_roots` 与 `scan_cursors` -- manifest inventory 自维护 - - `backend/app/services/manifest_inventory_service.py` - - 会维护 `path_inventory`、`scan_cursors.last_seen_*`、fingerprint 与 root 缺失状态 -- 结果目录自维护 - - `backend/app/services/dinsar_scan_service.py` - - D-InSAR 统一扫描链路已经是“发布 -> manifest inventory -> catalog rebuild -> compat sync” -- 健康检查和一致性检查 - - `backend/app/services/health_service.py` - - `frontend/src/HealthCheckPanel.jsx` - - 当前自检已包含:数据库 schema、worker、catalog、source roots、D-InSAR bridge、一致性统计、系统维护入口 - -### 2.2 当前配对系统的核心问题 - -现有配对系统的主要问题不是功能不够多,而是核心语义不稳定: - -- SQL 路径和 Python fallback 路径不是同一算法 -- 同日影像会出现反向重复配对 -- `start_date` 在 SQL 主路径上已失效,但接口仍保留 -- `star` 策略没有固定参考像角色 -- `sequential` 对同日多景不稳定 -- `all` 策略排序与前端描述不一致 -- `aoi_overlap_threshold=0` 的语义前后端不一致 -- 当前任务命名与配对唯一性混在一起,业务主键不稳定 - -### 2.3 当前系统约束 - -- 当前扫描模式是 manual-only,调度器默认关闭 - - `backend/app/scheduler.py` -- `radar_data` 是当前源影像事实表,配对系统必须以它为唯一源 -- `dinsar_task_batches` / `dinsar_task_items` 已经是生产任务落地表 -- `result_products` / `dinsar_product_profiles` 已经承担结果资产追踪 -- 健康检查和统计接口已上线,新增配对系统状态应并入现有面板 - -## 3. 重构总目标 - -本轮重构的目标不是继续修补 `find_dinsar_pairs_v2`,而是将配对系统改造成四层结构: - -1. 影像事实层 -2. 配对指标缓存层 -3. 网络选择层 -4. 任务物化层 - -核心原则: - -- 候选对生成与网络选择分层 -- 所有策略共享同一候选池 -- 所有配对方向统一规范化 -- 所有结果可追溯到策略版本、参数快照和候选边来源 -- 不再保留“算法不等价的 fallback” - -## 4. 目标架构 - -### 4.1 Layer A:影像事实层 - -沿用现有 `radar_data` 表作为唯一源影像事实表。 - -重构要求: - -- `radar_data.id` 继续作为数据库主键 -- 引入明确的场景业务键 `scene_uid` - - 优先复用 `radar_data.unique_id` - - 若历史数据存在 `unique_id` 缺失或不稳定,启动时执行一次回填 - - 回填规则:`hash(normalized file_path)`,并写入新列或标准化字段 -- 所有配对相关对象都基于 `scene_uid` 或 `radar_data.id` 建立引用 - -建议: - -- 本轮实现阶段直接在 `radar_data` 上新增: - - `scene_uid` - - `scene_signature` - - `pairing_eligible` - - `pairing_updated_at` -- 如果希望减少变更面,也可以只新增 `scene_uid` 与 `pairing_updated_at` - -### 4.2 Layer B:配对指标缓存层 - -这是本次重构的核心。 - -目标: - -- 将“任意两个影像是否可配、其基础指标是什么”从请求时临时计算,改为可维护的持久化缓存 -- 缓存只保存与 AOI 无关的基础指标 -- AOI 相关指标在请求时动态叠加计算 - -建议新增表: - -#### `pairing_metric_cache` - -一行代表一个规范化后的影像对。 - -建议字段: - -- `id` -- `master_scene_ref_id` -- `slave_scene_ref_id` -- `master_scene_uid` -- `slave_scene_uid` -- `pair_uid` -- `metric_version` -- `orientation_rule_version` -- `time_baseline_days` -- `spatial_baseline_meters` -- `scene_overlap_ratio` -- `orbit_direction` -- `same_satellite` -- `same_imaging_mode` -- `same_polarization` -- `master_imaging_date` -- `slave_imaging_date` -- `master_satellite` -- `slave_satellite` -- `master_imaging_mode` -- `slave_imaging_mode` -- `master_polarization` -- `slave_polarization` -- `master_file_path` -- `slave_file_path` -- `status` -- `computed_at` - -唯一约束: - -- `unique(master_scene_ref_id, slave_scene_ref_id, metric_version)` - -关键约定: - -- `master/slave` 不是请求期可变概念,而是规范化方向 -- 规范化规则: - - 早时间 = master - - 同时间时,较小 `scene_uid` 或较小 `id` = master - -### 4.3 Layer C:缓存状态与脏队列 - -为了接入现有“自动维护 + 健康检查”体系,建议新增两张状态表。 - -#### `pairing_cache_state` - -建议设计为单例表或按 `cache_scope='global'` 管理。 - -字段建议: - -- `id` -- `cache_scope` -- `metric_version` -- `status` -- `scene_count` -- `pair_count` -- `dirty_scene_count` -- `last_full_rebuild_at` -- `last_incremental_reconcile_at` -- `last_error` -- `updated_at` - -状态建议: - -- `READY` -- `DIRTY` -- `REBUILDING` -- `DEGRADED` -- `FAILED` - -#### `pairing_dirty_scenes` - -用于增量维护。 - -字段建议: - -- `id` -- `scene_ref_id` -- `scene_uid` -- `reason` -- `marked_at` -- `resolved_at` -- `status` - -唯一约束建议: - -- `unique(scene_ref_id, status='PENDING')` 或代码层去重 - -### 4.4 Layer D:网络选择层 - -网络选择层不直接查 `radar_data`,而是查 `pairing_metric_cache`。 - -建议新增: - -#### `pairing_network_runs` - -代表一次配对请求或一次已保存的网络。 - -字段建议: - -- `id` -- `network_run_id` -- `strategy` -- `policy_version` -- `request_hash` -- `request_params_json` -- `aoi_source` -- `aoi_hash` -- `aoi_summary_json` -- `candidate_count` -- `selected_edge_count` -- `warning_count` -- `status` -- `fallback_used` -- `created_by` -- `created_at` - -#### `pairing_network_edges` - -代表某个网络 run 选中的边。 - -字段建议: - -- `id` -- `network_run_ref_id` -- `metric_cache_ref_id` -- `edge_rank` -- `selection_reason` -- `selection_score` -- `selection_meta_json` -- `is_reference_edge` -- `created_at` - -唯一约束: - -- `unique(network_run_ref_id, metric_cache_ref_id)` - -### 4.5 Layer E:任务物化层 - -当前 `dinsar_task_batches` / `dinsar_task_items` 可继续保留。 - -但建议增加可追溯字段: - -在 `dinsar_task_items` 上新增: - -- `network_run_id` -- `network_edge_id` -- `policy_version` -- `selection_strategy` -- `scene_pair_uid` - -在结果侧建议同步补充: - -- `result_products.pair_key` 继续保留 -- 在 `dinsar_product_profiles` 或 `params_json` 中补充: - - `network_run_id` - - `network_edge_id` - - `policy_version` - - `master_scene_uid` - - `slave_scene_uid` - -这样最终可以把: - -- 配对网络 -- 任务项 -- 生产结果 - -串成一条可追踪链路。 - -## 5. 配对算法设计 - -### 5.1 候选对生成 - -候选对生成只负责回答: - -- 这两个场景在基础事实上是否可配 -- 基础指标是什么 - -不负责回答: - -- 用什么策略选 -- 最后是否进入任务 - -基础硬约束: - -- `master != slave` -- 方向规范化后只保留一个方向 -- 同轨向 -- 日期格式合法 -- 几何相交 - -基础指标: - -- `time_baseline_days` -- `spatial_baseline_meters` -- `scene_overlap_ratio` - -注意: - -- `same_satellite` -- `same_imaging_mode` -- `same_polarization` - -只作为缓存事实,不在缓存层提前删边,以便不同请求复用同一缓存。 - -### 5.2 请求期过滤 - -请求期过滤发生在 `pairing_metric_cache` 之上: - -- `time_baseline_min/max` -- `spatial_baseline_max_meters` -- `overlap_threshold` -- `allowed_satellites` -- `cross_satellite_pairing` -- `require_same_imaging_mode` -- `require_same_polarization` -- `master_date_from/to` -- `slave_date_from/to` -- `aoi_overlap_threshold` - -AOI 处理原则: - -- AOI 不进入持久化 pair cache 主键 -- AOI 相关 overlap 在请求时动态计算 -- `0` 不再表示数值阈值,而统一转成 `null` -- 前后端统一语义: - - `null` = 不启用 AOI 覆盖阈值 - - `0 < x <= 1` = 启用阈值 - -### 5.3 四种策略的正式定义 - -#### `all` - -- 返回全部合法候选边 -- 排序统一为稳定顺序: - - `master_imaging_date` - - `slave_imaging_date` - - `selection_score desc` - - `pair_uid` - -不再使用“数据库返回顺序”作为结果语义。 - -#### `sequential` - -- 基于稳定时间序列排序构图 -- 排序键: - - `acquisition_time_utc` - - 无则 `imaging_date` - - 再无则 `scene_uid` -- 每景连接后续 `N` 景 -- 同日多景必须稳定,不允许受候选池原始顺序影响 - -#### `star` - -- 参考像必须固定角色 -- 本系统建议统一采用: - - 参考像始终为 `master` -- 若物理上参考像日期晚于目标像,则在选择层进行拒绝或显式反转并记录相位方向规则 - -本轮建议: - -- 为降低复杂度,先采用“参考像必须是 master”的严格规则 -- 如果用户选择的参考像晚于部分场景,则这些边不入网,并给出 warning - -#### `sbas` - -本轮不采用“简单按 overlap 贪心去重”的旧实现。 - -建议目标: - -- 图连通 -- 每景至少达到最小连接数 -- 时间覆盖尽量连续 -- 每景度数受控,避免过密 -- AOI 覆盖尽量多样 - -可落地实现: - -1. 从合法候选边中先构造基础时间邻接骨架 -2. 保证每个场景至少连接前后最近邻 -3. 再按综合评分补边 -4. 每补一条边,对已覆盖区域施加惩罚 -5. 达到目标连通性和最大边数后停止 - -综合评分建议: - -`selection_score = w_time * time_score + w_spatial * spatial_score + w_overlap * overlap_score + w_aoi * aoi_gain - w_redundancy * redundancy_penalty` - -### 5.4 删除旧 fallback 的原则 - -本轮不再保留“Python 语义独立 fallback”。 - -替代方案: - -- 候选边缓存是主路径 -- 如果缓存不存在或脏数据过多: - - 返回明确的 `DEGRADED` / `DIRTY` 状态 - - 允许管理员触发重建 - - 或在安全阈值内自动执行增量重建 - -也就是说: - -- 可以有“降级状态” -- 不能再有“悄悄换算法” - -## 6. 与现有扫描和自动维护机制的集成 - -### 6.1 源影像扫描后的集成点 - -当前源影像扫描入口: - -- `backend/app/services/data_service.py::scan_radar_data` -- `backend/app/scheduler.py::scan_data_job` - -建议改造: - -在 `scan_radar_data()` 中,对新增、更新、删除的 `radar_data` 记录执行: - -1. 标记 `pairing_dirty_scenes` -2. 更新 `pairing_cache_state.status = DIRTY` -3. 根据变更规模决定是否立即增量重建 - -建议阈值: - -- 变更场景数 `<= SMALL_RECONCILE_LIMIT` - - 扫描结束后直接执行增量 reconcile -- 变更场景数 `> SMALL_RECONCILE_LIMIT` - - 仅标脏,不在扫描链路内同步重建 - - 由管理员在自检面板手动触发 - -这与当前 manual-only 模式兼容,不依赖后台常驻调度器。 - -### 6.2 启动期自维护 - -当前启动期会执行: - -- `ensure_database_ready()` -- `root_registry_service.sync_from_settings()` -- `manifest_inventory_service.sync_manifest_roots()` -- catalog bootstrap -- health check - -建议新增启动动作: - -- `pairing_service.bootstrap_pairing_cache_state()` - -只做轻量检查: - -- 新表是否存在 -- 是否有缓存状态行 -- `metric_version` 是否匹配当前版本 -- dirty scene 是否堆积 - -不建议启动即全量重建,避免启动时间失控。 - -### 6.3 数据库自动维护策略 - -当前数据库自维护具备两个模式: - -- 常规模式:建表、补列、应用 migration -- 开发机强制 reset 模式:整库 schema reset - -由于本轮是配对内核重构,且当前是开发机,推荐采用: - -#### 推荐落地策略 - -第一轮实施时直接执行一次开发机 schema reset。 - -原因: - -- 旧配对 SQL 函数与新缓存设计语义完全不同 -- 旧任务表中的配对数据没有保留价值 -- 旧结果可以通过 manifest inventory 和结果 catalog 重建 -- 源影像可以重新扫描 - -具体方式: - -- 合并新 ORM 与 migration 后 -- 启动前临时设置: - - `DB_SCHEMA_RESET_ON_MISMATCH=true` - - `DB_SCHEMA_RESET_CONFIRM=true` -- 启动一次后让 `ensure_database_ready()` 重建 schema -- 启动完成后恢复这两个环境变量为 `false` - -### 6.4 migration 文件策略 - -当前 `db_maintenance.py` 中 migration 顺序为: - -- `001_st_intersection_agg.sql` -- `002_spatial_functions.sql` -- `003_pairing_enhancement.sql` - -建议新增: - -- `004_pairing_refactor.sql` - -该 migration 负责: - -- 创建新 pairing 表 -- 创建必要索引 -- 创建候选边计算函数或 SQL helper view -- 如有需要,保留旧函数为 deprecated wrapper - -建议: - -- 不要在 `004` 里继续强化 `find_dinsar_pairs_v2` -- 将旧函数标记 deprecated,仅作为过渡壳 -- 新逻辑直接走新 service + 新表 - -## 7. 自检与一致性检测升级 - -### 7.1 Health 接口扩展 - -当前健康检查聚合点: - -- `backend/app/services/health_service.py::get_health_status` - -建议新增检查项: - -#### `pairing_system` - -字段建议: - -- `ok` -- `status` -- `metric_version` -- `scene_count` -- `pair_count` -- `dirty_scene_count` -- `network_run_count_last_7d` -- `last_full_rebuild_at` -- `last_incremental_reconcile_at` -- `last_error` -- `duplicate_reverse_pair_count` -- `orphan_metric_scene_ref_count` -- `task_without_network_ref_count` -- `result_without_pair_trace_count` - -判定规则建议: - -- `READY` 且无关键异常 -> `ok=true` -- `DIRTY` 但 dirty 数量较少 -> `ok=false`,级别 `warn` -- 存在重复反向边、孤儿引用、版本漂移 -> `ok=false`,级别 `error` - -### 7.2 Health 面板扩展 - -当前 `HealthCheckPanel.jsx` 已能展示结构化卡片。 - -建议新增卡片: - -- `配对系统` - -展示内容: - -- 缓存状态 -- 指标版本 -- 场景数 / 缓存对数 -- dirty scene 数 -- 最近全量 / 增量时间 -- 关键错误数 - -操作按钮建议: - -- `增量修复配对缓存` -- `全量重建配对缓存` -- `清理旧网络记录` - -### 7.3 Statistics 接口扩展 - -当前 `/statistics` 已承担一致性统计角色。 - -建议追加: - -- `pairing_consistency` - -字段建议: - -- `metric_cache_count` -- `duplicate_reverse_pair_count` -- `invalid_orientation_count` -- `network_edge_orphan_count` -- `task_orphan_count` -- `result_trace_missing_count` - -## 8. API 重构计划 - -### 8.1 后端接口 - -当前接口: - -- `POST /api/find-pairs` - -建议分两阶段改造。 - -#### Phase 1:保持前端可用 - -保留: - -- `POST /api/find-pairs` - -但内部改为: - -- 从 `pairing_metric_cache` 查询 -- 选择网络 -- 返回 `network_run_id + pairs` - -#### Phase 2:正式 API - -新增: - -- `POST /api/pairing/preview` -- `POST /api/pairing/networks/{network_run_id}/materialize-batch` -- `POST /api/pairing/reconcile-dirty` -- `POST /api/pairing/rebuild-cache` -- `GET /api/pairing/health` -- `GET /api/pairing/networks/{network_run_id}` - -### 8.2 请求模型 - -`PairingRequest` 应清理为正式语义: - -- 删除 `start_date` -- `aoi_overlap_threshold` 前端空值统一传 `null` -- `strategy` 继续保留: - - `all` - - `sequential` - - `star` - - `sbas` -- `reference_image_id` 仅在 `star` 时有效 - -### 8.3 返回模型 - -建议新增返回字段: - -- `network_run_id` -- `policy_version` -- `candidate_count` -- `selected_edge_count` -- `degraded` -- `warnings` - -## 9. 前端改造计划 - -涉及文件: - -- `frontend/src/components/PairingModal.jsx` -- `frontend/src/hooks/usePairingLogic.js` -- `frontend/src/store/pairingStore.js` - -改造原则: - -- 前端不再承载配对语义补丁 -- 所有“0 表示不限”“默认排序说明”等语义统一交回后端协议 -- 结果列表显示: - - `network_run_id` - - `strategy` - - `policy_version` - - warning - -界面调整建议: - -- `all` 策略文案改为“全部合法候选边,按稳定顺序展示” -- `star` 明确说明“参考像固定为主像” -- `sequential` 明确说明“同日多景按稳定键排序” -- AOI 覆盖阈值为空时传 `null` - -## 10. 旧代码处理策略 - -### 10.1 可以删除的内容 - -在新缓存路径稳定后,可以删除: - -- `spatial_service.find_dinsar_pairs()` 中当前 Python fallback -- 旧 `_apply_star_strategy()` 非固定角色实现 -- 旧 `start_date` 兼容逻辑 - -### 10.2 需要保留过渡一段时间的内容 - -- 旧前端 `find-pairs` 调用入口 -- 旧 `task_alias / pair_key` 命名辅助 - -但注意: - -- 命名只能作为展示与兼容字段 -- 不能继续作为算法主键 - -## 11. 实施阶段划分 - -### Phase 0:开发机切换准备 - -- 确认当前开发机允许 DB reset -- 记录 `.env` 中路径配置 -- 保留现有 `managed_roots` / `path_inventory` / manifest publish 目录,不做文件层清理 -- 准备新 migration 与 ORM - -验收: - -- 可以安全启动一次 schema reset - -### Phase 1:数据模型落地 - -- 新增 pairing 相关 ORM -- 新增 `004_pairing_refactor.sql` -- 接入 `ensure_database_ready()` -- 建立 `pairing_cache_state` 初始化逻辑 - -验收: - -- 后端启动后新表自动创建 -- 健康检查不报 schema 错 - -### Phase 2:候选缓存引擎 - -- 实现 `pairing_metric_cache` 全量构建 -- 实现 `pairing_dirty_scenes` -- 在 `scan_radar_data()` 中接入 dirty 标记 - -验收: - -- 能从 `radar_data` 构建稳定 pair cache -- 同日反向重复为 0 - -### Phase 3:网络选择器 - -- 重写 `all / sequential / star / sbas` -- 删除算法不等价 fallback -- 为 `find-pairs` 接口切换到新内核 - -验收: - -- 同一输入重复请求结果稳定 -- `star` 固定参考像角色 -- `sequential` 同日多景稳定 - -### Phase 4:任务物化与结果追踪 - -- `dinsar_task_items` 增加 network trace 字段 -- 结果 manifest / product profile 补充配对追踪字段 - -验收: - -- 任一结果可追溯到 network edge - -### Phase 5:自维护与自检 - -- 健康检查新增 `pairing_system` -- Health 面板新增配对卡片与修复按钮 -- Statistics 新增 pairing consistency - -验收: - -- 能显示 dirty scene 数、缓存状态、重复边异常 -- 能从 UI 触发增量修复与全量重建 - -### Phase 6:清理旧逻辑 - -- 删除旧 fallback -- 删除 `start_date` -- 下线旧语义说明 - -验收: - -- 配对链路只剩单一语义实现 - -## 12. 测试矩阵 - -本轮至少补以下自动化测试: - -### 12.1 候选缓存层 - -- 同日双景只生成单方向边 -- 同日多景生成顺序稳定 -- 不同轨向不生成边 -- 时间基线越界不入缓存 - -### 12.2 请求过滤层 - -- `allowed_satellites` 生效 -- `cross_satellite_pairing=false` 生效 -- `require_same_imaging_mode/polarization` 生效 -- `aoi_overlap_threshold=null` 与 `0.2` 行为不同 - -### 12.3 策略层 - -- `all` 顺序稳定 -- `sequential` 连接数正确 -- `star` 参考像固定为 master -- `sbas` 至少满足基础连通性 - -### 12.4 集成层 - -- 扫描新增源影像后 dirty scene 正确增加 -- 执行增量 reconcile 后 dirty scene 清零 -- `find-pairs` 返回 `network_run_id` -- `materialize-batch` 后任务项带 network trace - -### 12.5 自检层 - -- 重复边异常能被 health 检出 -- 孤儿网络边能被 health 检出 -- 任务无 network trace 能被 statistics 检出 - -## 13. 风险与取舍 - -### 13.1 本轮明确接受的取舍 - -- 开发机允许整库 reset -- 不迁移旧配对记录 -- 不保留旧 fallback - -### 13.2 需要重点控制的风险 - -- 首轮全量构建 pair cache 可能较慢 -- `radar_data.unique_id` 可能不稳定,需要一次性规范 -- 旧结果如果缺少 trace 字段,需要通过 manifest/profile 尽可能补齐 -- 若前端仍传 `0`,后端必须做强制归一化,不能再依赖 UI 自觉 - -## 14. 推荐实施顺序 - -建议严格按下面顺序推进: - -1. 先落表和缓存状态,不先改前端 -2. 再实现候选缓存和增量脏标记 -3. 再重写四种策略 -4. 再接任务物化追踪 -5. 最后接健康检查与自修复入口 - -不要反过来做: - -- 不要先改 UI 文案 -- 不要先修补 `find_dinsar_pairs_v2` -- 不要保留旧 fallback 到最后 - -## 15. 本轮结论 - -在当前代码基线上,最合理的重构路线不是继续维护“实时 SQL + Python fallback”模式,而是: - -- 用 `radar_data` 作为唯一事实层 -- 用 `pairing_metric_cache` 承载稳定候选对 -- 用 `pairing_network_runs / edges` 承载策略结果 -- 用 `pairing_cache_state / dirty_scenes` 承接自维护 -- 用现有 `HealthCheckPanel` 和 `/statistics` 承接自检 - -这样做的直接收益是: - -- 正确性可锁定 -- 结果可追溯 -- 与现有 DB 自维护、扫描链路、自检体系自然对接 -- 后续再接 SBAS、跨卫星和多引擎生产时不会继续堆积配对债务 diff --git a/docs/archive/PYINT_GAMMA_IMPLEMENTATION_TODO_20260418.md b/docs/archive/PYINT_GAMMA_IMPLEMENTATION_TODO_20260418.md deleted file mode 100644 index d66154c..0000000 --- a/docs/archive/PYINT_GAMMA_IMPLEMENTATION_TODO_20260418.md +++ /dev/null @@ -1,390 +0,0 @@ -# PyINT + Gamma 实施清单 - -更新日期:2026-04-18 - -关联设计文档: - -- [PYINT_GAMMA_INTEGRATION_DESIGN_20260418.md](PYINT_GAMMA_INTEGRATION_DESIGN_20260418.md) - -## 当前落地进度 - -- [x] 已完成第一批 `PyINT` 生产引擎接入:配置、引擎注册、任务队列、WSL 包装脚本、生产面板入口。 -- [x] 已完成 `PyINT` 运行目录规范化输出:生成 `.dinsar_run.json` 与 `pyint_run_summary.json`。 -- [x] 已完成 `PyINT` 基础环境检查:WSL、Python、`PYINT_HOME`、`pyintApp.py`、Gamma 命令可达性。 -- [x] 已将 `PyINT` 代码收编到仓库内 `third_party/PyINT`,不再依赖默认外部绝对路径。 -- [ ] 尚未完成 `PyINT` 结果目录自动发布兼容。当前原生输出已保存,但现有结果 catalog 仍主要面向 ENVI / ISCE2 栅格产物。 -- [ ] 尚未开始 `Gamma` 精配对后端与前端集成。 - -## 1. 文档定位 - -这份清单用于把 `PyINT` 生产引擎接入和 `Gamma` 精配对接入拆成可执行任务,作为后续实施顺序、联调顺序和验收顺序的统一依据。 - -本清单按以下原则编排: - -- 一期优先打通 `PyINT` 生产引擎 -- 二期再做 `Gamma` 精配对 MVP -- 一期不强制改数据库主结构 -- 运维自检只加状态,不把主要操作堆回健康页 - -## 2. 实施总顺序 - -推荐顺序: - -1. 先确认环境基线和配置项 -2. 先打通 `PyINT` 后端引擎与任务执行 -3. 再补结果归一化与目录扫描兼容 -4. 再补前端生产入口 -5. 然后做 `Gamma` 精配对 MVP -6. 最后补健康检查、烟测和治理 - -不建议顺序: - -- 先改数据库再写主流程 -- 先做健康页大改 -- 先把 `PyINT` 全部高级参数暴露到前端 - -## 3. Phase 0:环境基线确认 - -目标: - -- 确认当前机器上的 `PyINT + Gamma + WSL` 具备最小可执行条件 -- 把配置字段定清楚,但不把敏感信息写入仓库文档 - -### 任务 - -- [ ] 确认 `D:\Code\PyINT` 的实际可执行入口路径 -- [ ] 确认当前唯一 WSL distro 名称,默认与 `ISCE2_WSL_DISTRO` 对齐 -- [ ] 确认 WSL 中 `PyINT` 可用 Python 路径 -- [ ] 确认 `GAMMA_ENV_SCRIPT` 的实际路径 -- [ ] 确认 `base_calc` 在 WSL 中可执行 -- [ ] 确认 `pyintApp.py` 在 WSL 中可执行 -- [ ] 确认 `SCRATCHDIR` / `TEMPLATEDIR` / `DEMDIR` 对应的系统托管目录方案 -- [ ] 确认 PyINT 一期只支持的业务范围,建议锁定 `LT-1 + Gamma D-InSAR` - -### 涉及文件 - -- [ ] `backend/app/config.py` -- [ ] `.env.example` -- [ ] 根 `.env` 本机配置对照,不入库敏感值 - -### 阶段验收 - -- [ ] 可以给出完整的 PyINT 运行必需配置字段列表 -- [ ] 可以在 WSL 内成功跑通最小烟测命令 -- [ ] 不需要把管理员密码、邮箱密码、sudo 密码写入代码或文档 - -## 4. Phase 1:PyINT 后端引擎接入 - -目标: - -- 把 `PyINT` 作为新的 D-InSAR 引擎正式接入现有多引擎体系 - -### 4.1 配置层 - -- [ ] 在 `backend/app/config.py` 新增 `PYINT_*` 配置 -- [ ] 增加默认继承逻辑:`PYINT_WSL_DISTRO` 默认跟随 `ISCE2_WSL_DISTRO` -- [ ] 增加默认继承逻辑:`PYINT_WSL_PYTHON` 默认跟随 `ISCE2_PYTHON` -- [ ] 增加系统托管目录默认值: - - [ ] `PYINT_TEMPLATE_ROOT` - - [ ] `PYINT_WORK_ROOT` - - [ ] `PYINT_OUTPUT_ROOT` -- [ ] 在 `validate_runtime_config()` 中加入 PyINT 基础校验 -- [ ] 在 `.env.example` 中补齐非敏感 PyINT 配置示例 - -### 4.2 服务层 - -- [ ] 新增 `backend/app/services/pyint_service.py` -- [ ] 封装 WSL 执行逻辑,复用现有 `wsl_service.py` -- [ ] 实现 Windows 路径到 WSL 路径转换 -- [ ] 实现 PyINT 工作区目录初始化 -- [ ] 实现模板文件生成 -- [ ] 实现运行参数到模板字段的映射 -- [ ] 实现运行摘要 JSON 输出 -- [ ] 实现 PyINT 烟测函数 - -### 4.3 引擎层 - -- [ ] 新增 `backend/app/dinsar_engines/pyint_engine.py` -- [ ] 实现 `DinsarEngine` 接口 -- [ ] 定义 `engine_code=pyint` -- [ ] 定义一期唯一 profile,建议为 `lt1_gamma_dinsar` -- [ ] 定义最小参数 schema,避免一开始暴露过多 PyINT 原生参数 -- [ ] 实现 `check_available()` -- [ ] 实现 `run()` - -### 4.4 注册与任务调度 - -- [ ] 在 `backend/app/dinsar_engines/registry.py` 注册 `PyINT` -- [ ] 在 `backend/app/services/job_handlers.py` 增加 `JOB_TYPE_PYINT_RUN` -- [ ] 新增对应 handler -- [ ] 在 `backend/app/routers/dinsar_production.py` 允许 `engine_code=pyint` -- [ ] 让生产提交逻辑按 `pyint` 分派到新 job type - -### 4.5 WSL 包装脚本 - -- [ ] 新增 `backend/app/pyint_pipeline/run_lt1_pyint_pipeline.py` -- [ ] 负责把系统任务目录映射为 PyINT 项目目录 -- [ ] 负责设置 `SCRATCHDIR` / `TEMPLATEDIR` / `DEMDIR` -- [ ] 负责调用 `pyintApp.py` 或必要的细粒度 PyINT 脚本 -- [ ] 负责收集输出路径和运行摘要 - -### 涉及文件 - -- [ ] `backend/app/config.py` -- [ ] `backend/app/dinsar_engines/registry.py` -- [ ] `backend/app/dinsar_engines/pyint_engine.py` -- [ ] `backend/app/services/pyint_service.py` -- [ ] `backend/app/services/job_handlers.py` -- [ ] `backend/app/routers/dinsar_production.py` -- [ ] `backend/app/pyint_pipeline/run_lt1_pyint_pipeline.py` -- [ ] `.env.example` - -### 阶段验收 - -- [ ] `/dinsar-production/engines` 能返回 `pyint` -- [ ] `PyINT` 引擎可在后端被识别为可用/不可用 -- [ ] 可以成功提交一个 `pyint` 生产任务到队列 -- [ ] 任务日志、任务状态、错误信息可通过现有任务体系查看 - -## 5. Phase 2:PyINT 结果归一化与结果治理兼容 - -目标: - -- 保证 `PyINT` 输出能进入现有结果扫描、发布和 catalog 体系 - -### 任务 - -- [ ] 定义 PyINT 结果工作区与正式输出区的边界 -- [ ] 统一输出 bundle 元数据格式 -- [ ] 输出 engine/profile/run_key/task_name/pair trace 元数据 -- [ ] 补齐 pair 相关元数据: - - [ ] `pair_uid` - - [ ] `network_run_id` - - [ ] `network_edge_id` - - [ ] `policy_version` -- [ ] 让现有 `dinsar_scan_service` 可以识别 PyINT 结果 -- [ ] 验证现有 `result_catalog_service` 可处理 PyINT 产物 -- [ ] 验证桥接一致性逻辑不会把 PyINT 结果识别坏 - -### 涉及文件 - -- [ ] `backend/app/services/pyint_service.py` -- [ ] `backend/app/services/dinsar_scan_service.py` -- [ ] `backend/app/services/result_catalog_service.py` -- [ ] 可能涉及现有结果元数据写入辅助模块 - -### 阶段验收 - -- [ ] 跑完 PyINT 后可被系统扫描到 -- [ ] 可进入结果目录索引 -- [ ] 不影响现有 SARscape/ISCE2 结果扫描 - -## 6. Phase 3:前端生产页接入 PyINT - -目标: - -- 在现有生产页中把 `PyINT` 作为正式引擎展示和提交 - -### 任务 - -- [ ] 在 `frontend/src/DinsarProductionPanel.jsx` 中显示 `PyINT` 引擎卡片 -- [ ] 补充 `ENGINE_LABEL` / `TASK_TYPE_LABEL` -- [ ] 根据 `PyINT` profile 渲染参数输入项 -- [ ] 对不可用状态显示明确原因 -- [ ] 提交成功后沿用现有任务监控 -- [ ] 验证运行列表中能正确显示 `pyint` - -### 涉及文件 - -- [ ] `frontend/src/DinsarProductionPanel.jsx` -- [ ] `frontend/src/api/dinsarProduction.js` -- [ ] `frontend/src/utils/dinsarEngines.js` 如需要 - -### 阶段验收 - -- [ ] 前端可看到 `PyINT` -- [ ] 可提交 `PyINT` 任务 -- [ ] 可看到任务状态和日志 -- [ ] 不影响现有 SARscape/ISCE2 提交 - -## 7. Phase 4:Gamma 精配对 MVP - -目标: - -- 在现有配对规划体系上实现一版可用的 `Gamma` 精配对 - -### 7.1 后端能力 - -- [ ] 新增 `backend/app/services/pairing_refinement_service.py` -- [ ] 新增 `backend/app/services/gamma_pairing_service.py` 或同等职责模块 -- [ ] 实现基于现有 `network_run_id` 的场景集提取 -- [ ] 实现 PyINT/Gamma 配对工作区构建 -- [ ] 调用 `select_pairs.py` / `base_calc` -- [ ] 解析 `ifgram_list.txt` / baseline 输出 -- [ ] 生成新的 refined `network_run_id` -- [ ] 将精配对结果写入: - - [ ] `pairing_network_runs` - - [ ] `pairing_network_edges` - - [ ] `selection_meta_json` - -### 7.2 接口层 - -- [ ] 在 `backend/app/routers/pairing.py` 增加 `POST /pairing/refine-gamma` -- [ ] 设计请求体和响应体 -- [ ] 设计运行告警返回字段 -- [ ] 如需要,增加 refined artifacts 查询接口 - -### 7.3 存储策略 - -- [ ] 明确一期不改 `pairing_metric_cache` 语义 -- [ ] 明确只在 run/edge JSON 中落精配对元数据 -- [ ] 保留粗配对网络和精配对网络双轨并存 - -### 涉及文件 - -- [ ] `backend/app/routers/pairing.py` -- [ ] `backend/app/services/spatial_service.py` 如需复用 -- [ ] `backend/app/services/pairing_refinement_service.py` -- [ ] `backend/app/services/gamma_pairing_service.py` -- [ ] `backend/app/models/schemas.py` - -### 阶段验收 - -- [ ] 可基于一个已有 `network_run_id` 发起精配对 -- [ ] 返回新的 refined `network_run_id` -- [ ] 精配对结果可通过现有 network 查询接口查看 -- [ ] 不破坏原粗配对结果 - -## 8. Phase 5:前端配对规划页接入 Gamma 精配对 - -目标: - -- 在配对规划页提供精配对入口和结果摘要 - -### 任务 - -- [ ] 在 `frontend/src/panels/PairPlanningPanel.jsx` 新增 `Gamma 精配对` 区块 -- [ ] 展示当前粗配对网络摘要 -- [ ] 增加发起精配对按钮 -- [ ] 展示精配对结果摘要 -- [ ] 展示粗配对与精配对差异提示 -- [ ] 增加“采用哪一版网络继续生产”的状态表达 - -### 涉及文件 - -- [ ] `frontend/src/panels/PairPlanningPanel.jsx` -- [ ] `frontend/src/api/pairing.js` - -### 阶段验收 - -- [ ] 管理员可在配对规划页发起精配对 -- [ ] 能看到 refined 结果摘要 -- [ ] 不需要进入健康检查页做配对操作 - -## 9. Phase 6:运维自检与烟测补齐 - -目标: - -- 让 PyINT/Gamma 的环境状态可被健康检查观察 - -### 任务 - -- [ ] 在 `backend/app/services/health_service.py` 中纳入 PyINT 检查 -- [ ] 检查项至少包括: - - [ ] `PYINT_ENABLED` - - [ ] distro 可访问 - - [ ] WSL Python 可执行 - - [ ] `pyintApp.py` 存在 - - [ ] `GAMMA_ENV_SCRIPT` 存在 - - [ ] `base_calc` 可执行 - - [ ] 模板目录可读 - - [ ] 工作目录可写 -- [ ] 增加管理员烟测接口 -- [ ] 前端健康页只展示状态摘要,不加复杂操作区 - -### 涉及文件 - -- [ ] `backend/app/services/health_service.py` -- [ ] `backend/app/routers/dinsar_production.py` -- [ ] `frontend/src/HealthCheckPanel.jsx` - -### 阶段验收 - -- [ ] 健康页可看到 PyINT 状态 -- [ ] 可区分“引擎不可用”和“系统整体故障” -- [ ] 不把精配对主操作入口放回健康页 - -## 10. Phase 7:可选数据库结构化增强 - -目标: - -- 只有在业务确认需要更强的历史与运维管理时才进入本阶段 - -### 进入条件 - -- [ ] 需要独立查询精配对历史 -- [ ] 需要统计精配对失败率 -- [ ] 需要管理精配对 artifacts 生命周期 -- [ ] 需要构建更完整的后台管理页 - -### 任务 - -- [ ] 设计 `pairing_refinement_runs` 等新表 -- [ ] 新增迁移文件,例如 `007_pyint_gamma_integration.sql` -- [ ] 在 `backend/app/db_maintenance.py` 中加入迁移列表 -- [ ] 验证 `ensure_database_ready()` 启动自动迁移 -- [ ] 验证幂等执行 - -### 阶段验收 - -- [ ] 新表结构不破坏现有 pairing 逻辑 -- [ ] 启动时可自动应用迁移 -- [ ] 老数据和老接口保持兼容 - -## 11. 联调与验收矩阵 - -### 后端 - -- [ ] `py_compile` 或等价语法检查通过 -- [ ] 新增路由可正常注册 -- [ ] 新增引擎可正常列出 -- [ ] 任务队列能执行 `PyINT` -- [ ] 精配对接口能生成 refined network - -### 前端 - -- [ ] `npm run build` 通过 -- [ ] 生产页能显示 `PyINT` -- [ ] 配对规划页能显示 `Gamma 精配对` -- [ ] 健康页能显示 PyINT 状态 - -### 集成 - -- [ ] `PyINT` 单任务最小链路跑通 -- [ ] 结果能被系统扫描 -- [ ] 精配对 MVP 跑通 -- [ ] 现有 SARscape/ISCE2 不回归 - -## 12. 当前明确不做 - -- [ ] 一期不把 PyINT 的全部模板参数开放到前端 -- [ ] 一期不接入 GACOS 自动邮箱下载链路 -- [ ] 一期不接入 POT、phase bias、完整时序 MintPy 流程 -- [ ] 一期不改写现有 `pairing_metric_cache` 字段语义 -- [ ] 一期不在健康检查页增加主操作面板 -- [ ] 一期不做多 WSL distro 管理 - -## 13. 当前建议的首批落地包 - -建议第一轮直接落以下内容: - -- [ ] `config.py` + `.env.example` 的 `PYINT_*` 配置 -- [ ] `pyint_service.py` -- [ ] `pyint_engine.py` -- [ ] `registry.py` 注册 -- [ ] `job_handlers.py` 的 `JOB_TYPE_PYINT_RUN` -- [ ] `dinsar_production.py` 的 `pyint` 提交分派 -- [ ] `run_lt1_pyint_pipeline.py` -- [ ] `DinsarProductionPanel.jsx` 的 `PyINT` 引擎展示与提交 - -这批完成后,再进入 `Gamma` 精配对 MVP。 diff --git a/docs/archive/PYINT_LT1_COREG_ORBIT_HYPOTHESIS_EXPERIMENT_20260420.md b/docs/archive/PYINT_LT1_COREG_ORBIT_HYPOTHESIS_EXPERIMENT_20260420.md deleted file mode 100644 index e5c1e98..0000000 --- a/docs/archive/PYINT_LT1_COREG_ORBIT_HYPOTHESIS_EXPERIMENT_20260420.md +++ /dev/null @@ -1,343 +0,0 @@ -# PyINT LT-1 `coreg` 失败轨道假设验证实验 - -**日期**: 2026-04-20 -**状态**: 实验设计 -**目标问题**: 验证当前 LT-1 在 PyINT/Gamma 中 `coreg/init_offsetm` 失败,是否主要由 `par_LT1_SLC` 导入后的 orbit/state vector 处理缺失导致 - -## 1. 结论先行 - -这次实验不再重复验证配对逻辑,也不再重复验证 `Task_*` 路径组织、DEM 来源或多景输入形态。 - -本实验只回答一个更窄的问题: - -> 在保持同一批 LT-1 影像、同一 master、同一 DEM、同一 `coreg` 参数不变的前提下,只改变 `.slc.par` 中的 state vector 处理方式,是否会显著改变 `coreg/init_offsetm` 的失败行为。 - -如果答案是“会”,则当前问题主要集中在 LT-1 导入后的轨道几何链条。 -如果答案是“不会”,则需要把排查重点转回 LT-1 导入本身的几何建模或 `MLI/SLC` 生成环节。 - -## 2. 已知事实 - -当前已经有一个可复现的 3 景隔离实验环境: - -- 实验根目录: `D:\PyINT_POOL_TEST\LT1A_MONO_SYC_STRIP1_E129.6_N45.0_3scene` -- master: `20230726` -- slave: - - `20230624` - - `20230920` -- 已确认成功的阶段: - - `down2slc_all` - - `makedem_pyint` - - `generate_rdc_dem` -- 已确认失败的阶段: - - `coreg_gamma_all` -- 当前稳定失败特征: - - `init_offsetm failed` - - `ERROR: number of zero values ... exceeds threshold: 32768` - -这说明: - -1. 输入组织已经足够让 PyINT 跑到 `coreg`。 -2. DEM 不构成这轮失败的主因。 -3. 问题更像是 `coreg` 所依赖的几何输入有问题,尤其是 `.slc.par` 的 orbit/state vector 链条。 - -## 3. 实验假设 - -### H1: 当前主假设 - -`par_LT1_SLC` 导入后的 `.slc.par` 还需要额外的 orbit/state vector 处理。 - -这个处理至少可能包括两类: - -1. 对现有 state vectors 做平滑/过滤 -2. 用系统已有 LT-1 精密轨道 TXT 重写 state vectors,再做校验 - -### H0: 零假设 - -即使对 state vectors 做上述处理,`coreg/init_offsetm` 的失败模式也基本不变。 -若如此,则缺陷更可能位于: - -- LT-1 导入后的几何参数生成 -- `MLI` 生成质量 -- `deskew` / 时序 / 采样参数 -- 或 `par_LT1_SLC` 本身不适用于当前这批数据 - -## 4. 实验原则 - -本实验必须严格控制变量,避免再把多种问题混在一起。 - -固定不变的部分: - -- 同一批 3 景 LT-1 数据 -- 同一个 master: `20230726` -- 同一套 `range_looks=2`, `azimuth_looks=2` -- 同一个 DEM 数据源 -- 同一个 `coreg_gamma.py` -- 同一个 PyINT/Gamma 环境 - -唯一允许变化的部分: - -- `.slc.par` 内 state vector 的处理方式 - -不在本轮变化范围内: - -- 不切换配对策略 -- 不切换 DEM -- 不改 `coreg rescue` -- 不改 `select_pairs` -- 不重做新的场景池选择 - -## 5. 分组设计 - -### A 组: 基线组 - -目的: 复现当前失败,作为所有对照的基准。 - -处理方式: - -- 使用 `par_LT1_SLC` 当前直接生成的 `.slc.par` -- 不做任何 orbit/state vector 改写 -- 重新执行: - - `generate_rdc_dem` - - `coreg` 到每个 slave - -预期: - -- 与现有日志一致 -- 两个 slave 都在 `init_offsetm` 附近失败 - -### B 组: 仅做 Gamma orbit filter - -目的: 验证“问题是否只是 state vector 需要过滤/平滑,而不一定需要外部精轨替换”。 - -处理方式: - -- 在 A 组同源 `.slc.par` 副本上,仅对 state vectors 做 Gamma orbit filtering -- 不引入系统外部 LT-1 精密轨道 TXT -- 之后重新执行: - - `generate_rdc_dem` - - `coreg` - -判定意义: - -- 如果 B 组明显优于 A 组,说明 `par_LT1_SLC` 的原始 state vectors 质量不足,但问题可能主要是“需要轨道过滤” -- 如果 B 组与 A 组几乎一样失败,则仅做过滤不够 - -### C 组: 精密轨道重写组 - -目的: 验证“问题是否是导入后缺少外部精密轨道替换”。 - -处理方式: - -- 使用系统已有 LT-1 精密轨道 TXT -- 通过 [apply_lt1_precise_orbit.py](/D:/Code/Insar_management_system_v2/backend/app/pyint_pipeline/apply_lt1_precise_orbit.py) 重写 `.slc.par` 的 `state_vector_*` -- 插值目标时间栅格沿用 `.slc.par` 自身: - - `number_of_state_vectors` - - `time_of_first_state_vector` - - `state_vector_interval` -- 重写后重新执行: - - `generate_rdc_dem` - - `coreg` - -判定意义: - -- 如果 C 组明显优于 A 组,而 B 组没有明显改善,则说明问题更偏向“缺少精密轨道重写” -- 如果 C 组和 B 组都改善,则说明“导入后的轨道链条不完整”成立,但是否必须引入外部精轨还需进一步量化 - -### D 组: 精密轨道重写后校验组 - -目的: 观察“精轨重写后,Gamma 自身的 spline 校验是否仍给出大修正量”。 - -处理方式: - -- 先执行 C 组重写 -- 再调用 `ORB_filt_spline.py` 生成验证副本 -- 不一定把验证副本作为正式输入使用,先记录校验结果 - -判定意义: - -- 如果校验修正量很小,说明 C 组重写后的 state vectors 与 Gamma 的平滑约束基本一致 -- 如果校验修正量仍然很大,说明即使引入精轨,state vector 时间栅格或插值方式仍可能有问题 - -## 6. 实验执行路径 - -推荐直接复用现有 3 景实验目录,不重新拷贝数据: - -- 基础目录: `D:\PyINT_POOL_TEST\LT1A_MONO_SYC_STRIP1_E129.6_N45.0_3scene` -- 现有日志目录: `D:\PyINT_POOL_TEST\LT1A_MONO_SYC_STRIP1_E129.6_N45.0_3scene\logs` -- 现有 SLC 目录: `D:\PyINT_POOL_TEST\LT1A_MONO_SYC_STRIP1_E129.6_N45.0_3scene\pyint_stage\SLC` - -推荐把每个实验组做成并列工作副本,例如: - -```text -D:\PyINT_POOL_TEST\LT1A_MONO_SYC_STRIP1_E129.6_N45.0_3scene_cases\ - case_A_baseline\ - case_B_orb_filt\ - case_C_precise_orbit_rewrite\ - case_D_precise_orbit_validate\ -``` - -每个 case 必须从同一份 `down2slc_all` 完成后的结果拷贝出来,避免导入过程本身再次引入差异。 - -## 7. 每组执行顺序 - -### Step 1: 冻结基线输入 - -先选取一个共同起点: - -- `down2slc_all` 已完成 -- `makedem_pyint` 已完成 -- `coreg` 尚未执行,或清理已有 `coreg` 中间产物 - -这样所有 case 都基于同一份: - -- `SLC/*.slc` -- `SLC/*.slc.par` -- `MLI` -- `DEM` - -### Step 2: 仅修改 `.slc.par` - -按分组分别对 `.slc.par` 做处理: - -- A 组: 不改 -- B 组: 仅 filter -- C 组: 精轨重写 -- D 组: 精轨重写后再做 Gamma 校验 - -注意: - -- 不要重新导入原始压缩包 -- 不要改模板参数 -- 不要改 `ifgram_list` - -### Step 3: 重做 `generate_rdc_dem` - -因为 `rdc_dem` 与几何参数耦合,改完 `.slc.par` 后必须重做一次。 - -### Step 4: 单独跑每个 slave 的 `coreg` - -推荐不要一开始就跑 `coreg_gamma_all.py`,而是先分别跑: - -- `20230624` -- `20230920` - -这样更容易判断某个处理是否对所有 slave 都有效。 - -### Step 5: 收集指标 - -每个 case 对每个 slave 都产出独立日志,并汇总到统一表格。 - -## 8. 观测指标 - -本实验不能只看“是否跑通”,还要看失败形态是否发生了有意义的变化。 - -核心指标: - -1. `coreg` 是否成功进入下一阶段 -2. `init_offsetm` 是否仍失败 -3. 报错中的 zero-value patch 数量 -4. 失败位置是否仍固定在 `init_offsetm` -5. 是否产生有效的 `RSLC` - -轨道相关指标: - -1. 每个 `.slc.par` 改写前后的 state vector 位置差范数 -2. 每个 `.slc.par` 改写前后的 state vector 速度差范数 -3. `ORB_filt_spline.py` 若运行成功,其输出副本相对输入的修正量 - -结果质量指标: - -1. 如果 `coreg` 成功,后续 `diff/cor` 是否仍全 0 -2. `diff_filt.zero_ratio` -3. `cor.zero_ratio` - -## 9. 判定标准 - -### 支持主假设的证据 - -以下任一情况都可视为强支持: - -1. B 组或 C 组能让两个 slave 中至少一个从 `init_offsetm` 失败变成成功 -2. B 组或 C 组虽然仍失败,但 zero-value patch 数量显著下降 -3. C 组优于 B 组,说明外部精轨重写比单纯过滤更关键 - -### 反对主假设的证据 - -以下情况说明当前假设不足: - -1. A/B/C/D 四组都在同一位置、以近似相同错误失败 -2. 改写前后 state vector 差异很大,但 `coreg` 行为几乎不变 -3. `ORB_filt_spline.py` 校验显示修正量不大,但 `coreg` 仍完全失败 - -若出现这些情况,下一轮应优先排查: - -- `par_LT1_SLC` 生成的几何字段是否本身错误 -- `MLI` 数据中大面积零值的真实来源 -- LT-1 导入后的采样/deskew/时序链条 - -## 10. 推荐的最小可执行版本 - -如果想尽快验证,不必一次把四组都跑全,先做最小闭环: - -1. A 组: 当前基线 -2. C 组: 精密轨道重写 -3. D 组: 精密轨道重写后做 `ORB_filt_spline.py` 校验 - -理由: - -- A 组已经稳定存在 -- C 组最直接验证“外部精轨重写是否必要” -- D 组可以帮助判断“即便重写了,Gamma 仍不认可的程度有多大” - -B 组可以作为补充组,用来区分“只需过滤”还是“必须引入精轨”。 - -## 11. 执行前提 - -执行本实验前,需要确认: - -1. WSL 中可调用 Gamma 工具 -2. `apply_lt1_precise_orbit.py` 可以访问本次实验所需的 LT-1 精密轨道 TXT -3. `ORB_filt_spline.py` 如 shebang 不可用,则通过明确的 Python 解释器调用 -4. 每个 case 使用独立日志目录,避免覆盖 - -## 12. 风险与注意事项 - -1. 不能在同一个 `SLC` 目录上反复覆盖做多组实验,否则会污染基线 -2. 改写 `.slc.par` 后如果不重做 `generate_rdc_dem`,实验结论不可靠 -3. 这轮实验只验证“轨道处理是不是主因”,不等于验证“最终科学结果已经正确” -4. 即使某组 `coreg` 成功,也必须继续检查后续 `diff/cor` 是否仍然全 0 - -## 13. 实验产出物 - -建议每组最终至少保留: - -- 处理后的 `.slc.par` -- `generate_rdc_dem` 日志 -- 每个 slave 的 `coreg` 日志 -- 一个汇总 JSON 或 Markdown 表 - -建议的汇总字段: - -```json -{ - "case": "case_C_precise_orbit_rewrite", - "master": "20230726", - "slave": "20230920", - "state_vector_mode": "precise_orbit_rewrite", - "coreg_success": false, - "failed_stage": "init_offsetm", - "zero_patch_count": 190155, - "rslc_exists": false, - "orb_validation_status": "not_run" -} -``` - -## 14. 推荐下一步 - -推荐按下面顺序执行: - -1. 先在现有 3 景实验目录上做 A/C/D 三组 -2. 如果 C 组明显改善,再补 B 组区分“过滤”与“精轨重写”的贡献 -3. 如果 A/B/C/D 全部失败,再正式转向 `par_LT1_SLC` 输出几何字段和 `MLI` 零值来源排查 - -这轮实验的价值不在于立刻修好流程,而在于把问题边界收窄到“轨道链条”还是“导入几何链条”。 diff --git a/docs/archive/PYINT_LT1_DEM_GEOMETRY_CHAIN_EXPERIMENT_20260420.md b/docs/archive/PYINT_LT1_DEM_GEOMETRY_CHAIN_EXPERIMENT_20260420.md deleted file mode 100644 index ee4a67c..0000000 --- a/docs/archive/PYINT_LT1_DEM_GEOMETRY_CHAIN_EXPERIMENT_20260420.md +++ /dev/null @@ -1,308 +0,0 @@ -# PyINT LT-1 DEM 几何链定位实验 - -**日期**: 2026-04-20 -**状态**: 实验设计 -**目标问题**: 验证 LT-1 在 PyINT/Gamma 中 `coreg/init_offsetm` 失败,是否由 DEM 本体问题引起,还是由 DEM 参与的几何映射链条引起 - -## 1. 结论先行 - -这轮实验不再直接回答“轨道是不是问题”,而是专门回答下面这个问题: - -> `init_offsetm` 报错里的大量 `zero values in MLI1 image patch`,究竟是 DEM 文件本身有问题,还是 `DEM -> rdc_trans -> geocode -> mli0` 这一条几何映射链条有问题。 - -当前更值得优先验证的是: - -1. `HGTSIM` 是否本身就存在异常空洞或覆盖错误 -2. `lt0` 是否把参考图映射到了错误位置 -3. `mli0` 的中心 `512 x 512` patch 是否天然就是大面积 0 -4. `mli0` 与 `Samp` 的有效重叠区是否根本不在图像中心 - -## 2. 已知事实 - -当前 `coreg_gamma.py` 的关键顺序是: - -- `rdc_trans` -- `geocode` -- `create_diff_par` -- `init_offsetm mli0 Samp diff0 1 1` - -也就是说,`init_offsetm` 当前比较的是: - -- `MLI1 = mli0` -- `MLI2 = Samp` - -而不是直接比较 DEM。 - -已完成的 A/C/D 轨道实验说明: - -1. 只改 state vector,`init_offsetm` 的 zero-count 会下降 -2. 但下降后仍然失败 -3. `ORB_filt_spline.py` 已经基本认可重写后的轨道 - -这说明: - -- 轨道确实影响几何 -- 但当前失败不太像“只有轨道问题” -- DEM 相关的几何映射链条值得单独定位 - -## 3. 三类假设 - -### H1: DEM 本体问题 - -DEM 文件本身有问题,例如: - -- 覆盖范围不对 -- 裁剪窗口不对 -- sidecar / 投影信息不对 -- 高程值大面积异常或空洞 - -若 H1 成立,则更换 DEM 源后应显著改变 `HGTSIM` 和 `mli0` 的空洞模式。 - -### H2: DEM 几何链问题 - -DEM 文件本身可用,但 LT-1 导入几何、轨道或 `generate_rdc_dem` 中间步骤有问题,导致: - -- `rdc_trans` 查找表偏移 -- `geocode` 后的 `mli0` 被映射到错误位置 -- `mli0` 中心 patch 与 `Samp` 根本不重叠 - -若 H2 成立,则即使更换 DEM,本质失败形态也可能不变;但改变轨道或几何参数时,`mli0` 的零值分布会跟着变化。 - -### H3: patch 选取问题 - -`mli0` 与 `Samp` 不是完全不重叠,而是图像中心不是有效重叠区。 - -若 H3 成立,则: - -- 全图并非都坏 -- 换 `rpos/azpos` 后,`init_offsetm` 可能在其他 patch 上能工作 - -## 4. 实验原则 - -这轮实验尽量不跑整条生产链,只盯 `init_offsetm` 之前的中间产物。 - -固定不变: - -- 同一批 3 景 LT-1 数据 -- 同一个 master: `20230726` -- 同一套 looks 参数 -- 同一份 PyINT/Gamma 环境 - -允许变化: - -- DEM 来源 -- 是否启用精轨重写 -- `init_offsetm` 的 patch 位置 - -优先观测对象: - -- `HGTSIM` -- `lt0` -- `mli0` -- `Samp` -- `diff0` - -## 5. 分层实验设计 - -### Layer 1: 不改 DEM,先看中间产物 - -目的: 判断当前 DEM 链条到底在哪一步开始“空掉”。 - -#### 组 L1-A: 基线组 - -使用当前已经失败的 case,导出并统计: - -- `HGTSIM` -- `lt0` -- `mli0` -- `Samp` - -对每个文件都做: - -1. 全图零值比例 -2. 中心 `512 x 512` patch 零值比例 -3. 中心 patch 的最小值、最大值、均值 -4. 快速可视化图 - -判定意义: - -- 如果 `HGTSIM` 自身就明显异常,优先怀疑 DEM 或 `generate_rdc_dem` -- 如果 `HGTSIM` 正常而 `mli0` 异常,优先怀疑 `lt0/geocode` -- 如果 `mli0` 正常但中心 patch 不在有效重叠区,优先怀疑 patch 选取 - -#### 组 L1-B: 精轨重写对照组 - -复用已经跑过的精轨重写 case,只比较: - -- `HGTSIM` -- `mli0` -- `Samp` - -判定意义: - -- 如果只改轨道,`mli0` 的零值分布就跟着变,说明问题不在 DEM 文件本体 -- 如果 `HGTSIM` 也明显变化,说明 DEM 映射结果强依赖 `.slc.par` 几何 - -### Layer 2: 改 DEM 源,固定几何 - -目的: 判断 DEM 文件本身是否是主因。 - -#### 组 L2-A: 当前 DEM - -使用当前 `prepared_dem_source`,作为基线。 - -#### 组 L2-B: 替代 DEM - -推荐优先选一个同区域、同分辨率级别、不同来源的 DEM,例如: - -- 已有的另一份系统 DEM -- 或成功 ENVI/IDL 任务中使用过的 DEM - -要求: - -- 不改 `.slc.par` -- 不改轨道 -- 只重跑 `makedem_pyint -> generate_rdc_dem` - -判定意义: - -- 如果换 DEM 后 `HGTSIM/mli0` 明显改善,DEM 本体有嫌疑 -- 如果几乎不变,DEM 本体不是主因 - -#### 组 L2-C: 合成平坦 DEM - -用一个覆盖相同区域、常数高程的测试 DEM。 - -这组不是为了出正确结果,而是为了测试: - -- 失败是否强依赖真实地形起伏 -- 还是只要进入几何映射链就已经错位 - -判定意义: - -- 如果平坦 DEM 仍在同一位置失败,说明不是高程细节导致 -- 如果平坦 DEM 反而明显改善,则真实 DEM 参与的映射可能存在投影或裁剪问题 - -### Layer 3: 不改 DEM,扫描 patch 位置 - -目的: 判断中心 patch 是否只是选错了地方。 - -方法: - -- 保持 `mli0`、`Samp`、`diff0` 不变 -- 只改变 `init_offsetm` 的: - - `rpos` - - `azpos` -- 在图像中心周围做稀疏网格扫描 - -推荐: - -- 先做 `5 x 5` 或 `7 x 7` 网格 -- patch 大小仍保持默认 `512` - -每个点记录: - -- 是否报 zero-patch 错误 -- zero-count -- 是否能进入下一步 - -判定意义: - -- 如果某些位置能通过,说明不是整幅图都坏,而是中心 patch 选取不对 -- 如果所有位置都报大面积 0,更像几何链整体错位 - -## 6. 关键观测指标 - -### DEM 相关 - -1. `HGTSIM` 是否生成成功 -2. `HGTSIM` 的全图与中心 patch 零值比例 -3. `HGTSIM` 是否存在明显空洞、条带或边界错切 - -### 几何映射相关 - -1. `lt0` 是否可用 -2. `mli0` 的全图与中心 patch 零值比例 -3. `mli0` 与 `Samp` 的有效像元重叠比例 -4. `mli0` 与 `Samp` 是否在中心 patch 上具有相似纹理 - -### `init_offsetm` 相关 - -1. 是否仍失败在同一位置 -2. zero-count 是否显著下降 -3. 换 patch 位置后是否存在可工作区域 - -## 7. 推荐输出物 - -每轮实验至少产出: - -- 中间产物清单 -- 每个文件的统计 JSON -- 快速可视化 PNG/TIF -- 一张对照表 - -推荐的统计字段: - -```json -{ - "case": "L1-A_baseline", - "file": "mli0", - "width": 0, - "lines": 0, - "global_zero_ratio": 0.0, - "center_patch_zero_ratio": 0.0, - "center_patch_size": 512, - "nonzero_overlap_with_samp": 0.0 -} -``` - -## 8. 判定标准 - -### 支持“DEM 本体问题”的证据 - -1. 更换 DEM 后,`HGTSIM` 和 `mli0` 的空洞模式大幅变化 -2. 同一套几何下,只有某一份 DEM 会触发大面积零值 -3. 合成平坦 DEM 能显著改善中心 patch - -### 支持“DEM 几何链问题”的证据 - -1. `HGTSIM` 看起来基本正常,但 `mli0` 大面积为 0 -2. 只改轨道或 `.slc.par`,`mli0` 零值分布就发生变化 -3. 换 DEM 后失败模式基本不变 - -### 支持“patch 选取问题”的证据 - -1. 中心 patch 失败,但偏移后的 patch 可以工作 -2. `mli0` 与 `Samp` 在全图上存在局部重叠区,只是中心不对 - -## 9. 推荐的最小闭环 - -不建议一上来就换很多 DEM。最小闭环应按这个顺序: - -1. 先做 Layer 1 - - 对现有基线 case 和精轨重写 case 提取 `HGTSIM/lt0/mli0/Samp` 统计和快视图 -2. 再做 Layer 3 - - 扫描 `init_offsetm` 的 `rpos/azpos` -3. 只有在 Layer 1/3 仍无法判断时,再做 Layer 2 换 DEM - -原因: - -- 这能先区分“DEM 文件坏了”与“中心 patch 选错了” -- 也能避免过早把问题全部甩给 DEM - -## 10. 推荐下一步 - -推荐直接执行两个子实验: - -1. 中间产物审计实验 - - 把 A 组和 C 组的 `HGTSIM/lt0/mli0/Samp` 取出来做零值统计和快视图 -2. `init_offsetm` patch 扫描实验 - - 在同一 case 上只扫描 `rpos/azpos` - -如果这两步做完后发现: - -- `mli0` 全图都坏,再优先查 `generate_rdc_dem` 和 LT-1 导入几何 -- 只有中心 patch 坏,再优先查 patch 选取 -- 换 DEM 才有明显改善,再回到 DEM 本体问题 - -这轮实验的目的不是立刻修好 `coreg`,而是把“DEM 文件问题”和“DEM 几何链问题”从概念判断变成可测的证据链。 diff --git a/docs/archive/PYINT_LT1_DEM_SOURCE_EXPERIMENT_RESULTS_20260420.md b/docs/archive/PYINT_LT1_DEM_SOURCE_EXPERIMENT_RESULTS_20260420.md deleted file mode 100644 index c256f30..0000000 --- a/docs/archive/PYINT_LT1_DEM_SOURCE_EXPERIMENT_RESULTS_20260420.md +++ /dev/null @@ -1,158 +0,0 @@ -# PyINT LT-1 DEM Source Experiment Results - -**日期**: 2026-04-20 -**状态**: 已执行 -**目标问题**: `init_offsetm` 失败是否主要由 DEM 本体来源导致 - -## 1. 前置结论 - -在这轮 DEM 源对照之前,已经完成 `init_offsetm` patch 扫描。 - -- 基线 run root: - - `D:\PyINT_POOL_TEST\LT1A_MONO_SYC_STRIP1_E129.6_N45.0_3scene_cases\run_20260420T093322Z` -- 扫描结论: - - 更换 `rpos/azpos` 可以把中心 patch 的零值数从 `184099/190155` 降到约 `168322/166114` - - 但仍远高于 `32768` 阈值 - - 没有任何候选 patch 通过 `init_offsetm` - -因此,这里把“中心 patch 选错”降级为次要因素,继续验证 DEM 源本体是否主导失败。 - -## 2. 实验设计 - -固定条件: - -- 同一组 3 景 LT-1 数据 -- 同一 master: `20230726` -- 同一 baseline orbit 几何 -- 同一 PyINT/Gamma 处理链 - -只更换 DEM 来源: - -1. `COPDEM` - - `prepared_dem_source=/mnt/d/DEM/COPDEM_GLO30_China_4326_DEM` -2. `GMTED2010` - - `prepared_dem_source=/mnt/d/DEM/GMTED2010.jp2` -3. `OpenTopography SRTMGL1` - - 走 PyINT 默认下载路径 - -执行链路: - -- `makedem_pyint` -- `generate_rdc_dem` -- `coreg_gamma` -- `audit_lt1_dem_geometry_chain.py` - -## 3. 实验目录 - -本地 DEM 对照: - -- run root: - - `D:\PyINT_POOL_TEST\LT1A_MONO_SYC_STRIP1_E129.6_N45.0_3scene_dem_cases\run_20260420T152607Z` -- 审计输出: - - `D:\PyINT_POOL_TEST\LT1A_MONO_SYC_STRIP1_E129.6_N45.0_3scene_dem_cases\run_20260420T152607Z\audit_dem_geometry` - -OpenTopography SRTMGL1: - -- 首次尝试: - - `D:\PyINT_POOL_TEST\LT1A_MONO_SYC_STRIP1_E129.6_N45.0_3scene_dem_cases\run_20260420T170406Z` - - 失败原因: WSL `isce2` 环境缺少 `rasterio` -- 安装 `rasterio` 后重跑: - - `D:\PyINT_POOL_TEST\LT1A_MONO_SYC_STRIP1_E129.6_N45.0_3scene_dem_cases\run_20260420T220524Z` - - 审计输出: - - `D:\PyINT_POOL_TEST\LT1A_MONO_SYC_STRIP1_E129.6_N45.0_3scene_dem_cases\run_20260420T220524Z\audit_dem_geometry` - -## 4. 结果摘要 - -### 4.1 `coreg/init_offsetm` zero-count - -`20230624`: - -- `COPDEM`: `184099` -- `GMTED2010`: `262033` -- `SRTMGL1`: `183582` - -`20230920`: - -- `COPDEM`: `190155` -- `GMTED2010`: `262070` -- `SRTMGL1`: `189571` - -阈值均为 `32768`。 - -### 4.2 `HGTSIM` / `lt0` / `mli0_samp_overlap` - -`COPDEM`: - -- `hgtsim zero_ratio`: `0.967266` -- `lt0 valid_pair_ratio`: `0.032734` -- `20230624 center overlap`: `0.297718` -- `20230920 center overlap`: `0.274616` - -`GMTED2010`: - -- `hgtsim zero_ratio`: `0.999964` -- `lt0 valid_pair_ratio`: `0.000036` -- `20230624 center overlap`: `0.000423` -- `20230920 center overlap`: `0.000282` - -`SRTMGL1`: - -- `hgtsim zero_ratio`: `0.967264` -- `lt0 valid_pair_ratio`: `0.032736` -- `20230624 center overlap`: `0.299690` -- `20230920 center overlap`: `0.276844` - -## 5. 结论 - -### 5.1 当前 `COPDEM` 不是主要故障源 - -`SRTMGL1` 作为更接近原始 PyINT 默认路径的下载 DEM,跑出来的几何指标与当前 `COPDEM` 几乎一致: - -- `hgtsim zero_ratio` 基本相同 -- `lt0 valid_pair_ratio` 基本相同 -- `mli0_samp_overlap` 基本相同 -- `init_offsetm zero-count` 只改善了几百个像素,量级上没有本质变化 - -这说明: - -- 把当前系统 DEM 替换成原始 PyINT 默认下载 DEM -- 并不能把问题从 `184k/190k` 拉到 `32768` 阈值附近 - -### 5.2 更差的 DEM 会进一步恶化问题 - -`GMTED2010` 把几何链几乎压成全零: - -- `hgtsim zero_ratio` 逼近 `1.0` -- `lt0 valid_pair_ratio` 下降到 `0.000036` -- `init_offsetm zero-count` 直接升到 `262k` - -这说明 DEM 源会影响结果,但当前问题不是“现有 DEM 明显坏掉”,而是: - -- 当前几何链本来就已经非常稀疏 -- 更粗或不合适的 DEM 只会让它更差 - -### 5.3 当前最可疑的位置继续落在 LT-1 几何导入链 - -综合 patch 扫描和 DEM 源对照,当前更像是以下链路问题,而不是 DEM 文件本体问题: - -- `par_LT1_SLC / LT-1 导入几何` -- `generate_rdc_dem` -- `coreg_gamma` 中基于 DEM 的几何映射链 - -## 6. 补充记录 - -为完成 `OpenTopography SRTMGL1` 对照,已在 WSL `isce2` 环境安装: - -- `rasterio==1.4.4` - -安装原因不是业务修复,而是 PyINT 下载 DEM 路径在当前环境里依赖该包做分块 tif 校验与合并。 - -## 7. 建议下一步 - -下一轮不建议继续反复更换 DEM。 - -更值得做的是: - -1. 对比 `COPDEM` 与 `SRTMGL1` 生成出来的 `pyint_stage.dem.par`、`UTMDEMpar`、`UTM2RDC/UTMTORDC` 是否几乎一致 -2. 回到 LT-1 导入链,核查 `.slc.par` 中被 `gc_map1 / geocode / init_offsetm` 直接消费的几何字段 -3. 如果需要继续做 DEM 类实验,优先做“同一 DEM 下替换导入几何参数”,而不是继续换 DEM 本体 diff --git a/docs/archive/PYINT_LT1_PAIR_SELECTION_EXPERIMENT_20260421.md b/docs/archive/PYINT_LT1_PAIR_SELECTION_EXPERIMENT_20260421.md deleted file mode 100644 index c9e34c7..0000000 --- a/docs/archive/PYINT_LT1_PAIR_SELECTION_EXPERIMENT_20260421.md +++ /dev/null @@ -1,145 +0,0 @@ -# PyINT LT-1 Pair Selection Experiment - -**日期**: 2026-04-21 -**状态**: 已执行 -**目标问题**: 之前 `init_offsetm` 失败是否只是当前任务配对选得不好 - -## 1. 实验思路 - -上一轮根因定位主要围绕这一组 2023 年 SYC 数据: - -- `2023-06-24` -- `2023-07-26` -- `2023-09-20` - -其中: - -- `2023-06-24 -> 2023-07-26` -- `2023-07-26 -> 2023-09-20` - -两对都失败,且 `2023-06-24 -> 2023-07-26` 已经是较短时基。 - -为了验证是不是“只是这几对碰巧不行”,本轮换了一组新的、同条带同中心点的 2024 年三景,并改用中间时相作为 master。 - -## 2. 实验数据 - -实验根目录: - -- `D:\PyINT_POOL_TEST\LT1A_MONO_SYC_STRIP1_E129.4_N45.0_3scene_2024` - -从影像库复制的三景: - -1. `LT1A_MONO_SYC_STRIP1_012583_E129.4_N45.0_20240520_SLC_HH_S2A_0000402579` -2. `LT1A_MONO_SYC_STRIP1_013416_E129.4_N45.0_20240715_SLC_HH_S2A_0000454453` -3. `LT1A_MONO_SYC_STRIP1_014249_E129.4_N45.0_20240909_SLC_HH_S2A_0000505340` - -配置: - -- `masterDate=20240715` -- `startDate=20240501` -- `endDate=20240930` -- DEM 仍使用: - - `/mnt/d/DEM/COPDEM_GLO30_China_4326_DEM` - -## 3. 执行链路 - -执行脚本: - -- 复制实验根: - - `D:\Code\Insar_management_system_v2\.codex_tmp\setup_lt1_pool_multiscene_experiment.ps1` -- 跑三景最小链路: - - `D:\Code\Insar_management_system_v2\.codex_tmp\run_lt1_pool_multiscene_generic.sh` -- 审计几何中间产物: - - `D:\Code\Insar_management_system_v2\.codex_tmp\audit_lt1_pool_multiscene_root.py` - -实际运行结果: - -- `down2slc_all`: 成功 -- `makedem_pyint`: 成功 -- `generate_rdc_dem`: 成功 -- `coreg_gamma_all`: 失败 - -由于 `coreg_gamma_all` 在第一对失败后停止,又额外补跑了: - -- `coreg_20240909` - -这样两对都拿到了独立结果。 - -## 4. 结果 - -### 4.1 `init_offsetm` 失败情况 - -`20240520 <- 20240715(master)`: - -- `zero_count = 197702` -- `threshold = 32768` - -`20240909 <- 20240715(master)`: - -- `zero_count = 196712` -- `threshold = 32768` - -对应日志: - -- `D:\PyINT_POOL_TEST\LT1A_MONO_SYC_STRIP1_E129.4_N45.0_3scene_2024\logs\coreg_gamma_all.stderr.log` -- `D:\PyINT_POOL_TEST\LT1A_MONO_SYC_STRIP1_E129.4_N45.0_3scene_2024\logs\coreg_20240909.stderr.log` - -### 4.2 中间产物审计 - -审计汇总: - -- `D:\PyINT_POOL_TEST\LT1A_MONO_SYC_STRIP1_E129.4_N45.0_3scene_2024\audit_dem_geometry\audit_summary.tsv` - -关键指标: - -- `hgtsim zero_ratio = 0.974356` -- `lt0 valid_pair_ratio = 0.025644` -- `20240520 center overlap = 0.245827` -- `20240909 center overlap = 0.249603` - -## 5. 与 2023 年那组三景对比 - -2023 年 `E129.6_N45.0` 那组基线结果: - -- `20230624`: `zero_count = 184099` -- `20230920`: `zero_count = 190155` -- `hgtsim zero_ratio = 0.967266` -- `lt0 valid_pair_ratio = 0.032734` - -2024 年 `E129.4_N45.0` 新组三景结果: - -- `20240520`: `zero_count = 197702` -- `20240909`: `zero_count = 196712` -- `hgtsim zero_ratio = 0.974356` -- `lt0 valid_pair_ratio = 0.025644` - -## 6. 结论 - -这轮实验不支持“只是当前 pair 选坏了”这个解释。 - -原因很直接: - -1. 换了一整组三景 -2. 换了 master -3. 两对 slave 都单独跑到了 `init_offsetm` -4. 结果仍然失败 -5. 而且零值规模没有改善,反而比 2023 那组更差 - -因此当前更合理的判断是: - -- 问题不是单纯 pair selection -- 也不是只集中在 `2023-07-26` 这一景 -- 更像 LT-1 在 PyINT/Gamma 下的导入几何链存在系统性问题 - -## 7. 建议下一步 - -下一步不建议继续只靠“再换几对”来试。 - -更有价值的是继续往几何链前面查: - -1. 对比不同实验根生成的 `.slc.par` 几何字段 -2. 对比 `generate_rdc_dem` 产生的: - - `*.utm.dem.par` - - `*.UTM_TO_RDC` - - `*.rdc.dem` -3. 重点核查 LT-1 导入程序和 Gamma 实际消费字段之间是否存在系统性偏差 diff --git a/docs/archive/RADAR_PREVIEW_SCHEME_B.md b/docs/archive/RADAR_PREVIEW_SCHEME_B.md deleted file mode 100644 index 7c1fd79..0000000 --- a/docs/archive/RADAR_PREVIEW_SCHEME_B.md +++ /dev/null @@ -1,86 +0,0 @@ -# 源影像叠加稳定化方案(Scheme B) - -目标:解决“覆盖面是斜四边形,源影像是矩形包围框”的错位问题,优先保障长期稳定与前端流畅。 - ---- - -## 1. 问题本质 - -当前地图展示中: -- 覆盖范围来自 `coverage_polygon`(真实斜四边形) -- 源图叠加常用 `imageOverlay(url, bounds)`(矩形边界) - -当源图本身有旋转/倾斜时,直接矩形贴图会产生明显错位。 - ---- - -## 2. 方案选择 - -### A(前端实时变换) -- 在浏览器端做逐帧几何变换 -- 优点:改动快 -- 缺点:前端负载高,图层增多后容易卡顿 - -### B(后端预纠正缓存)✅ -- 扫描/构建阶段在后端完成几何纠正,前端只显示结果图 -- 优点:渲染轻、稳定性高、易观测 -- 缺点:后端实现复杂度更高 - -本项目已确定采用 **B 方案**。 - ---- - -## 3. 当前实现(2026-02-10) - -## 3.1 缓存分层 -- `backend/image_cache/radar_geo/`:地理纠正后的主缓存 -- `backend/image_cache/radar_raw/`:原图回退缓存 - -## 3.2 后端能力 -- 默认由手动扫描任务触发增量构建(前端“立即扫描”或 `POST /api/monitor/run-now`) -- 扫描时增量构建纠正缓存(`radar_geo`) -- 同步维护原图缓存(`radar_raw`)作为兜底 -- 方向判定优先使用 XML 的 `sceneCornerCoord/refRow/refColumn`,避免仅靠角点名称导致镜像/翻转误判 -- `GET /api/radar-data/{id}/thumb`: - - 优先返回 `radar_geo` - - 失败自动回退 `radar_raw` -- 新增状态接口: - - `GET /api/radar-data/{id}/preview-status` - - `POST /api/radar-data/{id}/rebuild-preview-cache`(管理员) - -## 3.3 前端能力 -- 每条源影像显示预览状态(纠正/回退/失败/未建) -- 管理员支持“重建”按钮 -- 预览请求增加缓存键,避免浏览器长期缓存旧图 - ---- - -## 4. 关键配置项 - -```env -RADAR_GEO_CACHE_WORKERS=2 -RADAR_GEO_CACHE_VERSION=b1 -RADAR_GEO_CACHE_QUALITY=84 -RADAR_PREVIEW_BUILD_ON_DEMAND=true -``` - -建议: -- 小规模先用 `RADAR_GEO_CACHE_WORKERS=1~2` -- 算法升级时提高 `RADAR_GEO_CACHE_VERSION` 触发重建 - ---- - -## 5. 验收标准 - -- 同一场景下,源图与覆盖面边界明显趋于一致 -- 缩放/平移时交互平滑,无明显掉帧 -- 构建失败可在状态接口与日志中定位原因 -- 失败场景可回退原图缓存,不阻塞业务操作 - ---- - -## 6. 后续建议 - -1. 采集 3 类典型数据(升轨/降轨/大倾斜)做对齐验收 -2. 统计 `radar_geo` 构建失败原因并分级治理 -3. 发布前执行一次全量重建,提升线上命中率 diff --git a/docs/archive/SBAS_FRONTEND_UX_SPEC_20260406.md b/docs/archive/SBAS_FRONTEND_UX_SPEC_20260406.md deleted file mode 100644 index 197e3d2..0000000 --- a/docs/archive/SBAS_FRONTEND_UX_SPEC_20260406.md +++ /dev/null @@ -1,232 +0,0 @@ -# SBAS 前端设计说明 - -更新日期:2026-04-06 - -## 1. 设计目标 - -SBAS 前端必须解决四个问题: - -- 让用户知道“匹配结果”和“正式生产运行”不是一回事 -- 让用户能从已有 PS 批次发起正式运行 -- 让用户能查看 `psinsar` 成果,而不是只看实验目录 -- 让用户能逐步过渡到更复杂的时序成果浏览,而不是一次性堆满功能 - -## 2. 导航结构 - -当前建议保留现有导航层级,只把占位页替换成真实页面。 - -### 2.1 生产规划组 - -- `ps_results` - - 匹配结果浏览与批次创建 -- `ps_production` - - 正式生产入口 -- `ps_products` - - 成果编目与产品管理 - -### 2.2 InSAR 变形分析组 - -- `psinsar_results` - - 以成果浏览为主的只读结果页 -- `psinsar_analysis` - - 后续时序分析页,当前可继续保留占位 - -## 3. 页面一:`ps_production` - -## 3.1 页面目标 - -从已有 `ps_task_batches` 中选择一个 stack,提交一次正式 SBAS 运行,并查看最近运行状态。 - -## 3.2 页面布局 - -建议分成三块: - -- 运行说明卡 -- 新建运行表单 -- 最近运行列表 - -## 3.3 新建运行表单字段 - -必填字段: - -- `batch_id` - -可选字段: - -- `run_name` -- `reference_date` -- `water_mask_mode` -- `notes` - -只读展示字段: - -- `direction` -- `stack_size` -- `with_orbit_count` -- `missing_orbit_count` - -## 3.4 提交后反馈 - -提交后需要立刻给出: - -- `task_id` -- `run_id` -- 当前状态 - -当前 Phase 1 仅执行 prepare,因此状态建议为: - -- `PENDING` -- `RUNNING` -- `PREPARED` -- `FAILED` - -## 3.5 最近运行列表 - -每个运行卡片至少展示: - -- `run_name` -- `run_id` -- `batch_id` -- `reference_date` -- `stack_size` -- `status` -- `created_at` - -详情展开建议展示: - -- DEM 路径 -- 轨道池路径 -- 生成的 stack manifest 路径 -- 输入日期列表 -- 质量/预检查摘要 - -## 4. 页面二:`ps_products` - -## 4.1 页面目标 - -承接管理员视角的产物目录维护。 - -能力包括: - -- 查看 `psinsar` catalog 状态 -- 手动重建 catalog -- 浏览产品列表 -- 查看产品详情 - -## 4.2 页面布局 - -建议分成三块: - -- catalog 状态卡 -- 管理操作区 -- 产品列表与详情区 - -## 4.3 catalog 状态卡 - -展示字段: - -- `catalog_name` -- `storage_root` -- `status` -- `needs_rebuild` -- `manifest_count` -- `db_count` -- `issue_count` -- `last_full_rebuild_at` - -## 4.4 产品列表 - -每个产品卡片建议展示: - -- `display_name` -- `product_id` -- `run_key` -- `reference_date` -- `stack_size` -- `status` -- `published_at` - -优先展示缩略图: - -- `preview/velocity_preview.png` - -## 4.5 产品详情 - -详情建议包含: - -- 基本信息 -- 时序信息 -- 质量摘要 -- 资产列表 -- 问题列表 - -资产列表首屏优先顺序: - -- `velocity_geotiff` -- `timeseries_cube` -- `temporal_coherence_geotiff` -- `quality_mask_geotiff` -- `preview_png` -- 其他辅助资产 - -## 5. 页面三:`psinsar_results` - -## 5.1 页面目标 - -承接业务用户视角的成果浏览,不承担目录维护操作。 - -## 5.2 与 `ps_products` 的区别 - -- `ps_products` 偏运维和管理 -- `psinsar_results` 偏展示和检索 - -## 5.3 当前 Phase 1 能力 - -- 浏览产品列表 -- 查看缩略图 -- 查看资产清单 -- 查看质量摘要 - -## 5.4 后续 Phase 2/3 扩展 - -- 地图叠加 `velocity.tif` -- 按 AOI 或时间筛选 -- 像元点位时序查询 -- 专题统计与热点识别 - -## 6. 交互原则 - -- 生产和结果页面必须分开 -- 管理操作只出现在管理页 -- 结果页优先展示 `velocity.tif` 对应预览 -- 但详情页必须强调主科学产物是 `geo_timeseries.h5` - -## 7. 前端阶段性实施建议 - -### Phase 1 - -- `ps_production`:真实可提交 prepare 任务 -- `ps_products`:真实 catalog 管理页 -- `psinsar_results`:复用 catalog 浏览能力做只读页 - -### Phase 2 - -- 地图叠加 `velocity.tif` -- 结果筛选 -- 与 run 详情联动 - -### Phase 3 - -- `geo_timeseries.h5` 点查询 -- 时间曲线图 -- 多产品对比 - -## 8. 当前结论 - -前端第一阶段不应追求“一口气把时序分析全做完”,而应优先做三件事: - -- 让 SBAS 生产有正式入口 -- 让 SBAS 结果有正式目录 -- 让用户在系统里能看到真正的 `psinsar` 成果 - -这三件事完成后,系统才算真正开始承接 SBAS 能力。 diff --git a/docs/archive/SBAS_IMPLEMENTATION_TODO_20260406.md b/docs/archive/SBAS_IMPLEMENTATION_TODO_20260406.md deleted file mode 100644 index 119c24f..0000000 --- a/docs/archive/SBAS_IMPLEMENTATION_TODO_20260406.md +++ /dev/null @@ -1,102 +0,0 @@ -# SBAS 系统实施 TODO - -更新日期:2026-04-06 - -## Phase 1:低侵入落地 - -目标: - -- 不影响 D-InSAR -- 让系统开始承接 SBAS 生产记录与结果目录 - -### 后端模型 - -- [x] 新增 `PsTimeseriesRunORM` -- [x] 新增 `PsTimeseriesRun` schema -- [x] 为 `settings` 增加 `TIMESERIES_*` / `PSINSAR_PRODUCT_DIR` 配置 - -### 后端服务 - -- [x] 新增 `timeseries_service.py` -- [x] 新增 `psinsar_catalog_service.py` -- [x] 增加 SBAS prepare 任务处理逻辑 - -### 后端路由 - -- [x] 新增 `timeseries_production.py` -- [x] 新增 `ps_products.py` -- [x] 注册新路由 - -### 前端 - -- [x] 新增 `TimeseriesProductionPanel.jsx` -- [x] 新增 `PsinsarCatalogPanel.jsx` -- [x] 新增 `timeseriesProduction.js` -- [x] 新增 `psinsarProducts.js` -- [x] 替换 `ps_production` 占位页 -- [x] 替换 `ps_products` 占位页 -- [x] 替换 `psinsar_results` 占位页 - -### 校验 - -- [x] 后端语法检查 -- [x] 前端 build 检查 -- [ ] 验证 D-InSAR 页面未受影响 - -## Phase 2:串接实验处理链 - -目标: - -- 在系统内真正触发 SBAS 处理 - -### 处理链 - -- [x] `prepare` 生成标准 stack manifest -- [x] `materialize` 生成 stack 工作目录 -- [ ] 接入 ISCE2 stack 执行 -- [ ] 接入 MintPy SBAS 执行 -- [ ] 接入 geocode/export/publish - -### 任务编排 - -- [x] 新增 workflow steps -- [ ] 新增失败重试策略 -- [x] 增加运行日志归集 - -## Phase 3:结果展示增强 - -目标: - -- 从“结果存在”升级到“结果可分析” - -### 展示 - -- [ ] 地图叠加 `velocity.tif` -- [ ] 按 AOI/时间筛选产品 -- [ ] 结果与 run 联动查看 - -### 分析 - -- [ ] `geo_timeseries.h5` 点位查询 -- [ ] 时间曲线图 -- [ ] 质量掩膜联动 -- [ ] 热点区域分析 - -## 当前阶段实施顺序 - -推荐顺序: - -1. 文档补齐 -2. 运行记录模型 -3. prepare 任务 -4. `psinsar` catalog -5. 前端生产页 -6. 前端结果页 -7. 静态校验 - -## 当前不做 - -- [ ] 不重构现有 D-InSAR 结果服务为完全通用框架 -- [ ] 不改现有配对算法 -- [ ] 不继续保留 SBAS 使用 `*_envi_import` 的旧逻辑 -- [ ] 不在当前 D-InSAR 生产环境中直接做依赖升级 diff --git a/docs/archive/SBAS_PROGRESS_STATUS_20260406.md b/docs/archive/SBAS_PROGRESS_STATUS_20260406.md deleted file mode 100644 index 011bca6..0000000 --- a/docs/archive/SBAS_PROGRESS_STATUS_20260406.md +++ /dev/null @@ -1,124 +0,0 @@ -# SBAS 系统改造当前状态 - -更新日期:2026-04-06 - -## 1. 当前结论 - -截至本轮,SBAS / PS-InSAR 已经完成第一阶段的“低侵入接入”改造: - -- 已能在系统内创建正式 `ps_timeseries_runs` -- 已能把现有 PS 匹配结果转换为 `stack_input_manifest.json` -- 已能把实验产物 `psinsar.publish.v1` bundle 注册到系统结果目录 -- 已能在前端查看 SBAS 运行记录与 `psinsar` 产品目录 -- 已补齐 `psinsar` catalog 的启动期 bootstrap 与健康检查接入 - -这意味着系统已经不再只是“影像管理口”,而是具备了 SBAS 正式生产入口的骨架。当前已经串起实验级四步链: - -- `prepare` -- `stack_prep_initial` -- `materialize` -- `stack_prep_refresh` - -但仍未接入真正的 ISCE2 stack / MintPy / geocode / publish 自动执行链。 - -## 2. 已完成范围 - -### 2.1 后端 - -- 新增 `ps_timeseries_runs` 运行记录模型 -- 新增 `timeseries_service.py` -- 新增 `psinsar_catalog_service.py` -- 新增 `timeseries_production.py` -- 新增 `ps_products.py` -- 作业系统已支持: - - `TIMESERIES_PREPARE` - - `TIMESERIES_STACK_PREP` - - `TIMESERIES_MATERIALIZE` -- `timeseries_service.py` 已补齐 workflow 驱动的: - - `create_run` - - `prepare_run` - - `build_stack_prep` - - `materialize_run` - - `list_runs` - - `get_run_detail` -- 运行详情已可返回 `workflow.steps` -- 启动阶段已同时检查 `dinsar` 与 `psinsar` 两套 catalog -- `/health` 已保留原有 `result_catalog` 兼容字段,并新增: - - `dinsar_result_catalog` - - `psinsar_result_catalog` - - `catalogs` - -### 2.2 前端 - -- 已接入 `TimeseriesProductionPanel` -- 已接入 `PsinsarCatalogPanel` -- 已替换 `ps_production`、`ps_products`、`psinsar_results` 占位页 -- `TimeseriesProductionPanel` 已补充 Phase 2 状态色和 workflow 步骤展示 - -### 2.3 文档 - -已形成以下系统设计与风险文档: - -- `SBAS_SYSTEM_INTEGRATION_AUDIT_20260406.md` -- `SBAS_SYSTEM_EMBEDDING_DESIGN_20260406.md` -- `SBAS_TASK_PATH_MODE_EVALUATION_20260406.md` -- `SBAS_SYSTEM_CHANGE_RISK_ASSESSMENT_20260406.md` -- `SBAS_RESULT_MANAGEMENT_AND_DISPLAY_SPEC_20260406.md` -- `SBAS_FRONTEND_UX_SPEC_20260406.md` -- `SBAS_IMPLEMENTATION_TODO_20260406.md` - -## 3. 本轮验证结果 - -### 3.1 已通过 - -- 前端 `npm run build` 已通过 -- WSL `Ubuntu-24.04` 下,`isce2` 环境对本轮修改后的后端文件执行 `py_compile` 通过 -- 启动日志已补充 `PSInSAR-Catalog` 状态输出 - -### 3.2 当前验证边界 - -本轮在 WSL `isce2` 环境做后端“导入级冒烟测试”时失败,失败原因不是本轮代码语法问题,而是该环境缺少系统后端依赖: - -- `ModuleNotFoundError: No module named 'fastapi'` - -这说明当前 `isce2` 环境可以作为 SBAS 处理链环境使用,也可以做后端源码级语法校验,但还不能直接作为系统后端完整运行环境。 - -## 4. 当前缺口 - -### 4.1 处理链缺口 - -系统尚未自动执行以下步骤: - -- ISCE2 stack 执行 -- MintPy SBAS 执行 -- geocode / export / publish -- 产物自动注册收口 - -### 4.2 环境缺口 - -若目标是“统一环境”,则还需要复制一份稳定的 ISCE2 环境并补齐系统依赖,形成一个既能跑: - -- ISCE2 / MintPy -- 又能支撑 FastAPI 后端依赖 - -的独立统一环境。当前不建议直接污染现有 D-InSAR 正在使用的生产环境。 - -## 5. 下一步建议 - -建议按以下顺序继续: - -1. 保持当前 Phase 2 bridge 收口,不继续扩散系统侧改动,先把 `prepare -> stack_prep -> materialize -> refresh` 稳定住。 -2. 在实验层继续推进 `isce2_stack -> mintpy -> export_publish` 分步脚本与统一环境验证。 -3. 单独复制一份 WSL `isce2` 环境,做“统一环境”安装实验,不动现有 D-InSAR 生产环境。 -4. 统一环境验证稳定后,再把真正的 stack / MintPy / publish 接回系统作业编排。 - -## 6. 当前判断 - -当前路线是成立的。 - -成立点不在于“已经跑通全系统自动 SBAS”,而在于: - -- 系统边界已经切对 -- D-InSAR 老链路基本保持隔离 -- SBAS 已有正式运行记录、结果目录和前端入口 -- 后续只需要继续往中间补处理链,而不是推倒重来 diff --git a/docs/archive/SBAS_RESULT_MANAGEMENT_AND_DISPLAY_SPEC_20260406.md b/docs/archive/SBAS_RESULT_MANAGEMENT_AND_DISPLAY_SPEC_20260406.md deleted file mode 100644 index 608cfe8..0000000 --- a/docs/archive/SBAS_RESULT_MANAGEMENT_AND_DISPLAY_SPEC_20260406.md +++ /dev/null @@ -1,236 +0,0 @@ -# SBAS 结果管理与展示规范 - -更新日期:2026-04-06 - -## 1. 管理目标 - -SBAS 结果进入系统后,必须满足三个层面的要求: - -- 可登记 -- 可追溯 -- 可展示 - -当前推荐的管理边界不是 MintPy 运行目录,而是发布级 bundle。 - -唯一注册入口: - -- `publish/.../manifest.json` - -## 2. 结果层级 - -### 2.1 运行层 - -运行层记录在: - -- `ps_timeseries_runs` - -职责: - -- 记录一次正式处理运行的业务身份 -- 记录输入、参数、DEM、轨道、掩膜、目录与状态 - -### 2.2 产品层 - -产品层继续复用: - -- `result_products` -- `result_assets` -- `result_issues` - -约定: - -- `catalog_name = psinsar` -- `product_type = psinsar_bundle` -- `run_key = ps_timeseries_runs.run_id` - -### 2.3 资产层 - -资产层由 `result_assets` 管理。 - -建议角色: - -- `timeseries_cube` -- `velocity_map` -- `velocity_geotiff` -- `temporal_coherence` -- `temporal_coherence_geotiff` -- `quality_mask` -- `quality_mask_geotiff` -- `preview_png` -- `diagnostic_png` -- `config` -- `quality_summary` - -## 3. 发布级 bundle 约束 - -标准目录: - -```text -/// - manifest.json - assets/ - preview/ - metadata/ -``` - -最低要求: - -- `manifest.json` -- `assets/geo_timeseries.h5` -- `assets/velocity.tif` -- `preview/velocity_preview.png` - -建议同时保留: - -- `assets/geo_velocity.h5` -- `assets/temporalCoherence.tif` -- `assets/maskTempCoh.tif` -- `preview/numTriNonzeroIntAmbiguity.png` -- `metadata/source_quality_summary.json` - -## 4. 产品主文件定义 - -### 4.1 科学主产物 - -- `assets/geo_timeseries.h5` - -这是系统证明“具备时序处理能力”的核心文件。 - -### 4.2 展示主产物 - -- `assets/velocity.tif` - -这是结果首屏展示、地图浏览、预览生成的主文件。 - -### 4.3 质量主产物 - -- `assets/temporalCoherence.tif` -- `assets/maskTempCoh.tif` - -## 5. manifest 解析要求 - -系统应支持: - -- `schema_version = psinsar.publish.v1` - -系统应从 manifest 中提取: - -- 基础身份 - - `catalog_name` - - `mode` - - `engine_code` - - `processor_code` -- 时序信息 - - `reference_date` - - `stack_dates` -- 资产信息 - - `artifacts[]` -- 质量信息 - - `quality` -- 辅助摘要 - - `summaries` - -如 manifest 缺少显式 bbox,可从 `summaries.*.attrs` 中推导: - -- `X_FIRST` -- `Y_FIRST` -- `X_STEP` -- `Y_STEP` -- `WIDTH` -- `LENGTH` - -## 6. 展示规范 - -## 6.1 列表页 - -优先展示: - -- 产品名称 -- 运行标识 -- 参考日期 -- 影像数量 -- 发布时间 -- 缩略图 - -## 6.2 详情页 - -详情页应至少展示: - -- 运行信息 -- 时间序列日期列表 -- 质量摘要 -- 资产清单 -- 问题清单 - -## 6.3 地图展示优先级 - -当前优先级: - -1. `velocity.tif` -2. `temporalCoherence.tif` -3. `maskTempCoh.tif` - -不建议当前阶段直接做: - -- `geo_timeseries.h5` 全量地图服务化 - -## 7. 结果与运行的关联 - -必须能从结果反查运行。 - -推荐方式: - -- `result_products.run_key = ps_timeseries_runs.run_id` - -这样可以回答: - -- 这个结果来自哪个 batch -- 用了哪些日期 -- DEM 和轨道来自哪里 -- 使用了什么水体掩膜策略 - -## 8. 目录与数据库职责边界 - -目录是事实源: - -- `manifest.json` -- `assets/*` -- `preview/*` - -数据库是索引层: - -- 产品列表 -- 检索字段 -- 资产索引 -- 问题索引 - -因此不应让数据库替代目录事实源,也不应让前端直接依赖实验运行目录。 - -## 9. Phase 1 / Phase 2 边界 - -### Phase 1 - -- 支持 bundle 扫描与登记 -- 支持产品列表和详情 -- 支持缩略图展示 - -### Phase 2 - -- 支持地图叠加 velocity -- 支持 run 与 product 联动 -- 支持更多质量视图 - -### Phase 3 - -- 支持 `geo_timeseries.h5` 点位时序查询 -- 支持图表和专题分析 - -## 10. 当前结论 - -SBAS 结果管理的核心不是“把很多文件存起来”,而是把下面三件事稳定下来: - -- 用 `manifest.json` 定义结果边界 -- 用 `result_products/result_assets` 登记 bundle -- 用 `run_key` 把结果和运行记录连起来 - -这三件事一旦成立,系统就具备了承接 SBAS 成果的基础能力。 diff --git a/docs/archive/SBAS_SYSTEM_CHANGE_RISK_ASSESSMENT_20260406.md b/docs/archive/SBAS_SYSTEM_CHANGE_RISK_ASSESSMENT_20260406.md deleted file mode 100644 index 91e14dd..0000000 --- a/docs/archive/SBAS_SYSTEM_CHANGE_RISK_ASSESSMENT_20260406.md +++ /dev/null @@ -1,219 +0,0 @@ -# SBAS 系统改造风险评估 - -更新日期:2026-04-06 - -## 1. 目标 - -本次改造的目标不是把实验脚本直接搬进系统,而是在尽量不影响现有 D-InSAR 生产链的前提下,为系统增加一条独立的 SBAS/PS-InSAR 生产与结果管理能力。 - -约束条件: - -- 现有 D-InSAR 生产逻辑必须保持可用 -- 现有匹配逻辑先保持不动 -- SBAS 生产只基于原始影像,不再沿用 `*_envi_import` -- 结果必须最终落到系统内可管理、可展示、可追踪的发布级 bundle -- 统一环境实验优先,但不能污染当前 D-InSAR 使用的 `isce2` 生产环境 - -## 2. 风险分级总览 - -### P0 高风险 - -- 误改现有 D-InSAR 生产入口、目录契约或作业类型,导致原有生产失败 -- 把 SBAS 直接套进现有 `master/slave` 双景输入模型,导致设计方向错误 -- 结果编目直接改写现有 `result_catalog_service.py` 的 D-InSAR 语义,造成历史产品异常 -- 在当前生产 `isce2` 环境内直接安装新依赖,破坏现有运行稳定性 - -### P1 中风险 - -- SBAS 运行记录、任务记录、结果记录之间关系不清,后期无法追溯 -- Windows/WSL 双路径继续散落在多个服务中,后续维护成本继续升高 -- 前端直接绑定运行时目录结构,导致实验目录变化后页面失效 -- 结果展示只展示 `velocity.tif`,忽略 `geo_timeseries.h5`,造成“有图无时序能力”的误判 - -### P2 低风险 - -- 字段命名不统一,后续扩展时需要补充适配 -- 初期前端只做结果浏览,不做像元级时序查询 -- 先不接入健康检查总览,短期不会阻断主流程 - -## 3. 风险清单 - -### 3.1 生产链串扰风险 - -风险: - -- 新增 SBAS 能力时误复用或改写 D-InSAR 路由、作业类型、拷贝逻辑、结果目录逻辑 - -触发点: - -- 修改 `backend/app/routers/dinsar_production.py` -- 修改 `backend/app/copier.py` 中现有 D-InSAR 行为 -- 在 `result_catalog_service.py` 中直接硬改通用逻辑 - -影响: - -- D-InSAR 生产失败 -- 历史产物目录重建异常 - -控制策略: - -- Phase 1 新增独立入口: - - `timeseries_production.py` - - `ps_products.py` - - `psinsar_catalog_service.py` -- 不修改 D-InSAR 的路由路径和任务类型 -- 不复用 `copy-ps-stack` 作为 SBAS 生产输入准备 - -### 3.2 输入契约错误风险 - -风险: - -- 继续沿用 D-InSAR 的 `Task_*/master + slave` 输入目录契约 - -影响: - -- SBAS 处理逻辑被错误建模为“双景处理” -- 后续 prepare/materialize/stack workflow 无法自然落地 - -控制策略: - -- 明确采用 `Stack_` / `stack_input_manifest.json` 契约 -- 生产入口接收 `batch_id`,由系统生成 stack 级 manifest -- 匹配层继续保留,生产层单独增加 prepare - -### 3.3 环境污染风险 - -风险: - -- 在当前 D-InSAR 使用的 `isce2` 运行环境内直接安装 MintPy 或其他依赖 - -影响: - -- 现有 D-InSAR 生产不可复现 -- 环境漂移,难以排查 - -控制策略: - -- 当前系统 Phase 1 仅记录并使用 `TIMESERIES_*` 独立配置 -- 当前实验优先对接已经验证过的统一环境或桥接环境 -- 保留现有 D-InSAR `isce2` 环境不动 - -### 3.4 数据身份与结果归档风险 - -风险: - -- 只有批次,没有正式“运行记录” -- 只有实验目录,没有系统级产品登记 - -影响: - -- 无法回答“某次 SBAS 运行用的 DEM、轨道、参考日期、掩膜策略是什么” -- 无法稳定展示和管理结果 - -控制策略: - -- 新增 `ps_timeseries_runs` -- 结果仍复用 `result_products` / `result_assets` / `result_issues` -- `run_key` 绑定 `ps_timeseries_runs.run_id` -- 发布边界固定为 `manifest.json` - -### 3.5 前端误导风险 - -风险: - -- 仅提供一个“PS 生产占位页”,但用户无法区分: - - 规划批次 - - 正式生产运行 - - 结果目录 - - 成果展示 - -影响: - -- 操作路径混乱 -- 误以为已有生产能力 - -控制策略: - -- 生产页和结果页分离 -- 生产页展示: - - 可选批次 - - 运行参数 - - 最近运行 - - 当前准备状态 -- 结果页展示: - - 编目状态 - - 产品列表 - - 产品详情 - - 资产列表 - - 质量摘要 - -## 4. 本次推荐改造范围 - -## 4.1 必做 - -- 新增 `ps_timeseries_runs` 业务表 -- 新增 `timeseries_production` 路由 -- 新增 SBAS prepare 任务 -- 新增 `psinsar_catalog_service` -- 新增 `ps_products` 路由 -- 新增前端 `TimeseriesProductionPanel` -- 新增前端 `PsinsarCatalogPanel` - -## 4.2 暂缓 - -- 不改现有 `find-ps-timeseries` 匹配算法 -- 不改现有 D-InSAR 生产入口 -- 不把 SBAS 直接接进 `copy-ps-stack` -- 不做像元级 `geo_timeseries.h5` 在线查询 -- 不做地图服务化切片发布 - -## 5. 回滚策略 - -回滚必须满足“删新不伤旧”。 - -可回滚对象: - -- 新增路由 -- 新增 ORM 表 -- 新增前端面板 -- 新增 `psinsar` 编目服务 - -不应触碰回滚的对象: - -- 现有 D-InSAR 路由 -- 现有 `system_jobs` / `system_tasks` 表结构语义 -- 现有 D-InSAR 结果目录 - -推荐回滚方式: - -1. 先停用前端入口 -2. 再停用新路由注册 -3. 保留数据库表,不做破坏性删除 -4. 保留已发布 `psinsar` bundle,必要时仅停止 catalog rebuild - -## 6. 验收标准 - -### 6.1 低风险验收 - -- D-InSAR 现有页面正常打开 -- D-InSAR 现有生产入口不报错 -- D-InSAR 现有结果目录查询不受影响 - -### 6.2 SBAS Phase 1 验收 - -- 可以从已有 `ps_task_batch` 创建一个正式 `ps_timeseries_run` -- 系统能为该 run 生成 stack 级 manifest -- 系统能记录 work/publish 目录、参考日期、轨道统计和输入快照 -- 系统可以扫描 `psinsar.publish.v1` bundle 并登记到结果目录 -- 前端能查看运行记录与产品详情 - -## 7. 当前结论 - -本次改造最重要的不是“尽快把 SBAS 跑起来”,而是先把系统边界做对: - -- 规划层不动 -- 生产层新增 -- 结果层隔离接入 -- D-InSAR 旧链路不碰 - -按这个边界推进,风险可控,且后续可以逐步把实验能力提升为正式生产能力。 diff --git a/docs/archive/SBAS_SYSTEM_EMBEDDING_DESIGN_20260406.md b/docs/archive/SBAS_SYSTEM_EMBEDDING_DESIGN_20260406.md deleted file mode 100644 index 7afd9b8..0000000 --- a/docs/archive/SBAS_SYSTEM_EMBEDDING_DESIGN_20260406.md +++ /dev/null @@ -1,680 +0,0 @@ -# SBAS InSAR 嵌入系统设计稿 - -更新日期:2026-04-06 - -## 1. 设计目标 - -目标不是把当前实验脚本“塞进系统”,而是把已经验证成功的 SBAS 路线变成系统内可提交、可跟踪、可复跑、可发布的正式能力。 - -设计必须同时满足四个约束: - -- 不破坏现有 D-InSAR 生产 -- 尽量复用现有 `task` / `job` / `workflow` / `result catalog` 骨架 -- 以发布级 bundle 作为系统注册边界,而不是 MintPy 运行目录 -- 保留 WSL + conda 的现实部署方式,但把路径和环境耦合收敛到专门层 - -## 2. 设计原则 - -### 2.1 规划层和生产层分离 - -现有: - -- `find-ps-timeseries` -- `ps_task_batches` -- `ps_task_items` - -这些应该继续保留,但它们只负责: - -- 选片 -- 栈定义 -- 输入快照 - -它们不应该继续承担: - -- 生产运行主记录 -- 产物发布状态 -- 重试与失败恢复 - -### 2.2 一个 SBAS 运行对应一个发布 bundle - -不要把 SBAS 多个文件当成多个独立产品行。 - -推荐模型: - -- 一个 `ps_timeseries_run` -- 对应一个 publish bundle -- 对应 `result_products` 中一条 `psinsar` 产品记录 -- bundle 内部多个文件通过 `result_assets` 管理 - -这样最贴合当前系统的 `result_products -> result_assets` 结构。 - -### 2.3 发布边界固定为 `manifest.json` - -系统不直接注册: - -- `stack_work/` -- `mintpy_work/` -- `timeseries.h5` -- 任意临时中间目录 - -系统只注册: - -- `publish/.../manifest.json` - -原因: - -- 这是当前实验里最稳定的对外契约 -- 它天然适合作为 catalog rebuild 和产品详情入口 -- 它把运行时目录与发布目录分开了 - -### 2.4 Phase 1 优先“隔离集成”,不优先“统一重构” - -当前 `result_catalog_service` 明显偏 D-InSAR。 - -因此 Phase 1 不建议直接大改现有 D-InSAR catalog 服务为完全泛化版本。 - -更稳妥的做法是: - -- 新增 `psinsar_catalog_service.py` -- 接口风格与 `result_catalog_service.py` 保持一致 -- 底层继续复用 `result_products` / `result_assets` / `result_issues` - -等 SBAS 生产链跑稳后,再提炼公共基类。 - -## 3. 目标架构 - -推荐分成五层。 - -### 3.1 规划层 - -复用现有: - -- `POST /find-ps-timeseries` -- `POST /task-batches/ps` - -职责: - -- 根据 AOI 和筛选条件找到时间序列候选栈 -- 生成一个可复用的 `ps_task_batch` - -### 3.2 生产控制层 - -新增: - -- `ps_timeseries_runs` -- `timeseries_production.py` -- `timeseries_service.py` - -职责: - -- 从一个 `ps_task_batch` 派生一次正式运行 -- 固化参数、环境、输入路径、输出路径、DEM、轨道、水体掩膜策略 -- 对接 `system_tasks` / `system_jobs` / `workflow_runs` - -### 3.3 执行编排层 - -复用现有: - -- `system_tasks` -- `system_jobs` -- `workflow_runs` -- `workflow_steps` -- `workflow_artifacts` - -新增: - -- `timeseries_workflow_factory.py` -- `mintpy_service.py` -- `isce2_stack_service.py` - -职责: - -- 把一次 SBAS 运行拆成多个受控步骤 -- 每一步进入统一 worker -- 失败可定位到 step,而不是只看到“整条链失败” - -### 3.4 发布与编目层 - -新增: - -- `psinsar_catalog_service.py` -- `ps_products.py` - -复用: - -- `result_products` -- `result_assets` -- `result_issues` -- `result_catalog_states` - -职责: - -- 扫描和注册 `psinsar.publish.v1` -- 管理产品详情、预览、健康检查、重建 - -### 3.5 展示层 - -新增页面: - -- `ps_production` -- `ps_products` -- `psinsar_results` - -职责: - -- 运行提交与监控 -- 产品浏览与筛选 -- 结果详情、预览和下载 - -## 4. 核心数据模型 - -## 4.1 新增业务表:`ps_timeseries_runs` - -建议字段: - -- `run_id` -- `batch_id` -- `run_name` -- `catalog_name` -- `mode` - - `sbas` -- `engine_code` - - `isce2` -- `processor_code` - - `isce2_stack_mintpy` -- `env_name` - - 例如 `isce2_mintpy_v1` -- `wsl_distro` - - 例如 `Ubuntu-24.04` -- `status` - - `PENDING` / `RUNNING` / `FAILED` / `COMPLETED` / `PUBLISHED` -- `workflow_run_id` -- `task_id` -- `direction` -- `stack_size` -- `reference_date` -- `water_mask_mode` - - `local` / `synthetic_fallback` -- `dem_path_windows` -- `dem_path_wsl` -- `orbit_pool_windows` -- `orbit_pool_wsl` -- `work_root_windows` -- `work_root_wsl` -- `publish_dir_windows` -- `publish_dir_wsl` -- `manifest_path_windows` -- `manifest_path_wsl` -- `params_json` -- `summary_json` -- `input_snapshot_json` -- `orbit_summary_json` -- `quality_summary_json` -- `error_message` -- `created_by` -- `created_at` -- `updated_at` -- `started_at` -- `ended_at` - -### 4.2 不新增产品主表 - -继续使用: - -- `result_products` -- `result_assets` -- `result_issues` - -但对 `psinsar` 约定如下: - -- `catalog_name = psinsar` -- `product_type = psinsar_bundle` -- `engine_code = isce2` -- `task_alias` 存运行展示名 -- `run_key` 直接绑定 `ps_timeseries_runs.run_id` - -原因: - -- 当前系统的产品层本来就是“一个产品行 + 多资产” -- SBAS publish bundle 天然适合这种结构 - -### 4.3 `result_assets.asset_role` 约定 - -建议统一定义以下资产角色: - -- `timeseries_cube` -- `velocity_h5` -- `velocity_geotiff` -- `temporal_coherence_h5` -- `temporal_coherence_geotiff` -- `quality_mask_h5` -- `quality_mask_geotiff` -- `preview_png` -- `diagnostic_png` -- `config` -- `quality_summary` -- `input_manifest` - -### 4.4 精轨 provenance 建议 - -最小建议不是新表,而是先把运行级 summary 做扎实。 - -在 `ps_timeseries_runs.orbit_summary_json` 中记录: - -- 每景影像日期 -- 轨道 stem -- 轨道 XML 路径 -- 来源 - - `existing_xml` / `generated_from_txt` -- checksum -- 是否来自 ISCE2 pool - -Phase 2 再考虑单独抽 `orbit_assets`。 - -## 5. 路径与目录设计 - -## 5.1 总原则 - -- 原始影像目录不复制 -- 工作目录和发布目录必须分离 -- 发布目录一旦注册,尽量不再原地改写 - -### 5.2 推荐目录结构 - -Windows 侧: - -```text -\{run_id}\ - input\ - stack\ - mintpy\ - logs\ - publish\ -``` - -WSL 侧: - -```text -/mnt/.../timeseries_work/{run_id}/ - input/ - stack/ - mintpy/ - logs/ - publish/ -``` - -正式发布根目录: - -```text -\{year}\{run_id}\ - manifest.json - assets/ - preview/ - metadata/ -``` - -### 5.3 路径服务 - -不要在 job handler、router、script wrapper 里反复手拼 `/mnt/z`。 - -新增: - -- `timeseries_paths.py` - -统一负责: - -- Windows -> WSL 路径转换 -- 运行目录分配 -- 发布目录分配 -- 产物入口路径解析 - -## 6. 工作流设计 - -推荐 workflow name: - -- `isce2_sbas_mintpy_v1` - -推荐步骤如下。 - -### Step 1 `prepare_stack_input` - -输入: - -- `batch_id` -- 运行参数 - -动作: - -- 读取 `ps_task_items` -- 校验栈大小、方向一致性、日期连续性 -- 固化 `input_snapshot_json` -- 解析 DEM、轨道池、输出路径 -- 生成 stack manifest - -产物: - -- `stack_input_manifest.json` -- `source stack snapshot` - -### Step 2 `materialize_stack_scenes` - -动作: - -- 复用现有实验脚本逻辑 -- 为每景影像生成 `SLC//` -- 显式写入 `sensor.orbitFile` - -产物: - -- 物化后的 SLC -- `materialization_summary.json` - -### Step 3 `run_isce2_stack` - -动作: - -- 运行 stripmapStack 预处理链 -- 生成几何、配准、干涉网络基础产物 - -产物: - -- stack work 目录 -- run files -- log files - -### Step 4 `run_mintpy_sbas` - -动作: - -- 运行 `smallbaselineApp` -- 生成 `timeseries.h5`、`velocity.h5`、`temporalCoherence.h5` -- 记录 MintPy 版本、参数模板、参考点信息 - -产物: - -- MintPy work 目录 -- inversion quality summary - -### Step 5 `export_publish_bundle` - -动作: - -- 地理编码 -- 导出 GeoTIFF -- 生成 preview -- 写出 `manifest.json` - -产物: - -- `publish/.../manifest.json` -- `assets/` -- `preview/` -- `metadata/` - -### Step 6 `register_psinsar_product` - -动作: - -- 调用 `psinsar_catalog_service` -- 把 bundle 注册进 `result_products` -- 回填 `ps_timeseries_runs.manifest_path_*` - -产物: - -- `result_products` 一条记录 -- `result_assets` 多条记录 - -### Step 7 `post_check` - -动作: - -- 校验关键资产是否存在 -- 校验 `velocity.tif` / `geo_timeseries.h5` / `temporalCoherence.tif` -- 生成最终健康状态 - -## 7. 调度与任务设计 - -## 7.1 一个用户可见主任务 - -建议每次 SBAS 运行只创建一个主 `system_task`: - -- `task_type = RUN_PSINSAR_PRODUCTION` - -用户在 UI 上主要看这条任务。 - -### 7.2 一个工作流运行 - -创建: - -- `workflow_run` - -每个步骤单独入 `workflow_steps`。 - -worker 现有能力已经支持: - -- job 完成后自动 `mark_step_completed` -- job 失败后自动 `mark_step_failed` - -因此不需要再发明新的 step 状态机制。 - -### 7.3 建议 job types - -- `TIMESERIES_PREP_STACK` -- `TIMESERIES_MATERIALIZE_STACK` -- `TIMESERIES_RUN_ISCE2_STACK` -- `TIMESERIES_RUN_MINTPY_SBAS` -- `TIMESERIES_EXPORT_PRODUCTS` -- `TIMESERIES_REGISTER_PRODUCTS` -- `TIMESERIES_POST_CHECK` - -## 8. 环境设计 - -## 8.1 运行环境原则 - -不修改现有 D-InSAR 使用的 `isce2` 环境。 - -SBAS 生产使用独立统一环境: - -- WSL distro:`Ubuntu-24.04` -- conda env:建议 `isce2_mintpy_v1` - -这是当前实验已验证成功的路线。 - -### 8.2 配置隔离 - -不要直接复用所有 `ISCE2_*` 配置项来承载 SBAS。 - -建议新增: - -- `TIMESERIES_ENABLED` -- `TIMESERIES_WSL_DISTRO` -- `TIMESERIES_PYTHON` -- `TIMESERIES_ENV_NAME` -- `TIMESERIES_WORK_ROOT` -- `TIMESERIES_PUBLISH_ROOT` -- `TIMESERIES_DEM_PATH` -- `TIMESERIES_ORBIT_POOL_ISCE2` -- `TIMESERIES_WATER_MASK_ROOT` -- `TIMESERIES_ALLOW_SYNTHETIC_WATER_MASK` -- `TIMESERIES_SMALLBASELINE_TEMPLATE` - -其中可允许默认回退: - -- `TIMESERIES_WSL_DISTRO -> ISCE2_WSL_DISTRO` -- `TIMESERIES_DEM_PATH -> ISCE2_DEM_PATH` -- `TIMESERIES_ORBIT_POOL_ISCE2 -> ORBIT_POOL_ISCE2` - -## 9. 精轨、DEM、水体掩膜策略 - -## 9.1 精轨 - -直接复用现有: - -- source orbit scan -- ENVI / ISCE2 orbit pool sync -- LT-1 orbit XML 解析/生成 helper - -生产要求: - -- 每景影像在运行前就解析到明确 orbit XML -- 运行记录中保存 orbit provenance - -## 9.2 DEM - -生产态不依赖在线下载。 - -要求: - -- 本地 DEM 路径必须可读 -- 运行记录中固定写入 DEM 路径与版本描述 - -## 9.3 水体掩膜 - -这里要区分实验和生产。 - -实验态: - -- 允许 `synthetic waterMask fallback` - -生产态建议: - -- 优先本地水体数据源 -- 若只能 synthetic fallback,则产品可生成,但 `health_status = WARN` -- UI 上必须显式标注“使用了 synthetic water mask” - -## 10. `psinsar` 产品注册策略 - -## 10.1 产品行设计 - -`result_products` 一条记录代表一个 SBAS bundle,而不是单个文件。 - -推荐约定: - -- `product_id = psinsar_` -- `catalog_name = psinsar` -- `product_type = psinsar_bundle` -- `display_name = SBAS___` -- `source_primary_path = assets/geo_timeseries.h5` -- `primary_asset_path = assets/velocity.tif` -- `preview_path = preview/velocity_preview.png` - -### 10.2 产品详情展示 - -产品详情页重点展示: - -- 栈日期列表 -- 参考日期 -- 方向 -- DEM -- 轨道来源 -- 速度图预览 -- 时间序列 cube 下载 -- 质量图层下载 -- 运行日志入口 - -## 11. API 设计 - -### 11.1 生产接口 - -- `POST /timeseries-production/runs` - - 基于 `batch_id` 提交一次 SBAS 运行 -- `GET /timeseries-production/runs` - - 列表 -- `GET /timeseries-production/runs/{run_id}` - - 详情 -- `POST /timeseries-production/runs/{run_id}/retry-step` - - 重试单步 -- `POST /timeseries-production/wsl-check` - - 环境检查 - -### 11.2 产品接口 - -- `GET /ps-products/catalog-status` -- `POST /ps-products/rebuild` -- `GET /ps-products` -- `GET /ps-products/{product_id}` -- `GET /ps-products/{product_id}/preview` - -## 12. 前端页面设计 - -### 12.1 `ps_production` - -页面模块: - -- 选择已有 `ps_task_batch` -- 设定运行参数 -- 提交运行 -- 查看步骤进度 -- 查看失败原因和重试按钮 - -### 12.2 `ps_products` - -页面模块: - -- 产品列表 -- AOI / 时间 / 方向 / 状态筛选 -- preview 缩略图 -- 一键跳转到产品详情 - -### 12.3 `psinsar_results` - -页面模块: - -- 地图展示 `velocity.tif` -- 质量图层切换 -- 时间序列主资产下载 -- 后续再扩展点时序查询 - -## 13. 推荐实施顺序 - -### Phase A:最小生产主链 - -- 新增 `ps_timeseries_runs` -- 新增 `timeseries_production.py` -- 新增 workflow factory -- 先直接复用实验脚本作为受控 worker wrapper - -目标: - -- 系统能发起一次 SBAS 运行 -- 能看到步骤状态 -- 能落到 publish bundle - -### Phase B:产品注册 - -- 新增 `psinsar_catalog_service.py` -- 新增 `ps_products.py` -- 注册 `psinsar.publish.v1` - -目标: - -- 发布 bundle 能在系统里被浏览和重建 - -### Phase C:稳定性与治理 - -- 增加 orbit provenance -- 增加 DEM / water mask 质量标记 -- 增加 post-check 和 health-check -- 完善路径服务 - -### Phase D:UI 完善 - -- 补生产页 -- 补产品页 -- 补结果页 - -## 14. 最终建议 - -结合当前审计结果,最稳妥的路线不是“直接把 SBAS 纳入现有 D-InSAR 产品通道”,而是: - -1. 继续复用 `ps_task_batches` 作为输入栈定义 -2. 新增 `ps_timeseries_runs` 作为生产主记录 -3. 复用现有任务、作业、工作流骨架 -4. 以 publish bundle 为产品注册边界 -5. 先独立实现 `psinsar_catalog_service`,避免影响现有 D-InSAR 生产 - -这样可以做到: - -- 技术上最贴合现有系统 -- 风险上最小 -- 后续也最容易把实验链稳稳升级成正式生产链 diff --git a/docs/archive/SBAS_SYSTEM_INTEGRATION_AUDIT_20260406.md b/docs/archive/SBAS_SYSTEM_INTEGRATION_AUDIT_20260406.md deleted file mode 100644 index e10dede..0000000 --- a/docs/archive/SBAS_SYSTEM_INTEGRATION_AUDIT_20260406.md +++ /dev/null @@ -1,359 +0,0 @@ -# SBAS 系统集成与数据管理审计报告 - -更新日期:2026-04-06 - -## 1. 审计范围 - -本次审计聚焦三件事: - -- 当前 ISCE2 SBAS 实验是否已经真实读取并使用精密轨道 -- 现有系统的数据管理方案是否适合承接 SBAS/PS-InSAR 生产口 -- 如果后续要把实验方案嵌入系统,推荐怎样分阶段落地 - -本报告基于当前仓库实现与已成功的实验产物,不涉及对现有 D-InSAR 生产链的破坏性修改。 - -## 2. 结论摘要 - -- 结论 1:当前 SBAS 实验已经真实读取并使用精密轨道,不只是“数据库里有轨道路径”。 -- 结论 2:当前系统可以完成 PS/SBAS 选片和实验产物生成,但还不能把 `psinsar` 产物作为正式生产结果注册、编目、健康检查和发布。 -- 结论 3:当前数据管理整体偏“路径驱动”,原始数据身份、存储位置、运行产物三层边界不够清晰,因此用户会感到“数据管理比较松散”。 -- 结论 4:轨道管理反而是当前较完整的一块,已经具备源目录扫描、池同步、一致性检查和数据库状态统计能力。 - -## 3. 审计发现 - -### P0 / 高优先级 - -#### 3.1 结果编目和发布入口仍然是 D-InSAR 专用,`psinsar` 实验产物无法直接接入系统 - -证据: - -- `backend/app/services/result_catalog_service.py:707-713` - - `_load_manifest()` 只接受 `product_type == dinsar` -- `backend/app/services/result_catalog_service.py:750-753` - - 即使读入 manifest,也把 `product_type` 固定写成 `dinsar` -- `backend/app/routers/dinsar_products.py:107-140` - - 现有编目 API 和后台任务只暴露 `/dinsar-products/*` -- `backend/app/services/health_service.py:124-170` - - 结果目录健康检查只统计 `catalog_name == dinsar` -- `experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/publish/mintpy_sbas_unified_v1/manifest.json:2-9` - - 当前实验成功产物的 manifest 是 `schema_version = psinsar.publish.v1`,`catalog_name = psinsar` - -影响: - -- 实验已经能产出发布级 bundle,但系统当前不会把它识别为正式产品 -- 不能复用现有结果页、目录重建、健康检查、产品详情接口 -- 后续如果直接“硬塞”进 D-InSAR 目录,会污染现有 D-InSAR 语义 - -建议: - -- Phase 1 不要新建一套完全独立的产品表,继续复用 `result_products` / `result_assets` -- 但必须让 catalog 服务显式支持 `catalog_name = psinsar` -- 最小可行改造是新增 `psinsar` manifest 解析路径,或把当前 catalog service 抽象成多 catalog 分发器 - -#### 3.2 `ps_task_batches` / `ps_task_items` 目前只是选片快照,不是生产记录 - -证据: - -- `backend/app/models/orm.py:516-550` - - `PsTaskBatchORM` / `PsTaskItemORM` 只保存批次、`file_path`、日期、极化、是否有轨道和状态 -- `backend/app/routers/task_batches.py:281-320` - - 创建 `/task-batches/ps` 时只是把选中的影像元数据写入 `ps_task_items` -- `backend/app/routers/pairing.py:124-147` - - 现有 `find-ps-timeseries` 只负责找 stack -- `backend/app/services/spatial_service.py:444-481` - - `find_ps_timeseries_data()` 只返回按轨道方向分组的影像栈 -- `backend/app/services/job_handlers.py:306-321` - - 对 `PS_STACK` 的后台处理目前只是复制选中的影像路径 - -影响: - -- 系统里没有“某次 SBAS 生产运行”的业务主记录 -- 无法稳定记录 `work_dir`、`publish_dir`、运行环境、参数版本、DEM、轨道来源、水体掩膜来源 -- 无法形成真正可追溯、可重跑、可审计的生产口 - -建议: - -- 新增业务表 `ps_timeseries_runs` -- 保留 `ps_task_batches` 作为“输入栈定义” -- 把真正的处理运行、产物发布、失败重试、步骤状态全部挂到 `ps_timeseries_runs + workflow_runs` - -#### 3.3 数据身份与存储路径耦合过深,迁移和重构成本偏高 - -证据: - -- `backend/app/models/orm.py:22-40` - - `RadarDataORM` 直接保存 `file_path`、`orbit_file_path`、`preview_cache_path` -- `backend/app/models/orm.py:82-101` - - `ResultProductORM` 直接保存 `publish_dir`、`manifest_path`、`source_primary_path`、`primary_asset_path` -- `backend/app/models/orm.py:182-195` - - `ResultAssetORM` 保存 `absolute_path` -- `backend/app/services/data_service.py:621-668` - - `unique_id` 通过 `os.path.relpath()` 生成,并按 `unique_id` 做 upsert - -影响: - -- 数据身份会受存储根目录变化影响 -- Windows 根目录变更、WSL 路径调整、NAS 迁移时,数据库主身份和存储位置语义混在一起 -- 产物迁移到正式发布目录后,历史绝对路径会变成系统耦合点 - -建议: - -- 把“数据身份”和“存储位置”拆开 -- 原始影像层至少补充稳定的 `scene_uid` 或 `product_uid` -- 结果层至少补充稳定的 `run_id` / `product_id` 业务主键,把绝对路径退化为可替换的存储定位信息 - -### P1 / 中优先级 - -#### 3.4 原始影像与轨道的关联粒度偏粗,缺少轨道版本与来源追踪 - -证据: - -- `backend/app/services/data_service.py:611-612` - - 影像是否有轨道仅按 `(satellite, imaging_date)` 关联 -- `backend/app/services/data_service.py:688-692` - - 后续补关联时也是按同一键更新 - -影响: - -- 当同一天存在多个候选轨道版本时,数据库层无法区分来源和版本 -- 无法在结果层明确回答“本次 SBAS 用的是哪份轨道文件、其 checksum 是多少、来自 source 目录还是 ISCE2 pool” - -建议: - -- 最小可行做法:在 `RadarDataORM` 或新表中增加 - - `orbit_stem` - - `orbit_checksum` - - `orbit_source_type` - - `orbit_pool_path` -- 更完整做法:单独建 `orbit_assets` / `orbit_bindings` - -#### 3.5 数据扫描服务职责过重,扫描、轨道同步、预览缓存、数据库写入耦合在一起 - -证据: - -- `backend/app/services/data_service.py:426-463` - - 扫描原始轨道并同步 ENVI / ISCE2 池 -- `backend/app/services/data_service.py:568-699` - - 同一流程里完成影像元数据读取、轨道关联、数据库 upsert -- `backend/app/services/data_service.py:709-739` - - 后面又继续做缓存重建候选收集 - -影响: - -- 这个服务变成“总装配间”,测试和回归都更困难 -- 后续要引入 SBAS 生产口时,很容易继续把更多责任塞进同一个服务 - -建议: - -- 拆分为至少四块: - - `radar_inventory_service` - - `orbit_inventory_service` - - `radar_catalog_service` - - `preview_cache_service` - -#### 3.6 Windows/WSL 双路径模型是当前现实,但需要收口到专门的路径层 - -证据: - -- `backend/app/config.py:150-186` - - 同时维护 Windows 轨道池、DEM、IDL、WSL distro、WSL Python 等配置 -- `experiments/isce2_sbas_timeseries/scripts/build_lt1_stack_prep.py:140-149` - - 实验准备阶段会解析 orbit XML -- `experiments/isce2_sbas_timeseries/scripts/build_lt1_stack_prep.py:171-193` - - 同时生成 Windows 路径和 WSL 路径 -- `experiments/isce2_sbas_timeseries/scripts/materialize_lt1_stack_scenes.py:98-103` - - 物化阶段直接把 orbit XML 传给 ISCE2 sensor - -影响: - -- 当前方案能跑通,但如果没有统一的路径转换层,后续每个服务都可能自己拼接 `/mnt/*` -- 这会继续加重“数据管理松散”的体感 - -建议: - -- 保留当前双路径现实,不要硬回避 -- 但把路径转换统一收敛到 `timeseries_paths.py` 或类似组件 -- 生产口只暴露业务字段,不让上层 UI 和业务逻辑直接感知 `/mnt/z` 与 `F:\` - -### P2 / 正向发现 - -#### 3.7 轨道管理是当前系统里相对成熟的一块 - -证据: - -- `backend/app/services/data_service.py:426-463` - - 已有源目录扫描与 ENVI / ISCE2 轨道池同步 -- `backend/app/routers/orbit.py:27-108` - - 已有数据库统计、池一致性检查、缺口汇总 - -结论: - -- 不需要为 SBAS 重新发明一套轨道管理 -- 更合理的方向是复用现有轨道池和一致性检查能力,再补生产级 provenance - -## 4. 关于“现在实验读取精密轨道了吗” - -答案:是,已经读取,而且已进入实际处理链。 - -证据链如下: - -1. 轨道解析入口: - - `backend/app/isce2_pipeline/lt1_input_resolver.py:165-201` - - `ensure_lt1_orbit_xml()` 会优先取已有 XML,找不到才从 LT-1 轨道 txt 生成 XML -2. Stack 准备阶段: - - `experiments/isce2_sbas_timeseries/scripts/build_lt1_stack_prep.py:140-149` - - 每景影像都调用 `ensure_lt1_orbit_xml()` -3. Stack 物化阶段: - - `experiments/isce2_sbas_timeseries/scripts/materialize_lt1_stack_scenes.py:98-103` - - `sensor.orbitFile = orbit_xml` -4. 实验 manifest 证据: - - `experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/stack_input_manifest.json:92-95` - - `experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/stack_input_manifest.json:123-126` - - `experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/stack_input_manifest.json:154-157` - - manifest 已明确记录 `D:\\orbit_pools\\isce2\\LT1A_GpsData_GAS_C_*.xml` -5. 运行日志证据: - - `experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/stack_work/logs/run_01_reference.log:18` - - `experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/stack_work/logs/run_03_geo2rdr_coarseResamp.log:6` - - 日志中已出现 `Orbit interpolation method: hermite` - -结论补充: - -- 当前 SBAS 实验使用的是 ISCE2 轨道池中的 XML 精轨 -- 因此“精轨是否已读入”这件事在实验层已经成立 -- 真正缺的不是“有没有轨道”,而是“系统如何把这次运行及其轨道 provenance 规范地登记为生产记录” - -## 5. 集成设计建议 - -### 5.1 集成边界 - -当前最合理的系统集成边界不是 MintPy 运行目录,而是发布级 bundle: - -- 入口文件:`manifest.json` -- 目录结构:`assets/`、`preview/`、`metadata/` - -这与现有产品规范一致: - -- `docs/ISCE2_SBAS_PRODUCT_SPEC.md:69-88` - -因此: - -- 不要直接注册 `stack_work/mintpy_sbas_*` -- 要注册 `publish/.../manifest.json` 所代表的一整个 bundle - -### 5.2 推荐最小落地架构 - -#### 层 1:规划层,继续复用现有能力 - -- 继续使用 `find-ps-timeseries` -- 继续使用 `ps_task_batches` / `ps_task_items` -- 其职责只保留为“选片与栈定义” - -#### 层 2:生产层,新增业务主记录 - -新增 `ps_timeseries_runs`,至少包含: - -- `run_id` -- `batch_id` -- `catalog_name` -- `engine_code` -- `processor_code` -- `env_name` -- `wsl_distro` -- `work_dir` -- `publish_dir` -- `manifest_path` -- `reference_date` -- `direction` -- `params_json` -- `summary_json` -- `orbit_summary_json` -- `dem_path` -- `water_mask_source` -- `status` -- `error_message` -- `started_at` -- `ended_at` - -#### 层 3:编目层,扩展现有结果目录 - -不要单独再造一套产品表,继续复用: - -- `result_products` -- `result_assets` -- `result_issues` - -但要新增 `psinsar` catalog 入口,支持: - -- 解析 `psinsar.publish.v1` -- 注册 `catalog_name = psinsar` -- 单独的 catalog status / rebuild / health-check - -#### 层 4:接口层 - -建议新增: - -- `POST /timeseries-production/runs` -- `GET /timeseries-production/runs` -- `GET /timeseries-production/runs/{run_id}` -- `POST /timeseries-production/runs/{run_id}/retry-step` -- `GET /ps-products` -- `GET /ps-products/{product_id}` -- `POST /ps-products/rebuild-catalog` - -### 5.3 与当前设计文档的一致性 - -当前仓库里的 SBAS 设计文档已经提前写出了这条路线: - -- `docs/ISCE2_SBAS_TIMESERIES_DESIGN.md:227` - - 建议新增 `ps_timeseries_runs` -- `docs/ISCE2_SBAS_TIMESERIES_DESIGN.md:292-296` - - 建议新增 SBAS 相关 job types -- `docs/ISCE2_SBAS_TIMESERIES_DESIGN.md:306-313` - - 建议新增 `timeseries_production` / `ps_products` / `psinsar_catalog_service` -- `docs/ISCE2_SBAS_TIMESERIES_DESIGN.md:563-575` - - TODO 中已明确列出 ORM、路由、job handler、catalog 扩展和健康检查 - -所以本次审计的判断不是与现有设计冲突,而是说明: - -- 实验层已经坐实 -- 代码现实也证明下一步确实该进入“生产层与 catalog 层补齐” - -## 6. 建议 TODO - -### 6.1 立即做 - -- [ ] 新增 `ps_timeseries_runs` ORM / schema / migration -- [ ] 新增 `timeseries_production` router -- [ ] 新增 `PUBLISH_PSINSAR_PRODUCTS` 及相关 job handlers -- [ ] 让 catalog 服务支持 `psinsar.publish.v1` -- [ ] 把 `publish/.../manifest.json` 作为唯一注册入口 - -### 6.2 紧接着做 - -- [ ] 给 `ps_timeseries_runs` 记录 DEM、轨道、水体掩膜、运行环境版本 -- [ ] 给轨道绑定补 provenance 字段或单独轨道资产表 -- [ ] 把 Windows/WSL 路径转换收敛到独立路径服务 -- [ ] 为 `psinsar` 增加 catalog health-check 与 rebuild - -### 6.3 暂缓做 - -- [ ] 不要现在就改动现有 D-InSAR 生产链 -- [ ] 不要把 SBAS 运行目录直接暴露为产品目录 -- [ ] 不要为了 SBAS 先拆掉现有 `result_products` - -## 7. 审计结论 - -如果现在问“SBAS 能不能开始往系统里接”,答案是: - -- 能,但应该先接“发布 bundle + 生产记录 + psinsar catalog” -- 不能直接把实验目录粗暴塞进现有 D-InSAR 产品体系 - -如果现在问“系统的数据管理为什么显得松散”,核心原因是: - -- 原始数据身份、物理存储路径、生产产物目录三层目前还没有彻底分开 - -如果现在问“下一步最值得做什么”,答案是: - -- 先把 `ps_timeseries_runs + psinsar catalog` 这两个缺口补上 - -这样既不会动坏现有 D-InSAR 生产,又能把已经验证成功的 SBAS 实验稳稳接成系统能力。 diff --git a/docs/archive/SBAS_TASK_PATH_MODE_EVALUATION_20260406.md b/docs/archive/SBAS_TASK_PATH_MODE_EVALUATION_20260406.md deleted file mode 100644 index dc6a1e4..0000000 --- a/docs/archive/SBAS_TASK_PATH_MODE_EVALUATION_20260406.md +++ /dev/null @@ -1,319 +0,0 @@ -# SBAS 是否可复用现有 D-InSAR Task 路径生产模式 - -更新日期:2026-04-06 - -## 1. 结论 - -结论分两层: - -- 可以复用“以标准输入目录作为生产入口”的模式 -- 不能直接复用现有 D-InSAR 的 `Task_x/master + slave` 目录协议和 `copy-ps-stack` 实现 - -也就是说: - -- 可以借用 D-InSAR 的生产思想 -- 不能照搬 D-InSAR 的输入结构 - -## 2. 现有 D-InSAR 生产模式是什么 - -当前 D-InSAR 的 ISCE2 生产模式,本质上是: - -1. 先把一对影像整理成标准任务目录 -2. 每个任务目录固定包含: - - `master/` - - `slave/` - - `.dinsar_pair.json` -3. 生产入口只接收一个 `root_dir` -4. 引擎自行扫描这个 `root_dir` - - 要么它本身就是一个 task 目录 - - 要么它下面有多个 `Task_*` 子目录 -5. 每个 task 目录独立产出一个 pair result - -关键实现: - -- `backend/app/dinsar_engines/isce2_engine.py:314-361` - - `validate_root_dir()` 要求: - - 单个 task 目录必须有 `master` 和 `slave` - - 或父目录下有多个合法 `Task_*` -- `backend/app/copier.py:178-218` - - D-InSAR 复制时会构建: - - `//master` - - `//slave` - - `.dinsar_pair.json` -- `backend/app/isce2_pipeline/run_lt1_dinsar_pipeline.py:262-298` - - pipeline 运行时明确按 `master` / `slave` 两景读取 - -所以这是一套“面向影像对”的输入协议。 - -## 3. 现有 PS/SBAS 选片算法能不能找出时序队列 - -可以,但当前更准确地说,它是: - -- 一个“候选栈发现器” -- 还不是一个“生产级栈定义器” - -现有实现: - -- `backend/app/services/spatial_service.py:414-481` - -当前逻辑会: - -- 先选出与 AOI 初始重叠比例满足阈值的影像 -- 按轨道方向分组 -- 求每个方向的公共重叠区 -- 再筛出对公共区覆盖比例满足阈值的影像 -- 最终按日期排序返回 - -这对 SBAS 是有价值的,因为它已经能给出: - -- 同轨方向 -- 共享公共覆盖区 -- 时间排序后的影像列表 - -但它当前还缺少若干生产级约束: - -- 没有显式要求 `has_orbit_data = True` -- 没有显式要求相同 `imaging_mode` -- 没有显式要求相同 `polarization` -- 没有最小栈规模规则 -- 没有最大时间间隔或参考影像策略 -- 没有把结果固化为“一个稳定的 stack identity” - -所以答案是: - -- 用来找 SBAS 候选时序队列,可以 -- 直接当生产入口,不够 - -## 4. 为什么 D-InSAR 的 task 路径模式不能原样套给 SBAS - -### 4.1 D-InSAR 是二元输入,SBAS 是栈输入 - -D-InSAR 当前 pipeline 只解析两个角色: - -- `master` -- `slave` - -见: - -- `backend/app/isce2_pipeline/run_lt1_dinsar_pipeline.py:262-298` - -而 SBAS 至少需要: - -- 一个有序场景集合 -- 栈级别的公共参数 -- 栈级别的参考日期 -- 栈级别的输出目录约定 - -因此 SBAS 的输入目录协议必须是“stack contract”,不是“pair contract”。 - -### 4.2 当前 `copy-ps-stack` 是平铺复制,不会生成栈元数据 - -现有 PS 复制逻辑: - -- `backend/app/copier.py:45-115` - -它的行为是: - -- 从 `PsTaskItemORM.file_path` 取路径 -- 优先复制 `original_path + "_envi_import"` -- 否则复制原路径 -- 直接平铺复制到 `dest_dir/` - -它不会生成: - -- 栈 manifest -- 日期排序 -- 参考日期 -- 轨道清单 -- DEM / water mask / publish 约定 - -因此这套复制结果目前更像“把选中的影像搬过去”,不是“形成一个可投产的 SBAS 输入包”。 - -### 4.3 当前 PS 批次模型保存的信息不够 - -现有: - -- `backend/app/models/orm.py:532-550` -- `backend/app/routers/task_batches.py:303-312` - -`PsTaskItemORM` 只保存: - -- `file_path` -- `satellite` -- `imaging_date` -- `polarization` -- `has_orbit_data` - -没有保存: - -- `orbit_direction` -- `imaging_mode` -- 稳定 `group_key` -- 公共覆盖区 -- 参考影像策略 - -这说明 PS 批次目前还是“输入快照”,不是“可直接投产的 stack contract”。 - -## 5. 哪部分可以直接复用 - -可以直接复用的,不少。 - -### 5.1 生产入口模式 - -复用思路: - -- 一个 `root_dir` -- 引擎自己扫描这个目录 -- 支持“单个 stack 目录”或“父目录下多个 stack 目录” - -这点可以直接仿照: - -- `backend/app/dinsar_engines/isce2_engine.py:314-361` - -只是把目录判定从: - -- `master + slave` - -换成: - -- `stack_input_manifest.json` 或 `.ps_stack.json` - -### 5.2 作业提交模式 - -可以直接复用 D-InSAR 当前的: - -- router 提交任务 -- `task_service.create_task()` -- `job_queue_service.create_job()` -- worker 执行 -- 返回单次运行状态 - -### 5.3 目录驱动执行 - -这部分思想也可以复用: - -- 输入目录是唯一入口 -- engine 不依赖前端临时状态 -- 只要目录契约稳定,就可以重复运行 - -这对 SBAS 是对的。 - -## 6. SBAS 推荐的“task 路径模式” - -建议做一个 SBAS 版输入目录协议,而不是复用 D-InSAR 的 `Task_*` 结构。 - -推荐: - -```text -Stack_/ - stack_input_manifest.json - scenes/ - 20250118/ - source_scene.json - - 20250315/ - source_scene.json - - 20250510/ - source_scene.json - - inputs/ - dem/ - logs/ -``` - -后续运行阶段再生成: - -```text -Stack_/ - stack_input_manifest.json - scenes/ - SLC/ - stack_work/ - publish/ - logs/ -``` - -## 7. 推荐的 `stack_input_manifest.json` 最小字段 - -至少应包含: - -- `group_key` -- `batch_id` -- `mode` - - `sbas` -- `direction` -- `reference_date` -- `stack_dates` -- `scene_count` -- `dem_path_windows` -- `dem_path_wsl` -- `orbit_pool_windows` -- `orbit_pool_wsl` -- `publish_dir_windows` -- `publish_dir_wsl` -- `water_mask_mode` -- `scenes[]` - - `date` - - `file_path` - - `satellite` - - `imaging_mode` - - `polarization` - - `orbit_direction` - - `orbit_xml_path` - - `has_orbit_data` - -这个文件对 SBAS 的作用,等价于 D-InSAR 里的 `.dinsar_pair.json`,但它描述的是一个栈,不是一对。 - -## 8. 物理复制要不要照搬 D-InSAR - -不建议原样照搬。 - -原因: - -- SBAS 常常是 5 景、10 景甚至更多 -- 全量复制原始影像会放大 IO 和存储成本 -- 你当前原始数据已经在 WSL 可见路径下,不一定需要再复制一遍 - -更合理的 Phase 1 做法: - -- 保留“输入目录”概念 -- 但目录里优先存: - - manifest - - sidecar metadata - - 规范化链接或原路径引用 -- 仅在必要时物化最小运行所需文件 - -也就是说: - -- 推荐“目录驱动” -- 不推荐“全量复制驱动” - -## 9. 最终判断 - -如果你的问题是: - -- “现有匹配算法能不能找出 SBAS 需要的时序影像队列?” - -答案是: - -- 可以作为候选栈发现器 -- 但还需要补生产级校验和栈固化 - -如果你的问题是: - -- “能不能沿用 D-InSAR 生产时的输入 task 路径模式?” - -答案是: - -- 可以沿用这个模式的思想 -- 不能沿用现有 D-InSAR 的具体目录协议和复制实现 - -最推荐的落地方式是: - -1. 保留“目录作为生产入口” -2. 为 SBAS 定义独立的 `Stack_*` 输入契约 -3. 用 `stack_input_manifest.json` 替代 `.dinsar_pair.json` -4. 用 stack-aware 的 prepare/materialize 步骤替代当前 `copy-ps-stack` 平铺复制 - -这样既能继承现有系统的生产模式,又不会把 SBAS 错套成二景 D-InSAR。 diff --git a/docs/archive/SECURITY_AUDIT_2026-03-04.md b/docs/archive/SECURITY_AUDIT_2026-03-04.md deleted file mode 100644 index 49873f2..0000000 --- a/docs/archive/SECURITY_AUDIT_2026-03-04.md +++ /dev/null @@ -1,89 +0,0 @@ -# 项目代码审计报告(2026-03-04) - -审计范围:后端核心链路、前端鉴权调用链、启动与脚本安全边界。 -审计方式:静态只读审计(未修改业务代码)。 - -## 1. 高危问题 - -### 1.1 ENVI 主流程成功后不返回结果,导致下游按 `dict` 使用时崩溃 -- 证据: - - `backend/app/services/envi_service.py:1568` - - `backend/app/services/envi_service.py:1630` - - `backend/app/services/envi_runner_cli.py:44` - - `backend/app/services/envi_runner_cli.py:51` - - `backend/app/services/job_handlers.py:1020` - - `backend/app/services/job_handlers.py:1031` -- 影响:任务可能在实际执行后仍被标记失败或抛异常。 - -### 1.2 只读用户可通过 `GET` 触发写操作和高开销构建,绕过读写分离意图 -- 证据: - - `backend/app/routers/radar.py:619` - - `backend/app/routers/radar.py:365` - - `backend/app/routers/radar.py:346` - - `backend/app/routers/dinsar.py:214` - - `backend/app/routers/dinsar.py:146` - - `backend/app/routers/dependencies.py:196` - - `backend/app/routers/dependencies.py:315` -- 影响:读账号可诱发缓存重建/数据库写入,存在资源滥用风险。 - -### 1.3 AOI token 内存存储无容量上限,且 token 可续期 -- 证据: - - `backend/app/routers/dependencies.py:131` - - `backend/app/routers/dependencies.py:825` - - `backend/app/routers/dependencies.py:850` - - `backend/app/routers/dependencies.py:59` - - `backend/app/routers/radar.py:439` - - `backend/app/routers/radar.py:509` -- 影响:认证后低权限账号可通过频繁检索制造进程内存膨胀(DoS 面)。 - -## 2. 中危问题 - -### 2.1 `extract_disp_results` 失败计数重复累加 -- 证据: - - `backend/app/services/envi_service.py:1717` - - `backend/app/services/envi_service.py:1726` - - `backend/app/services/envi_service.py:1729` -- 影响:统计报表和告警判断失真。 - -### 2.2 AOI 文件解析异常未统一转为 4xx,用户输入可触发 500 -- 证据: - - `backend/app/routers/dependencies.py:792` - - `backend/app/routers/dependencies.py:800` -- 影响:可用性下降,错误语义不一致。 - -### 2.3 路径归属判断使用 `startswith`,存在前缀误匹配 -- 证据: - - `backend/app/services/data_service.py:530` -- 影响:可能将不属于监控根目录的路径误判为合法。 - -### 2.4 解包安全校验仅检查 `member.name`,仍使用 `extractall` -- 证据: - - `scripts/unpack_archives.py:98` - - `scripts/unpack_archives.py:107` - - `scripts/unpack_archives.py:181` -- 影响:若归档文件不可信,仍可能存在目录逃逸/覆盖风险(尤其符号链接/硬链接场景)。 - -## 3. 低危问题 - -### 3.1 启动脚本写 `nginx.conf` 未显式编码 -- 证据: - - `scripts/start_app.ps1:388` -- 影响:在 Windows PowerShell 5 环境可能写出 UTF-16,导致 Nginx 配置解析异常。 - -## 4. 已复核并排除 - -### 4.1 `task_service.create_task` 的 `task_id` 未定义问题为误报 -- 证据: - - `backend/app/services/task_service.py:153` -- 结论:`task_id` 已正常赋值。 - -## 5. 假设与待确认 - -1. 解包问题是否上调为“高危”,取决于归档输入是否可被外部/低信任来源控制。 -2. “只读账号触发缓存构建”是否视为权限缺陷,取决于产品策略是否允许此行为。 - -## 6. 说明 - -- 本报告对应一次只读审计,不包含代码修复提交。 -- 可在确认后按优先级先修复:1.1 / 1.2 / 1.3。 - diff --git a/docs/archive/SECURITY_AUDIT_ANALYSIS_2026-03-04.md b/docs/archive/SECURITY_AUDIT_ANALYSIS_2026-03-04.md deleted file mode 100644 index 8ca7500..0000000 --- a/docs/archive/SECURITY_AUDIT_ANALYSIS_2026-03-04.md +++ /dev/null @@ -1,577 +0,0 @@ -# 安全审计分析与修复方案(2026-03-04) - -## 审计概述 - -本文档针对 `SECURITY_AUDIT_2026-03-04.md` 中提出的安全问题进行详细分析,并提供具体的修复方案。 - -审计发现: -- 🔴 高危问题:3 个 -- 🟡 中危问题:4 个 -- 🟢 低危问题:1 个 - ---- - -## 1. 高危问题分析与修复方案 - -### 1.1 ENVI 主流程成功后不返回结果 - -**问题描述**: -- `envi_service.py:run_workflow()` 在成功执行后不返回 `record` 字典 -- `envi_runner_cli.py` 期望打印 JSON 结果,但 `run_workflow()` 在成功时返回 `None` -- `job_handlers.py:_run_envi_workflow_job()` 尝试将返回值作为字典使用,导致崩溃 - -**根本原因**: -```python -# envi_service.py:1630 -if error: - raise RuntimeError(error) -# 缺少: return record -``` - -**影响等级**:🔴 高危 -- 所有 ENVI 工作流任务在成功执行后会被误判为失败 -- 下游代码尝试访问 `run_meta.get('workflow')` 时会抛出 `AttributeError` - -**修复方案**: -```python -# envi_service.py:1630 之后添加 -if error: - raise RuntimeError(error) -return record # 添加此行 -``` - -**验证方法**: -1. 运行一个 D-InSAR 工作流任务 -2. 检查任务状态是否正确标记为 SUCCESS -3. 检查日志中是否包含 `workflow=dinsar_custom duration=XXXs` - ---- - -### 1.2 只读用户可通过 GET 触发写操作 - -**问题描述**: -多个 GET 端点在只读操作中触发了数据库写入和高开销计算: - -1. **`GET /radar-data/{data_id}/thumb`** (radar.py:622) - - 调用 `_get_cached_radar_preview()` → `_build_radar_preview_cache()` - - 触发预览图生成(GDAL 处理)并写入数据库 - -2. **`GET /radar-data/imaging-dates`** (radar.py:609) - - 虽然是只读查询,但在 `_build_radar_preview_cache()` 中会更新数据库 - -3. **`GET /dinsar-results/{result_id}/preview`** (dinsar.py:214) - - 类似问题,按需构建预览缓存 - -**根本原因**: -```python -# radar.py:365 -if settings.RADAR_PREVIEW_BUILD_ON_DEMAND: - build_status = await _build_radar_preview_cache(record, db, force=False) - # 内部会执行 db.add(record) 和 db.commit() -``` - -**影响等级**:🔴 高危 -- 只读账号可以触发大量 GDAL 处理任务,导致 CPU/内存耗尽(DoS) -- 违反最小权限原则,读账号不应有写权限 -- 可能导致数据库锁竞争 - -**修复方案**: - -**方案 A:权限分离(推荐)** -```python -# 在 dependencies.py 中添加权限检查 -def require_write_permission(current_user: User = Depends(get_current_user)): - if current_user.role == "readonly": - raise HTTPException( - status_code=403, - detail="此操作需要写权限" - ) - return current_user - -# 在 radar.py 中修改端点 -@router.get("/radar-data/{data_id}/thumb") -async def get_radar_data_thumb_endpoint( - data_id: int, - db: AsyncSession = Depends(get_db), - _: User = Depends(require_write_permission) # 添加权限检查 -): - # 仅当用户有写权限时才允许按需构建 - ... -``` - -**方案 B:配置控制** -```python -# 在 .env 中添加 -RADAR_PREVIEW_BUILD_ON_DEMAND_READONLY=false - -# 在 radar.py 中修改 -if settings.RADAR_PREVIEW_BUILD_ON_DEMAND: - if current_user.role == "readonly" and not settings.RADAR_PREVIEW_BUILD_ON_DEMAND_READONLY: - # 只返回已有缓存,不触发构建 - raise HTTPException(status_code=404, detail="预览图未生成") - build_status = await _build_radar_preview_cache(record, db, force=False) -``` - -**方案 C:改为 POST 端点(最符合 RESTful 原则)** -```python -# 将按需构建改为显式 POST 操作 -@router.post("/radar-data/{data_id}/build-preview") -async def build_radar_preview_endpoint( - data_id: int, - db: AsyncSession = Depends(get_db), - current_user: User = Depends(require_write_permission) -): - # 显式构建预览图 - ... - -# GET 端点只返回已有缓存 -@router.get("/radar-data/{data_id}/thumb") -async def get_radar_data_thumb_endpoint(data_id: int, db: AsyncSession = Depends(get_db)): - # 不触发构建,只返回已有文件 - if not os.path.exists(geo_cache_path): - raise HTTPException(status_code=404, detail="预览图未生成,请先调用构建接口") - return FileResponse(geo_cache_path, ...) -``` - -**推荐方案**:方案 C(最符合 RESTful 原则) - ---- - -### 1.3 AOI token 内存存储无容量上限 - -**问题描述**: -- `_AOI_TOKEN_STORE` 是全局字典,无大小限制 -- 每次查询 AOI 时会续期 token(`expires_at = now + AOI_TOKEN_TTL_SECONDS`) -- 恶意用户可以通过频繁查询不同 AOI 区域来填满内存 - -**根本原因**: -```python -# dependencies.py:131 -_AOI_TOKEN_STORE: Dict[str, Dict[str, Any]] = {} # 无容量限制 - -# dependencies.py:850 -payload["expires_at"] = now + AOI_TOKEN_TTL_SECONDS # 每次访问都续期 -``` - -**影响等级**:🔴 高危 -- 认证后的低权限账号可以制造内存泄漏 -- 30 分钟 TTL + 续期机制 = 长期占用内存 -- 可能导致进程 OOM 崩溃 - -**修复方案**: - -**方案 A:添加容量上限(推荐)** -```python -# dependencies.py -AOI_TOKEN_MAX_STORE_SIZE = read_int_env( - "AOI_TOKEN_MAX_STORE_SIZE", - 1000, # 最多存储 1000 个 token - minimum=100, - maximum=10000, -) - -async def _store_aoi_token(aoi_wkt: str, feature_collection: Dict[str, Any]) -> str: - token = uuid.uuid4().hex - now = time.monotonic() - async with _AOI_TOKEN_LOCK: - _cleanup_expired_aoi_tokens(now) - - # 检查容量上限 - if len(_AOI_TOKEN_STORE) >= AOI_TOKEN_MAX_STORE_SIZE: - # 删除最旧的 token(按 expires_at 排序) - oldest_tokens = sorted( - _AOI_TOKEN_STORE.items(), - key=lambda x: x[1]["expires_at"] - )[:100] # 删除最旧的 100 个 - for old_token, _ in oldest_tokens: - _AOI_TOKEN_STORE.pop(old_token, None) - - _AOI_TOKEN_STORE[token] = { - "aoi_wkt": aoi_wkt, - "aoi_geojson": feature_collection, - "expires_at": now + AOI_TOKEN_TTL_SECONDS, - } - return token -``` - -**方案 B:限制续期次数** -```python -# 在 token payload 中添加续期计数 -_AOI_TOKEN_STORE[token] = { - "aoi_wkt": aoi_wkt, - "aoi_geojson": feature_collection, - "expires_at": now + AOI_TOKEN_TTL_SECONDS, - "renew_count": 0, # 添加续期计数 - "max_renews": 5, # 最多续期 5 次 -} - -# 在 _get_aoi_from_token 中检查 -async def _get_aoi_from_token(aoi_token: Optional[str]) -> Optional[Tuple[str, Dict[str, Any]]]: - ... - payload = _AOI_TOKEN_STORE.get(normalized_token) - if not payload: - return None - - # 检查续期次数 - if payload.get("renew_count", 0) >= payload.get("max_renews", 5): - # 达到续期上限,不再续期 - return payload.get("aoi_wkt"), payload.get("aoi_geojson") - - payload["expires_at"] = now + AOI_TOKEN_TTL_SECONDS - payload["renew_count"] = payload.get("renew_count", 0) + 1 - return payload.get("aoi_wkt"), payload.get("aoi_geojson") -``` - -**方案 C:使用 Redis(生产环境推荐)** -```python -# 使用 Redis 替代内存字典,自动过期 -import redis.asyncio as redis - -_redis_client = redis.Redis( - host=settings.REDIS_HOST, - port=settings.REDIS_PORT, - decode_responses=False, -) - -async def _store_aoi_token(aoi_wkt: str, feature_collection: Dict[str, Any]) -> str: - token = uuid.uuid4().hex - payload = { - "aoi_wkt": aoi_wkt, - "aoi_geojson": feature_collection, - } - await _redis_client.setex( - f"aoi_token:{token}", - AOI_TOKEN_TTL_SECONDS, - json.dumps(payload) - ) - return token -``` - -**推荐方案**:方案 A(短期)+ 方案 C(长期) - ---- - -## 2. 中危问题分析与修复方案 - -### 2.1 extract_disp_results 失败计数重复累加 - -**问题描述**: -```python -# envi_service.py:1717, 1726, 1729 -except OSError as e: - task_failed += 1 - failed += 1 # 第一次累加 - task_status = f"error: {e}" -... -failed += task_failed # 第二次累加(1729 行) -``` - -**影响等级**:🟡 中危 -- 统计数据不准确,影响监控和告警 -- 可能导致误报(失败数翻倍) - -**修复方案**: -```python -# envi_service.py:1717 和 1726 行,删除 failed += 1 -except OSError as e: - task_failed += 1 - # 删除: failed += 1 - task_status = f"error: {e}" - -# 保留 1729 行的汇总 -failed += task_failed # 只在这里累加一次 -``` - ---- - -### 2.2 AOI 文件解析异常未统一转为 4xx - -**问题描述**: -```python -# dependencies.py:792, 800 -# 解析 GeoJSON/Shapefile 时的异常直接抛出,返回 500 -aoi_wkt = _parse_geojson_to_wkt(geojson_data) # 可能抛出 ValueError -``` - -**影响等级**:🟡 中危 -- 用户输入错误导致 500 错误,应该返回 400 -- 错误语义不一致,影响 API 可用性 - -**修复方案**: -```python -# dependencies.py 中添加统一异常处理 -try: - aoi_wkt = _parse_geojson_to_wkt(geojson_data) -except (ValueError, KeyError, TypeError) as e: - raise HTTPException( - status_code=400, - detail=f"GeoJSON 格式错误: {str(e)}" - ) -except Exception as e: - logger.error(f"AOI 解析失败: {e}") - raise HTTPException( - status_code=500, - detail="AOI 解析失败,请联系管理员" - ) -``` - ---- - -### 2.3 路径归属判断使用 startswith - -**问题描述**: -```python -# data_service.py:530 -if not any(file_path_abs.startswith(root) for root in radar_roots): - continue -``` - -**影响等级**:🟡 中危 -- 前缀误匹配:`/data/radar` 会匹配 `/data/radar_backup` -- 可能将不属于监控目录的文件误判为合法 - -**修复方案**: -```python -# 使用 os.path.commonpath 或规范化路径比较 -def is_subpath(child: str, parent: str) -> bool: - """检查 child 是否是 parent 的子路径""" - try: - child_abs = os.path.abspath(child) - parent_abs = os.path.abspath(parent) - common = os.path.commonpath([child_abs, parent_abs]) - return common == parent_abs - except (ValueError, TypeError): - return False - -# data_service.py:530 -if not any(is_subpath(file_path_abs, root) for root in radar_roots): - continue -``` - ---- - -### 2.4 解包安全校验仅检查 member.name - -**问题描述**: -```python -# unpack_archives.py:98-104 -def _is_safe_tar_member(member_name): - norm_name = os.path.normpath(member_name) - if os.path.isabs(norm_name): - return False - if norm_name.startswith(".."): - return False - return True - -# 但仍使用 extractall,未检查符号链接/硬链接 -tar.extractall(path=tmp_dir) # 181 行 -``` - -**影响等级**:🟡 中危 -- 符号链接可以指向任意路径(如 `/etc/passwd`) -- 硬链接可以覆盖系统文件 -- 如果归档文件来自不可信来源,存在目录逃逸风险 - -**修复方案**: -```python -# unpack_archives.py -def _is_safe_tar_member(member): - """检查 tar 成员是否安全(包括符号链接检查)""" - # 检查路径 - norm_name = os.path.normpath(member.name) - if os.path.isabs(norm_name): - return False - if norm_name.startswith("..") or norm_name.startswith("../") or norm_name.startswith("..\\"): - return False - - # 检查符号链接 - if member.issym() or member.islnk(): - # 检查链接目标是否安全 - link_target = member.linkname - if os.path.isabs(link_target) or link_target.startswith(".."): - return False - - return True - -def _validate_tar_members(tar_obj, archive_path): - for member in tar_obj.getmembers(): - if not _is_safe_tar_member(member): # 传入 member 对象而非 name - raise IOError(f"unsafe tar entry detected: {member.name} in {archive_path}") - -# 使用安全的逐个提取方式 -def _safe_extract_tar(tar_obj, output_dir): - """安全地提取 tar 文件""" - for member in tar_obj.getmembers(): - if not _is_safe_tar_member(member): - raise IOError(f"unsafe tar entry: {member.name}") - - # 逐个提取,避免 extractall - tar_obj.extract(member, path=output_dir, filter='data') # Python 3.12+ - # 或者对于旧版本: - # tar_obj.extract(member, path=output_dir) - -# 在 181 行替换 -with tarfile.open(archive_path, "r:*") as tar: - _validate_tar_members(tar, archive_path) - _safe_extract_tar(tar, tmp_dir) # 替换 tar.extractall -``` - ---- - -## 3. 低危问题分析与修复方案 - -### 3.1 启动脚本写 nginx.conf 未显式编码 - -**问题描述**: -```powershell -# start_app.ps1:388 -$NewConfContent | Set-Content -LiteralPath "$NginxConfPath" -NoNewline -# 未指定 -Encoding UTF8 -``` - -**影响等级**:🟢 低危 -- PowerShell 5 默认使用 UTF-16 LE 编码 -- Nginx 期望 UTF-8 编码,可能导致配置解析失败 - -**修复方案**: -```powershell -# start_app.ps1:388 -$NewConfContent | Set-Content -LiteralPath "$NginxConfPath" -NoNewline -Encoding UTF8 -``` - ---- - -## 4. 修复优先级建议 - -### P0(立即修复) -1. ✅ **1.1 ENVI 主流程返回值缺失** - 影响所有工作流任务 -2. ✅ **1.2 只读用户触发写操作** - 权限绕过 + DoS 风险 - -### P1(本周内修复) -3. ✅ **1.3 AOI token 内存泄漏** - DoS 风险 -4. ✅ **2.1 失败计数重复累加** - 影响监控准确性 - -### P2(两周内修复) -5. ✅ **2.2 AOI 解析异常处理** - API 可用性问题 -6. ✅ **2.3 路径归属判断** - 潜在安全风险 - -### P3(一个月内修复) -7. ✅ **2.4 解包安全校验** - 取决于归档文件来源可信度 -8. ✅ **3.1 PowerShell 编码** - 低影响,但易修复 - ---- - -## 5. 修复验证清单 - -### 1.1 ENVI 主流程返回值 -- [ ] 运行 D-InSAR 工作流任务 -- [ ] 检查任务状态是否为 SUCCESS -- [ ] 检查日志是否包含 `workflow=dinsar_custom` - -### 1.2 只读用户权限 -- [ ] 使用只读账号访问 `/radar-data/{id}/thumb` -- [ ] 验证是否返回 403 或 404(不触发构建) -- [ ] 使用写权限账号验证按需构建仍可用 - -### 1.3 AOI token 容量 -- [ ] 创建 1000+ 个不同 AOI token -- [ ] 验证内存占用是否稳定 -- [ ] 检查日志是否有 token 清理记录 - -### 2.1 失败计数 -- [ ] 运行 `extract_disp_results` 并故意触发失败 -- [ ] 验证返回的 `failed` 计数是否准确 - -### 2.2 AOI 解析异常 -- [ ] 上传无效 GeoJSON 文件 -- [ ] 验证是否返回 400 而非 500 - -### 2.3 路径归属判断 -- [ ] 创建 `/data/radar_backup/test.tif` 文件 -- [ ] 验证是否被误判为 `/data/radar` 的子路径 - -### 2.4 解包安全 -- [ ] 创建包含符号链接的 tar 文件 -- [ ] 验证是否被拒绝解包 - -### 3.1 PowerShell 编码 -- [ ] 在 PowerShell 5 环境运行启动脚本 -- [ ] 验证 nginx.conf 是否为 UTF-8 编码 - ---- - -## 6. 长期改进建议 - -### 6.1 引入速率限制 -```python -# 使用 slowapi 限制 API 调用频率 -from slowapi import Limiter -from slowapi.util import get_remote_address - -limiter = Limiter(key_func=get_remote_address) - -@router.get("/radar-data/{data_id}/thumb") -@limiter.limit("10/minute") # 每分钟最多 10 次 -async def get_radar_data_thumb_endpoint(...): - ... -``` - -### 6.2 添加审计日志 -```python -# 记录所有写操作和高开销操作 -async def audit_log( - user_id: int, - action: str, - resource: str, - details: Dict[str, Any] -): - await db.execute( - insert(AuditLogORM).values( - user_id=user_id, - action=action, - resource=resource, - details=details, - timestamp=datetime.utcnow(), - ) - ) -``` - -### 6.3 使用 Redis 替代内存缓存 -- AOI token 存储 -- 预览图构建队列 -- 速率限制计数器 - -### 6.4 添加资源配额 -```python -# 限制每个用户的资源使用 -USER_QUOTA = { - "readonly": { - "max_concurrent_requests": 5, - "max_aoi_tokens": 10, - "max_preview_builds_per_hour": 0, - }, - "user": { - "max_concurrent_requests": 20, - "max_aoi_tokens": 50, - "max_preview_builds_per_hour": 100, - }, - "admin": { - "max_concurrent_requests": 100, - "max_aoi_tokens": 500, - "max_preview_builds_per_hour": 1000, - }, -} -``` - ---- - -## 7. 总结 - -本次审计发现了 **3 个高危问题**、**4 个中危问题**、**1 个低危问题**,主要集中在: -1. 函数返回值缺失导致任务失败 -2. 权限控制不严格(读账号可触发写操作) -3. 资源无限制(内存泄漏风险) -4. 统计数据不准确 -5. 异常处理不规范 - -建议按照优先级逐步修复,并在修复后进行充分的回归测试。长期来看,应该引入更完善的权限管理、资源配额、审计日志和速率限制机制。 diff --git a/docs/archive/SECURITY_FIX_CHECKLIST.md b/docs/archive/SECURITY_FIX_CHECKLIST.md deleted file mode 100644 index 2da04a1..0000000 --- a/docs/archive/SECURITY_FIX_CHECKLIST.md +++ /dev/null @@ -1,330 +0,0 @@ -# 安全修复快速检查清单 - -## 使用说明 -- 每次修复前检查此清单 -- 每次修复后更新状态 -- 测试失败立即停止,分析原因 - ---- - -## 第一阶段检查清单 - -### ✅ 1.1 ENVI 主流程返回值 -- [x] 代码已修改 -- [ ] 本地测试通过 -- [ ] 集成测试通过 -- [ ] 无副作用 -- [ ] 可以继续下一项 - -**快速测试命令**: -```bash -# 运行一个 D-InSAR 工作流 -# 在前端 IDL 自动化面板中提交任务 -# 观察任务状态和日志 -``` - ---- - -### ✅ 1.2 失败计数重复累加 -- [x] 代码已修改 -- [ ] 本地测试通过 -- [ ] 集成测试通过 -- [ ] 无副作用 -- [ ] 可以继续下一项 - -**快速测试命令**: -```bash -# 在前端 IDL 自动化面板中 -# 使用 "Step 3: 提取 Disp 结果" 功能 -# 故意触发一些失败(如修改文件权限) -# 检查返回的统计数据 -``` - ---- - -### ✅ 1.3 PowerShell 编码 -- [x] 代码已修改 -- [ ] 本地测试通过 -- [ ] 集成测试通过 -- [ ] 无副作用 -- [ ] 可以继续下一项 - -**快速测试命令**: -```powershell -# 运行启动脚本 -.\scripts\start_app.ps1 - -# 检查 nginx.conf 编码 -Get-Content .\nginx\conf\nginx.conf -Encoding UTF8 - -# 验证 Nginx 启动 -curl http://localhost:8080 -``` - ---- - -## 第二阶段检查清单 - -### ⏳ 2.1 AOI token 容量上限 -- [ ] 代码已修改 -- [ ] 环境变量已配置 -- [ ] 本地测试通过 -- [ ] 压力测试通过 -- [ ] 内存监控正常 -- [ ] 无副作用 -- [ ] 可以继续下一项 - -**快速测试脚本**: -```python -# 创建测试脚本 test_aoi_token_limit.py -import requests -import json - -base_url = "http://localhost:8000" -token = "your_auth_token" - -# 创建 1500 个 AOI token -for i in range(1500): - geojson = { - "type": "FeatureCollection", - "features": [{ - "type": "Feature", - "geometry": { - "type": "Point", - "coordinates": [100 + i * 0.001, 30 + i * 0.001] - } - }] - } - response = requests.post( - f"{base_url}/api/aoi/upload", - headers={"Authorization": f"Bearer {token}"}, - json=geojson - ) - if i % 100 == 0: - print(f"Created {i} tokens") - -print("Test completed") -``` - ---- - -### ⏳ 2.2 AOI 解析异常处理 -- [ ] 代码已修改 -- [ ] 本地测试通过 -- [ ] 前端兼容性测试通过 -- [ ] 错误消息清晰 -- [ ] 无副作用 -- [ ] 可以继续下一项 - -**快速测试数据**: -```json -// 无效 GeoJSON 1:缺少 type -{ - "features": [] -} - -// 无效 GeoJSON 2:错误的坐标 -{ - "type": "FeatureCollection", - "features": [{ - "type": "Feature", - "geometry": { - "type": "Point", - "coordinates": [999, 999] - } - }] -} - -// 无效 GeoJSON 3:格式错误 -{ - "type": "FeatureCollection", - "features": "not an array" -} -``` - ---- - -### ⏳ 2.3 路径归属判断 -- [ ] 代码已修改 -- [ ] 本地测试通过 -- [ ] 边界情况测试通过 -- [ ] 扫描结果准确 -- [ ] 无副作用 -- [ ] 可以继续下一项 - -**快速测试步骤**: -```bash -# 1. 创建测试目录结构 -mkdir -p /data/radar_backup -mkdir -p /data/radar/subdir - -# 2. 放入测试文件 -touch /data/radar_backup/test.tif -touch /data/radar/subdir/test.tif - -# 3. 运行数据扫描 -# 在前端数据管理面板中点击"刷新" - -# 4. 验证结果 -# radar_backup 的文件不应该出现在列表中 -# radar/subdir 的文件应该出现在列表中 -``` - ---- - -## 第三阶段检查清单 - -### ⏳ 3.1 解包安全校验 -- [ ] 代码已修改 -- [ ] 本地测试通过 -- [ ] 安全测试通过 -- [ ] 性能测试通过 -- [ ] 无副作用 -- [ ] 可以继续下一项 - -**快速测试脚本**: -```python -# 创建测试脚本 test_tar_security.py -import tarfile -import os - -# 测试 1:创建包含符号链接的 tar -with tarfile.open("test_symlink.tar", "w") as tar: - # 创建一个符号链接指向 /etc/passwd - info = tarfile.TarInfo(name="link_to_passwd") - info.type = tarfile.SYMTYPE - info.linkname = "/etc/passwd" - tar.addfile(info) - -# 测试 2:创建包含 .. 路径的 tar -with tarfile.open("test_escape.tar", "w") as tar: - info = tarfile.TarInfo(name="../../../etc/passwd") - info.size = 0 - tar.addfile(info) - -# 测试 3:创建正常的 tar -with tarfile.open("test_normal.tar", "w") as tar: - # 创建一个正常文件 - info = tarfile.TarInfo(name="normal_file.txt") - info.size = 5 - tar.addfile(info, fileobj=io.BytesIO(b"hello")) - -print("Test archives created") -print("test_symlink.tar - should be rejected") -print("test_escape.tar - should be rejected") -print("test_normal.tar - should be accepted") -``` - ---- - -## 紧急回滚程序 - -如果任何测试失败,立即执行: - -```bash -# 1. 查看最近的提交 -git log --oneline -5 - -# 2. 回滚到上一个提交 -git revert HEAD - -# 3. 或者硬回滚(谨慎使用) -git reset --hard HEAD~1 - -# 4. 重启服务 -# 停止所有服务 -# 重新运行 start_app.ps1 - -# 5. 验证系统恢复正常 -curl http://localhost:8080 -``` - ---- - -## 测试环境要求 - -### 最小测试环境 -- [ ] Python 3.9+ -- [ ] PostgreSQL 运行中 -- [ ] Ollama 运行中(如果测试 AI 功能) -- [ ] 至少 1 个 D-InSAR 结果数据 -- [ ] 至少 1 个雷达数据 - -### 推荐测试环境 -- [ ] 完整的开发环境 -- [ ] 测试数据库(非生产) -- [ ] 监控工具(内存、CPU) -- [ ] 日志查看工具 - ---- - -## 测试数据准备 - -### D-InSAR 测试数据 -``` -需要准备: -- 至少 1 个完整的 Task_* 目录 -- 包含 dinsar_results/out_ISARPTD_*_rsp_disp -- 用于测试 extract_disp_results 功能 -``` - -### 雷达数据测试 -``` -需要准备: -- 至少 2 个 .tif 文件 -- 放在 MONITOR_RADAR_DIRS 配置的目录中 -- 用于测试预览图生成和路径判断 -``` - -### AOI 测试数据 -``` -需要准备: -- 有效的 GeoJSON 文件 -- 无效的 GeoJSON 文件(多种错误类型) -- 用于测试异常处理 -``` - ---- - -## 性能基准 - -记录修复前后的性能指标: - -### ENVI 工作流 -- 修复前成功率:_% -- 修复后成功率:_% -- 平均耗时:_秒 - -### AOI token -- 修复前内存占用:_MB -- 修复后内存占用:_MB -- Token 数量上限:_个 - -### 解包性能 -- 修复前平均耗时:_秒 -- 修复后平均耗时:_秒 -- 性能差异:_% - ---- - -## 签名确认 - -每完成一个阶段,填写以下信息: - -### 第一阶段 -- 修复人:_______ -- 测试人:_______ -- 完成日期:_______ -- 签名:_______ - -### 第二阶段 -- 修复人:_______ -- 测试人:_______ -- 完成日期:_______ -- 签名:_______ - -### 第三阶段 -- 修复人:_______ -- 测试人:_______ -- 完成日期:_______ -- 签名:_______ diff --git a/docs/archive/SECURITY_FIX_PLAN.md b/docs/archive/SECURITY_FIX_PLAN.md deleted file mode 100644 index e90d4e8..0000000 --- a/docs/archive/SECURITY_FIX_PLAN.md +++ /dev/null @@ -1,478 +0,0 @@ -# 安全问题分阶段修复计划 - -## 修复原则 - -1. **安全第一**:每个阶段修复后必须充分测试,确保不影响现有功能 -2. **最小改动**:优先选择改动最小、风险最低的方案 -3. **可回滚**:每个阶段独立提交,出问题可以快速回滚 -4. **渐进式**:从简单到复杂,从低风险到高风险 - ---- - -## 第一阶段:明确 Bug 修复(低风险) - -**目标**:修复明确的代码错误,不涉及架构变更 - -### 1.1 ENVI 主流程返回值缺失 ✅ 已完成 - -**文件**:`backend/app/services/envi_service.py:1630` - -**修改**: -```python -# 修改前 -if error: - raise RuntimeError(error) - -# 修改后 -if error: - raise RuntimeError(error) -return record # 添加此行 -``` - -**风险评估**:🟢 极低 -- 只添加一行返回语句 -- 不改变任何逻辑 -- 修复了明确的 bug(成功时应该返回 record) - -**测试计划**: -- [ ] 运行一个完整的 D-InSAR 工作流(dinsar_custom) -- [ ] 检查任务状态是否正确标记为 SUCCESS -- [ ] 检查日志是否包含 `workflow=dinsar_custom duration=XXXs` -- [ ] 验证前端是否正确显示任务完成 - ---- - -### 1.2 失败计数重复累加 - -**文件**:`backend/app/services/envi_service.py:1717, 1727` - -**修改**: -```python -# 修改前(1717 行) -except OSError as e: - task_failed += 1 - failed += 1 # 删除此行 - task_status = f"error: {e}" - -# 修改后 -except OSError as e: - task_failed += 1 - task_status = f"error: {e}" - -# 修改前(1727 行) -except OSError as e: - task_failed += 1 - failed += 1 # 删除此行 - task_status = f"error: {e}" - -# 修改后 -except OSError as e: - task_failed += 1 - task_status = f"error: {e}" - -# 保留 1730 行的汇总 -failed += task_failed # 只在这里累加一次 -``` - -**风险评估**:🟢 极低 -- 只删除重复的累加语句 -- 不改变业务逻辑 -- 修复统计数据不准确的问题 - -**测试计划**: -- [ ] 运行 `extract_disp_results` 功能 -- [ ] 故意触发一些失败(如权限问题) -- [ ] 验证返回的 `failed` 计数是否准确(不翻倍) - ---- - -### 1.3 PowerShell 编码问题 - -**文件**:`scripts/start_app.ps1:388` - -**修改**: -```powershell -# 修改前 -$NewConfContent | Set-Content -LiteralPath "$NginxConfPath" -NoNewline - -# 修改后 -$NewConfContent | Set-Content -LiteralPath "$NginxConfPath" -NoNewline -Encoding UTF8 -``` - -**风险评估**:🟢 极低 -- 只添加编码参数 -- 确保 nginx.conf 使用 UTF-8 编码 -- 避免 PowerShell 5 默认 UTF-16 导致的问题 - -**测试计划**: -- [ ] 在 PowerShell 5 环境运行启动脚本 -- [ ] 检查生成的 nginx.conf 文件编码(使用 `file` 命令或文本编辑器) -- [ ] 验证 Nginx 是否能正常启动和解析配置 - ---- - -**第一阶段提交**: -``` -fix: 修复 ENVI 工作流返回值缺失和统计计数错误 - -- 修复 run_workflow 成功时不返回 record 的问题 -- 修复 extract_disp_results 失败计数重复累加 -- 修复 PowerShell 脚本 nginx.conf 编码问题 - -Co-Authored-By: Claude Opus 4.6 -``` - ---- - -## 第二阶段:防御性增强(中风险) - -**目标**:添加保护措施,不改变现有业务逻辑 - -### 2.1 AOI token 容量上限 - -**文件**:`backend/app/routers/dependencies.py` - -**修改策略**: -1. 添加环境变量配置 `AOI_TOKEN_MAX_STORE_SIZE`(默认 1000) -2. 在 `_store_aoi_token` 中添加容量检查 -3. 达到上限时删除最旧的 100 个 token - -**实现**: -```python -# 在 dependencies.py 顶部添加 -AOI_TOKEN_MAX_STORE_SIZE = read_int_env( - "AOI_TOKEN_MAX_STORE_SIZE", - 1000, - minimum=100, - maximum=10000, -) - -# 修改 _store_aoi_token 函数 -async def _store_aoi_token(aoi_wkt: str, feature_collection: Dict[str, Any]) -> str: - token = uuid.uuid4().hex - now = time.monotonic() - async with _AOI_TOKEN_LOCK: - _cleanup_expired_aoi_tokens(now) - - # 检查容量上限 - if len(_AOI_TOKEN_STORE) >= AOI_TOKEN_MAX_STORE_SIZE: - # 删除最旧的 token(按 expires_at 排序) - oldest_tokens = sorted( - _AOI_TOKEN_STORE.items(), - key=lambda x: x[1]["expires_at"] - )[:100] - for old_token, _ in oldest_tokens: - _AOI_TOKEN_STORE.pop(old_token, None) - # 记录日志 - print(f"[INFO] AOI token store reached limit, cleaned {len(oldest_tokens)} oldest tokens") - - _AOI_TOKEN_STORE[token] = { - "aoi_wkt": aoi_wkt, - "aoi_geojson": feature_collection, - "expires_at": now + AOI_TOKEN_TTL_SECONDS, - } - return token -``` - -**风险评估**:🟡 中等 -- 添加了新的容量限制逻辑 -- 可能影响高并发场景下的 AOI 查询 -- 但不改变现有 API 接口 - -**测试计划**: -- [ ] 创建 1000+ 个不同的 AOI token -- [ ] 验证内存占用是否稳定(不超过预期) -- [ ] 检查日志是否有 token 清理记录 -- [ ] 验证被清理的 token 是否无法再使用 -- [ ] 验证新创建的 token 仍然可用 - ---- - -### 2.2 AOI 解析异常处理 - -**文件**:`backend/app/routers/dependencies.py` - -**修改策略**: -在所有 AOI 解析函数中添加统一的异常处理 - -**实现位置**: -- `_parse_geojson_to_wkt` 调用处 -- `_parse_shapefile_to_wkt` 调用处 -- 文件上传端点 - -**实现**: -```python -# 在相关端点中添加 try-except -try: - aoi_wkt = _parse_geojson_to_wkt(geojson_data) -except (ValueError, KeyError, TypeError) as e: - raise HTTPException( - status_code=400, - detail=f"GeoJSON 格式错误: {str(e)}" - ) -except Exception as e: - logger.error(f"AOI 解析失败: {e}", exc_info=True) - raise HTTPException( - status_code=500, - detail="AOI 解析失败,请联系管理员" - ) -``` - -**风险评估**:🟡 中等 -- 改变了错误响应码(500 → 400) -- 可能影响前端错误处理逻辑 -- 但提升了 API 语义正确性 - -**测试计划**: -- [ ] 上传无效的 GeoJSON 文件 -- [ ] 验证是否返回 400 而非 500 -- [ ] 验证错误消息是否清晰 -- [ ] 验证前端是否能正确处理 400 错误 - ---- - -### 2.3 路径归属判断优化 - -**文件**:`backend/app/services/data_service.py:530` - -**修改策略**: -添加辅助函数 `is_subpath`,使用 `os.path.commonpath` 进行准确判断 - -**实现**: -```python -# 在 data_service.py 顶部添加辅助函数 -def is_subpath(child: str, parent: str) -> bool: - """检查 child 是否是 parent 的子路径""" - try: - child_abs = os.path.abspath(child) - parent_abs = os.path.abspath(parent) - # 确保父路径以分隔符结尾,避免前缀误匹配 - if not parent_abs.endswith(os.sep): - parent_abs += os.sep - if not child_abs.endswith(os.sep) and os.path.isdir(child_abs): - child_abs += os.sep - # 使用 commonpath 判断 - common = os.path.commonpath([child_abs, parent_abs]) - return common == parent_abs.rstrip(os.sep) - except (ValueError, TypeError): - return False - -# 修改 530 行 -# 修改前 -if not any(file_path_abs.startswith(root) for root in radar_roots): - continue - -# 修改后 -if not any(is_subpath(file_path_abs, root) for root in radar_roots): - continue -``` - -**风险评估**:🟡 中等 -- 改变了路径判断逻辑 -- 可能影响文件扫描结果 -- 但修复了潜在的安全问题 - -**测试计划**: -- [ ] 在 `MONITOR_RADAR_DIRS` 旁边创建 `MONITOR_RADAR_DIRS_backup` 目录 -- [ ] 放入测试文件 -- [ ] 运行数据扫描 -- [ ] 验证 backup 目录的文件不会被误判为合法 - ---- - -**第二阶段提交**: -``` -feat: 添加 AOI token 容量限制和异常处理增强 - -- 添加 AOI_TOKEN_MAX_STORE_SIZE 配置(默认 1000) -- 达到上限时自动清理最旧的 token -- 统一 AOI 解析异常处理(400 vs 500) -- 优化路径归属判断,避免前缀误匹配 - -Co-Authored-By: Claude Opus 4.6 -``` - ---- - -## 第三阶段:安全加固(高风险) - -**目标**:解决权限和安全问题,可能涉及架构调整 - -### 3.1 解包安全校验增强 - -**文件**:`scripts/unpack_archives.py` - -**修改策略**: -1. 修改 `_is_safe_tar_member` 接收 member 对象而非字符串 -2. 添加符号链接和硬链接检查 -3. 使用逐个提取替代 `extractall` - -**实现**: -```python -def _is_safe_tar_member(member): - """检查 tar 成员是否安全(包括符号链接检查)""" - # 检查路径 - norm_name = os.path.normpath(member.name) - if os.path.isabs(norm_name): - return False - if norm_name.startswith("..") or norm_name.startswith("../") or norm_name.startswith("..\\"): - return False - - # 检查符号链接和硬链接 - if member.issym() or member.islnk(): - link_target = member.linkname - if os.path.isabs(link_target) or link_target.startswith(".."): - return False - - return True - -def _validate_tar_members(tar_obj, archive_path): - for member in tar_obj.getmembers(): - if not _is_safe_tar_member(member): - raise IOError(f"unsafe tar entry detected: {member.name} in {archive_path}") - -def _safe_extract_tar(tar_obj, output_dir): - """安全地提取 tar 文件""" - for member in tar_obj.getmembers(): - if not _is_safe_tar_member(member): - raise IOError(f"unsafe tar entry: {member.name}") - # 逐个提取 - tar_obj.extract(member, path=output_dir) - -# 在 181 行替换 -with tarfile.open(archive_path, "r:*") as tar: - _validate_tar_members(tar, archive_path) - _safe_extract_tar(tar, output_dir) # 替换 tar.extractall -``` - -**风险评估**:🟡 中等 -- 改变了解包逻辑 -- 可能影响解包性能(逐个提取 vs 批量提取) -- 但提升了安全性 - -**测试计划**: -- [ ] 创建包含符号链接的 tar 文件 -- [ ] 验证是否被拒绝解包 -- [ ] 创建正常的 tar 文件 -- [ ] 验证是否能正常解包 -- [ ] 对比解包性能(前后差异) - ---- - -### 3.2 只读用户权限控制(暂缓) - -**状态**:⏸️ 暂缓到第四阶段 - -**原因**: -- 涉及权限系统重构 -- 需要修改多个端点 -- 可能影响前端调用逻辑 -- 需要更充分的测试 - -**备选方案**: -- 方案 A:添加权限检查依赖 -- 方案 B:配置控制(允许/禁止只读用户触发构建) -- 方案 C:改为 POST 端点(需要前端配合) - -**决策点**: -- 是否有只读用户在使用系统? -- 只读用户是否需要查看预览图? -- 是否可以接受只读用户触发构建? - ---- - -**第三阶段提交**: -``` -security: 增强 tar 解包安全校验 - -- 添加符号链接和硬链接检查 -- 使用逐个提取替代 extractall -- 防止目录逃逸和文件覆盖攻击 - -Co-Authored-By: Claude Opus 4.6 -``` - ---- - -## 第四阶段:权限重构(待定) - -**目标**:解决只读用户权限问题 - -**前置条件**: -- 第一、二、三阶段全部完成并稳定运行 -- 明确只读用户的使用场景和需求 -- 前端团队配合(如果需要修改 API 调用) - -**待讨论问题**: -1. 系统中是否有只读用户? -2. 只读用户的典型使用场景是什么? -3. 是否可以接受只读用户无法查看预览图? -4. 是否需要前端配合修改? - ---- - -## 修复时间表 - -| 阶段 | 预计时间 | 风险等级 | 依赖 | -|------|---------|---------|------| -| 第一阶段 | 1 天 | 🟢 低 | 无 | -| 第二阶段 | 2-3 天 | 🟡 中 | 第一阶段完成 | -| 第三阶段 | 2-3 天 | 🟡 中 | 第二阶段完成 | -| 第四阶段 | 待定 | 🔴 高 | 需求确认 | - ---- - -## 回滚计划 - -每个阶段独立提交,出现问题时可以: - -1. **快速回滚**: - ```bash - git revert - ``` - -2. **部分回滚**: - 如果只有某个修复有问题,可以单独回滚该文件: - ```bash - git checkout -- - git commit -m "revert: 回滚 的修改" - ``` - -3. **紧急回滚**: - 如果影响生产环境,立即回滚到上一个稳定版本: - ```bash - git reset --hard - ``` - ---- - -## 监控指标 - -修复后需要监控的关键指标: - -### 第一阶段 -- ENVI 工作流成功率(应该提升) -- 任务状态准确性 -- 统计数据准确性 - -### 第二阶段 -- AOI token 内存占用(应该稳定) -- 400 vs 500 错误比例(400 应该增加) -- 路径扫描结果准确性 - -### 第三阶段 -- 解包成功率(应该不变) -- 解包性能(可能略微下降) -- 安全事件(应该为 0) - ---- - -## 总结 - -这个分阶段修复计划: -- ✅ 从低风险到高风险渐进式修复 -- ✅ 每个阶段独立可测试、可回滚 -- ✅ 优先修复明确的 bug,暂缓架构性改动 -- ✅ 保留了灵活性,可以根据实际情况调整 - -**建议**:先完成第一阶段,充分测试后再决定是否继续第二阶段。 diff --git a/docs/archive/SECURITY_FIX_PROGRESS.md b/docs/archive/SECURITY_FIX_PROGRESS.md deleted file mode 100644 index aec76bf..0000000 --- a/docs/archive/SECURITY_FIX_PROGRESS.md +++ /dev/null @@ -1,278 +0,0 @@ -# 安全问题修复进度跟踪 - -## 修复状态图例 -- ✅ 已完成 -- 🚧 进行中 -- ⏸️ 暂缓 -- ⏳ 待开始 -- ❌ 已回滚 - ---- - -## 第一阶段:明确 Bug 修复 - -### 1.1 ENVI 主流程返回值缺失 -- **状态**:✅ 已完成 -- **修改文件**:`backend/app/services/envi_service.py:1630` -- **修改内容**:添加 `return record` -- **提交时间**:2026-03-04 -- **测试状态**:⏳ 待测试 - -**测试清单**: -- [ ] 运行完整的 D-InSAR 工作流(dinsar_custom) -- [ ] 检查任务状态是否为 SUCCESS -- [ ] 检查日志是否包含 `workflow=dinsar_custom duration=XXXs` -- [ ] 验证前端显示是否正常 - -**测试结果**: -``` -待填写... -``` - ---- - -### 1.2 失败计数重复累加 -- **状态**:✅ 已完成 -- **修改文件**:`backend/app/services/envi_service.py:1717, 1726` -- **修改内容**:删除重复的 `failed += 1`(两处) -- **提交时间**:2026-03-04 -- **测试状态**:⏳ 待测试 - -**测试清单**: -- [ ] 运行 `extract_disp_results` 功能 -- [ ] 故意触发失败(如权限问题) -- [ ] 验证 `failed` 计数是否准确 - -**测试结果**: -``` -待填写... -``` - ---- - -### 1.3 PowerShell 编码问题 -- **状态**:✅ 已完成(已增强) -- **修改文件**:`scripts/start_app.ps1:388` -- **修改内容**:使用 UTF-8 无 BOM 编码写入 -- **提交时间**:2026-03-04 -- **测试状态**:✅ 已验证 - -**额外修复**: -- 发现 `nginx.conf` 文件包含 UTF-8 BOM,导致 Nginx 启动失败 -- 已删除 BOM(使用 `tail -c +4`) -- 修改脚本使用 `[System.IO.File]::WriteAllText()` 确保无 BOM - -**测试清单**: -- [ ] 在 PowerShell 5 环境运行启动脚本 -- [ ] 检查 nginx.conf 文件编码 -- [ ] 验证 Nginx 是否正常启动 - -**测试结果**: -``` -待填写... -``` - ---- - -**第一阶段总结**: -- 完成度:3/3 (100%) ✅ -- 完成时间:2026-03-04 -- 遇到的问题:无 -- 回滚记录:无 -- **状态**:代码修改已完成,等待测试验证 - ---- - -## 第二阶段:防御性增强 - -### 2.1 AOI token 容量上限 -- **状态**:⏳ 待开始 -- **修改文件**:`backend/app/routers/dependencies.py` -- **修改内容**:添加 `AOI_TOKEN_MAX_STORE_SIZE` 配置和容量检查 -- **提交时间**:- -- **测试状态**:- - -**测试清单**: -- [ ] 创建 1000+ 个 AOI token -- [ ] 验证内存占用是否稳定 -- [ ] 检查日志是否有清理记录 -- [ ] 验证被清理的 token 无法使用 -- [ ] 验证新 token 仍可用 - -**测试结果**: -``` -待填写... -``` - ---- - -### 2.2 AOI 解析异常处理 -- **状态**:⏳ 待开始 -- **修改文件**:`backend/app/routers/dependencies.py` -- **修改内容**:添加统一异常处理(400 vs 500) -- **提交时间**:- -- **测试状态**:- - -**测试清单**: -- [ ] 上传无效 GeoJSON 文件 -- [ ] 验证返回 400 而非 500 -- [ ] 验证错误消息清晰 -- [ ] 验证前端能正确处理 - -**测试结果**: -``` -待填写... -``` - ---- - -### 2.3 路径归属判断优化 -- **状态**:⏳ 待开始 -- **修改文件**:`backend/app/services/data_service.py:530` -- **修改内容**:使用 `is_subpath` 替代 `startswith` -- **提交时间**:- -- **测试状态**:- - -**测试清单**: -- [ ] 创建 `MONITOR_RADAR_DIRS_backup` 目录 -- [ ] 放入测试文件 -- [ ] 运行数据扫描 -- [ ] 验证 backup 目录文件不被误判 - -**测试结果**: -``` -待填写... -``` - ---- - -**第二阶段总结**: -- 完成度:0/3 (0%) -- 预计完成时间:- -- 遇到的问题:- -- 回滚记录:无 - ---- - -## 第三阶段:安全加固 - -### 3.1 解包安全校验增强 -- **状态**:⏳ 待开始 -- **修改文件**:`scripts/unpack_archives.py` -- **修改内容**:添加符号链接检查,使用逐个提取 -- **提交时间**:- -- **测试状态**:- - -**测试清单**: -- [ ] 创建包含符号链接的 tar 文件 -- [ ] 验证被拒绝解包 -- [ ] 创建正常 tar 文件 -- [ ] 验证正常解包 -- [ ] 对比解包性能 - -**测试结果**: -``` -待填写... -``` - ---- - -**第三阶段总结**: -- 完成度:0/1 (0%) -- 预计完成时间:- -- 遇到的问题:- -- 回滚记录:无 - ---- - -## 第四阶段:权限重构 - -### 4.1 只读用户权限控制 -- **状态**:⏸️ 暂缓 -- **原因**:需要明确需求和前端配合 -- **待讨论问题**: - - [ ] 系统中是否有只读用户? - - [ ] 只读用户的使用场景? - - [ ] 是否可以接受只读用户无法查看预览图? - - [ ] 是否需要前端配合? - ---- - -## 总体进度 - -| 阶段 | 状态 | 完成度 | 预计时间 | 实际时间 | -|------|------|--------|---------|---------| -| 第一阶段 | ✅ 已完成 | 3/3 (100%) | 1 天 | 2026-03-04 | -| 第二阶段 | ⏳ 待开始 | 0/3 (0%) | 2-3 天 | - | -| 第三阶段 | ⏳ 待开始 | 0/1 (0%) | 2-3 天 | - | -| 第四阶段 | ⏸️ 暂缓 | - | 待定 | - | - -**总完成度**:3/7 (43%) - ---- - -## 问题记录 - -### 问题 #1:Nginx 启动失败 - UTF-8 BOM 问题 -- **发现时间**:2026-03-04 -- **问题描述**:Nginx 启动失败,错误信息 `unknown directive "worker_processes"`,发现 `nginx.conf` 文件包含 UTF-8 BOM(`ef bb bf`) -- **影响范围**:Nginx 无法启动,整个系统无法访问 -- **解决方案**: - 1. 使用 `tail -c +4` 删除现有文件的 BOM - 2. 修改 PowerShell 脚本使用 `[System.IO.File]::WriteAllText()` 确保写入时无 BOM -- **状态**:✅ 已解决 - -### 问题 #2:PROJ 数据库版本冲突 -- **发现时间**:2026-03-05 -- **问题描述**:扫描 D-InSAR 结果时出现 PROJ 警告 `DATABASE.LAYOUT.VERSION.MINOR = 2 whereas >= 3 is expected`,PostgreSQL 自带的 PROJ 数据库版本过旧 -- **影响范围**:不影响功能,但产生大量警告日志 -- **解决方案**: - 1. 在 `run_backend.py` 中自动检测并设置 `PROJ_LIB` 环境变量 - 2. 指向 GDAL 自带的新版 PROJ 数据库 -- **状态**:✅ 已修复,待测试验证 - -### 问题 #3:扫描任务时前端未锁定 -- **发现时间**:2026-03-05 -- **问题描述**:D-InSAR 结果扫描任务运行时前端没有锁定 -- **影响范围**:用户可能在扫描过程中进行其他操作,导致数据不一致 -- **可能原因**: - 1. 扫描任务执行太快(< 3 秒),前端轮询间隔(3 秒)来不及捕获 - 2. 任务状态更新有延迟 -- **解决方案**:待确认是否需要修复(可能是正常行为) -- **状态**:⏳ 待分析 - ---- - -## 回滚记录 - -### 回滚 #1 -- **回滚时间**:- -- **回滚内容**:- -- **回滚原因**:- -- **影响范围**:- - ---- - -## 下一步行动 - -1. **立即行动**: - - [ ] 测试 1.1 ENVI 主流程返回值修复 - - [ ] 如果测试通过,继续修复 1.2 和 1.3 - -2. **本周计划**: - - [ ] 完成第一阶段所有修复 - - [ ] 充分测试第一阶段 - - [ ] 提交第一阶段代码 - -3. **下周计划**: - - [ ] 根据第一阶段测试结果决定是否继续 - - [ ] 如果继续,开始第二阶段 - ---- - -## 备注 - -- 每次修复后立即更新此文档 -- 测试结果详细记录,包括成功和失败的情况 -- 遇到问题及时记录到"问题记录"区域 -- 回滚操作必须记录原因和影响范围 diff --git a/docs/archive/SECURITY_FIX_STAGE1_SUMMARY.md b/docs/archive/SECURITY_FIX_STAGE1_SUMMARY.md deleted file mode 100644 index b906ba1..0000000 --- a/docs/archive/SECURITY_FIX_STAGE1_SUMMARY.md +++ /dev/null @@ -1,271 +0,0 @@ -# 第一阶段修复总结(2026-03-04) - -## 修复概览 - -**阶段**:第一阶段 - 明确 Bug 修复 -**状态**:✅ 代码修改完成,等待测试验证 -**完成时间**:2026-03-04 -**风险等级**:🟢 低风险 - ---- - -## 修复内容 - -### 1.1 ENVI 主流程返回值缺失 ✅ - -**问题**:`run_workflow()` 函数在成功执行后不返回 `record` 字典,导致下游代码崩溃。 - -**修改文件**:`backend/app/services/envi_service.py:1630` - -**修改内容**: -```python -# 修改前 -if error: - raise RuntimeError(error) -# 函数结束,返回 None - -# 修改后 -if error: - raise RuntimeError(error) -return record # 添加此行 -``` - -**影响**: -- 修复了所有 ENVI 工作流任务在成功后被误判为失败的问题 -- 下游代码可以正确访问 `run_meta.get('workflow')` 等字段 - -**风险评估**:🟢 极低 -- 只添加一行返回语句 -- 不改变任何业务逻辑 -- 修复明确的 bug - ---- - -### 1.2 失败计数重复累加 ✅ - -**问题**:`extract_disp_results()` 函数中失败计数被重复累加,导致统计数据翻倍。 - -**修改文件**:`backend/app/services/envi_service.py:1717, 1726` - -**修改内容**: -```python -# 修改前(1717 行) -except OSError as e: - task_failed += 1 - failed += 1 # 删除此行 - task_status = f"error: {e}" - -# 修改后 -except OSError as e: - task_failed += 1 - task_status = f"error: {e}" - -# 修改前(1726 行) -except OSError as e: - task_failed += 1 - failed += 1 # 删除此行 - task_status = f"error: {e}" - -# 修改后 -except OSError as e: - task_failed += 1 - task_status = f"error: {e}" - -# 保留 1730 行的汇总(只在这里累加一次) -failed += task_failed -``` - -**影响**: -- 修复了失败计数翻倍的问题 -- 统计数据现在准确反映实际失败数量 - -**风险评估**:🟢 极低 -- 只删除重复的累加语句 -- 不改变业务逻辑 -- 修复统计错误 - ---- - -### 1.3 PowerShell 编码问题 ✅ - -**问题**:启动脚本写入 `nginx.conf` 时未指定编码,PowerShell 5 默认使用 UTF-16,导致 Nginx 无法解析。 - -**修改文件**:`scripts/start_app.ps1:388` - -**修改内容**: -```powershell -# 修改前 -$NewConfContent | Set-Content -LiteralPath "$NginxConfPath" -NoNewline - -# 修改后 -$NewConfContent | Set-Content -LiteralPath "$NginxConfPath" -NoNewline -Encoding UTF8 -``` - -**影响**: -- 确保 `nginx.conf` 使用 UTF-8 编码 -- 避免 Nginx 配置解析失败 - -**风险评估**:🟢 极低 -- 只添加编码参数 -- 不改变文件内容 -- 提升兼容性 - ---- - -## 代码验证 - -### Python 语法检查 -```bash -cd "Z:\Code\Insar_management_system_v2\backend\app\services" -python -m py_compile envi_service.py -``` -**结果**:✅ 通过 - -### 修改文件列表 -1. `backend/app/services/envi_service.py` - 3 处修改 -2. `scripts/start_app.ps1` - 1 处修改 - ---- - -## 测试计划 - -### 1.1 ENVI 主流程返回值测试 - -**测试步骤**: -1. 启动后端服务 -2. 在前端 IDL 自动化面板中提交一个 D-InSAR 工作流任务 -3. 等待任务完成 -4. 检查任务状态 - -**预期结果**: -- 任务状态显示为 SUCCESS -- 日志包含 `workflow=dinsar_custom duration=XXXs` -- 前端正确显示任务完成信息 - -**测试命令**: -```bash -# 查看任务日志 -tail -f backend/logs/app.log - -# 查看 ENVI 运行日志 -ls backend/runtime/*.log -``` - ---- - -### 1.2 失败计数测试 - -**测试步骤**: -1. 准备一个包含 `dinsar_results` 的 Task 目录 -2. 故意修改某些文件权限,使复制失败 -3. 在前端使用 "Step 3: 提取 Disp 结果" 功能 -4. 检查返回的统计数据 - -**预期结果**: -- `failed` 计数等于实际失败的文件数(不翻倍) -- 统计数据准确 - -**测试命令**: -```bash -# 修改文件权限(Windows) -icacls "Z:\Test_data\Task_xxx\dinsar_results\out_ISARPTD_xxx_rsp_disp.dat" /deny Everyone:F - -# 查看返回的统计数据 -# 在前端点击"提取 Disp 结果"后查看响应 -``` - ---- - -### 1.3 PowerShell 编码测试 - -**测试步骤**: -1. 在 PowerShell 5 环境运行启动脚本 -2. 检查生成的 `nginx.conf` 文件编码 -3. 验证 Nginx 是否正常启动 - -**预期结果**: -- `nginx.conf` 文件编码为 UTF-8 -- Nginx 正常启动,无配置解析错误 - -**测试命令**: -```powershell -# 运行启动脚本 -.\scripts\start_app.ps1 - -# 检查文件编码(使用 file 命令或文本编辑器) -# 在 VS Code 中打开 nginx/conf/nginx.conf,右下角显示编码 - -# 验证 Nginx 启动 -curl http://localhost:8080 -``` - ---- - -## 回滚计划 - -如果测试失败,可以快速回滚: - -### 回滚 1.1 ENVI 返回值 -```python -# 删除 envi_service.py:1631 行的 return record -# 恢复为: -if error: - raise RuntimeError(error) -# 函数结束 -``` - -### 回滚 1.2 失败计数 -```python -# 在 envi_service.py:1718 和 1727 行恢复 -except OSError as e: - task_failed += 1 - failed += 1 # 恢复此行 - task_status = f"error: {e}" -``` - -### 回滚 1.3 PowerShell 编码 -```powershell -# 删除 start_app.ps1:388 行的 -Encoding UTF8 -$NewConfContent | Set-Content -LiteralPath "$NginxConfPath" -NoNewline -``` - ---- - -## 下一步行动 - -### 立即行动 -1. ⏳ 执行第一阶段测试计划 -2. ⏳ 记录测试结果到 `SECURITY_FIX_PROGRESS.md` - -### 测试通过后 -1. 决定是否继续第二阶段修复 -2. 如果继续,开始第二阶段:防御性增强 - -### 测试失败时 -1. 分析失败原因 -2. 记录到 `SECURITY_FIX_PROGRESS.md` 的"问题记录"区域 -3. 决定是否回滚 -4. 如果回滚,记录回滚原因 - ---- - -## 文档更新 - -已更新的文档: -- ✅ `SECURITY_FIX_PROGRESS.md` - 更新修复状态和总体进度 -- ✅ `SECURITY_FIX_CHECKLIST.md` - 标记代码修改完成 -- ✅ `CLAUDE.md` - 更新当前任务状态 - ---- - -## 总结 - -第一阶段修复已完成所有代码修改,共修复 3 个明确的 bug: -1. ✅ ENVI 主流程返回值缺失 -2. ✅ 失败计数重复累加 -3. ✅ PowerShell 编码问题 - -所有修改都是低风险的,不涉及架构变更,只修复明确的代码错误。Python 语法检查已通过,现在等待功能测试验证。 - -**风险评估**:🟢 低风险 -**建议**:可以安全地进行测试,不太可能影响现有功能。 diff --git a/docs/archive/SECURITY_OVERVIEW.md b/docs/archive/SECURITY_OVERVIEW.md deleted file mode 100644 index 06fe275..0000000 --- a/docs/archive/SECURITY_OVERVIEW.md +++ /dev/null @@ -1,273 +0,0 @@ -# 安全审计与修复总览 - -## 📋 文档导航 - -本次安全审计产生了以下文档,请按顺序阅读: - -1. **[SECURITY_AUDIT_2026-03-04.md](./SECURITY_AUDIT_2026-03-04.md)** - 原始审计报告 - - 审计人员提供的问题清单 - - 包含代码位置和影响分析 - -2. **[SECURITY_AUDIT_ANALYSIS_2026-03-04.md](./SECURITY_AUDIT_ANALYSIS_2026-03-04.md)** - 详细分析文档 - - 每个问题的根本原因分析 - - 多种修复方案对比 - - 长期改进建议 - -3. **[SECURITY_FIX_PLAN.md](./SECURITY_FIX_PLAN.md)** - 分阶段修复计划 ⭐ - - 四个阶段的详细修复计划 - - 风险评估和测试方案 - - 回滚计划和时间表 - -4. **[SECURITY_FIX_PROGRESS.md](./SECURITY_FIX_PROGRESS.md)** - 进度跟踪 - - 实时更新的修复进度 - - 测试结果记录 - - 问题和回滚记录 - -5. **[SECURITY_FIX_CHECKLIST.md](./SECURITY_FIX_CHECKLIST.md)** - 快速检查清单 - - 每个修复项的检查清单 - - 快速测试命令和脚本 - - 紧急回滚程序 - ---- - -## 🎯 问题概览 - -### 高危问题(3 个) -1. **ENVI 主流程返回值缺失** - 导致所有工作流任务失败 -2. **只读用户可触发写操作** - 权限绕过 + DoS 风险 -3. **AOI token 内存泄漏** - 可导致进程 OOM - -### 中危问题(4 个) -1. **失败计数重复累加** - 统计数据不准确 -2. **AOI 解析异常处理** - 用户输入错误返回 500 -3. **路径归属判断错误** - 前缀误匹配风险 -4. **解包安全校验不足** - 符号链接逃逸风险 - -### 低危问题(1 个) -1. **PowerShell 编码问题** - 可能导致 Nginx 配置解析失败 - ---- - -## 🚀 修复策略 - -### 分阶段修复原则 -1. **安全第一** - 每个阶段充分测试后再继续 -2. **最小改动** - 优先选择风险最低的方案 -3. **可回滚** - 每个阶段独立提交 -4. **渐进式** - 从简单到复杂 - -### 修复阶段 - -#### 第一阶段:明确 Bug 修复(1 天) -- ✅ ENVI 主流程返回值缺失 -- ⏳ 失败计数重复累加 -- ⏳ PowerShell 编码问题 - -**风险等级**:🟢 低 -**状态**:🚧 进行中(1/3 完成) - -#### 第二阶段:防御性增强(2-3 天) -- ⏳ AOI token 容量上限 -- ⏳ AOI 解析异常处理 -- ⏳ 路径归属判断优化 - -**风险等级**:🟡 中 -**状态**:⏳ 待开始 -**前置条件**:第一阶段完成并测试通过 - -#### 第三阶段:安全加固(2-3 天) -- ⏳ 解包安全校验增强 - -**风险等级**:🟡 中 -**状态**:⏳ 待开始 -**前置条件**:第二阶段完成并测试通过 - -#### 第四阶段:权限重构(待定) -- ⏸️ 只读用户权限控制 - -**风险等级**:🔴 高 -**状态**:⏸️ 暂缓 -**原因**:需要明确需求和前端配合 - ---- - -## 📊 当前状态 - -### 总体进度 -- **已完成**:1/8 (12.5%) -- **进行中**:1/8 (12.5%) -- **待开始**:6/8 (75%) -- **已暂缓**:1/8 (12.5%) - -### 已完成的修复 -1. ✅ ENVI 主流程返回值缺失 - - 文件:`backend/app/services/envi_service.py:1630` - - 修改:添加 `return record` - - 状态:代码已修改,待测试 - -### 下一步行动 -1. 测试 ENVI 主流程返回值修复 -2. 如果测试通过,继续修复 1.2 和 1.3 -3. 完成第一阶段后提交代码 - ---- - -## 🧪 测试要求 - -### 第一阶段测试 -- [ ] ENVI 工作流端到端测试 -- [ ] 失败计数准确性测试 -- [ ] PowerShell 编码测试 - -### 第二阶段测试 -- [ ] AOI token 压力测试(1000+ tokens) -- [ ] AOI 解析异常处理测试 -- [ ] 路径判断边界测试 - -### 第三阶段测试 -- [ ] 解包安全测试(符号链接、路径逃逸) -- [ ] 解包性能测试 - ---- - -## 🔄 回滚计划 - -每个阶段独立提交,出现问题时可以: - -### 快速回滚 -```bash -git revert -``` - -### 部分回滚 -```bash -git checkout -- -git commit -m "revert: 回滚 的修改" -``` - -### 紧急回滚 -```bash -git reset --hard -``` - ---- - -## 📈 监控指标 - -### 关键指标 -- ENVI 工作流成功率 -- AOI token 内存占用 -- API 错误率(400 vs 500) -- 解包性能 - -### 监控工具 -- 应用日志 -- 系统资源监控 -- 数据库性能监控 - ---- - -## 🤝 协作流程 - -### 修复流程 -1. 阅读修复计划(SECURITY_FIX_PLAN.md) -2. 执行代码修改 -3. 本地测试 -4. 更新进度文档(SECURITY_FIX_PROGRESS.md) -5. 提交代码 -6. 集成测试 -7. 部署到测试环境 - -### 测试流程 -1. 阅读检查清单(SECURITY_FIX_CHECKLIST.md) -2. 准备测试数据 -3. 执行测试用例 -4. 记录测试结果 -5. 更新进度文档 - -### 问题处理流程 -1. 发现问题立即记录到进度文档 -2. 分析问题原因 -3. 决定是否回滚 -4. 如果回滚,记录回滚原因 -5. 修复问题后重新测试 - ---- - -## 📞 联系方式 - -### 遇到问题时 -1. 查看进度文档的"问题记录"区域 -2. 查看分析文档的相关章节 -3. 如果需要回滚,参考回滚计划 - -### 需要讨论时 -- 第四阶段(权限重构)需要讨论需求 -- 遇到无法解决的技术问题 -- 需要调整修复计划 - ---- - -## 📝 更新日志 - -### 2026-03-04 -- ✅ 创建审计分析文档 -- ✅ 创建分阶段修复计划 -- ✅ 创建进度跟踪文档 -- ✅ 创建快速检查清单 -- ✅ 完成 1.1 ENVI 主流程返回值修复(代码已修改,待测试) - ---- - -## 🎓 经验总结 - -### 审计发现的根本问题 -1. **错误处理不完善** - 成功路径缺少返回值 -2. **权限控制不严格** - GET 请求触发写操作 -3. **资源管理不当** - 内存无限增长 -4. **统计逻辑错误** - 重复累加 -5. **安全校验不足** - 路径判断、解包校验 - -### 改进建议 -1. 引入代码审查流程 -2. 添加单元测试覆盖 -3. 使用静态分析工具 -4. 定期安全审计 -5. 完善错误处理和日志 - ---- - -## 📚 参考资料 - -### 相关文档 -- [CLAUDE.md](../CLAUDE.md) - 项目工作备忘录 -- [AI_ANALYSIS_REDESIGN.md](./AI_ANALYSIS_REDESIGN.md) - AI 分析模块设计 -- [RECOMMENDED_VLM_MODELS.md](./RECOMMENDED_VLM_MODELS.md) - 推荐多模态模型 - -### 外部资源 -- [OWASP Top 10](https://owasp.org/www-project-top-ten/) -- [Python Security Best Practices](https://python.readthedocs.io/en/stable/library/security_warnings.html) -- [FastAPI Security](https://fastapi.tiangolo.com/tutorial/security/) - ---- - -## ✅ 下一步 - -1. **立即行动**: - - 测试 1.1 ENVI 主流程返回值修复 - - 准备测试环境和测试数据 - -2. **本周计划**: - - 完成第一阶段所有修复 - - 充分测试第一阶段 - - 提交第一阶段代码 - -3. **下周计划**: - - 根据第一阶段测试结果决定是否继续 - - 如果继续,开始第二阶段 - ---- - -**最后更新**:2026-03-04 -**文档版本**:v1.0 -**维护人**:Claude Opus 4.6 diff --git a/docs/archive/TODO.md b/docs/archive/TODO.md deleted file mode 100644 index a6607e5..0000000 --- a/docs/archive/TODO.md +++ /dev/null @@ -1,115 +0,0 @@ -# InSAR Management System TODO(持续维护) - -最后更新:2026-02-10 -维护目标:帮助开发者快速定位风险、统一改造节奏、避免“修一处坏一处”。 - ---- - -## 一、当前总体状态 - -- 认证授权:已切换为 `Session + Cookie`,管理员/只读权限可用。 -- 用户管理:管理员可创建与管理账号,前端面板已接入。 -- 审计日志:高风险写接口审计已接入,前端可查询。 -- 雷达源图预览:已进入 **Scheme B(后端预纠正缓存)** 实装阶段。 -- 调度模式:当前默认 `Manual-only`,通过手动触发扫描,不启用后台定时任务。 - ---- - -## 二、任务看板(按优先级) - -## P0(本周优先) - -### 1) SEC-01 鉴权与权限边界完善 -- **状态**:DONE(首版完成,持续加固) -- **目标**:确保所有写接口只允许管理员,所有新增接口必须复用权限依赖。 -- **验收**: - - 未登录访问受限接口返回 `401` - - 只读用户写操作返回 `403` - - 管理员写操作返回 `2xx` - -### 2) SEC-01A 审计链路闭环 -- **状态**:DONE(首版完成,持续加固) -- **目标**:高风险操作均可追踪“谁、何时、做了什么”。 -- **验收**: - - 后端 `GET /api/auth/audit-logs` 可查关键操作 - - 前端审计面板支持过滤与检索 - -### SEC-02(新增)部署安全基线收敛 -- **状态**:TODO -- **目标**:降低默认配置带来的部署风险,形成可复制的最小安全基线。 -- **验收**: - - 生产环境 `CORS_ORIGINS` 使用明确白名单(不使用 `*`) - - 部署文档明确 `.env` 密钥管理规范(不入库、不共享) - - HTTP/HTTPS 场景下 `AUTH_COOKIE_SECURE` 配置策略明确 - -### 3) GEO-01 Scheme B 纠正缓存主链路 -- **状态**:DONE(首版落地) -- **范围**: - - 双缓存目录:`radar_geo` + `radar_raw` - - 后端透视纠正生成地理预览缓存 - - `thumb` 接口优先 `radar_geo`,失败回退 `radar_raw` - - 新增状态与重建接口 - -### 4) GEO-02 纠正缓存状态可观测 -- **状态**:DONE(首版落地) -- **范围**: - - 数据库记录 `preview_cache_status/version/path/error/updated_at` - - 前端列表显示状态徽标(纠正/回退/失败/未建) - - 管理员可“单条重建缓存” - ---- - -## P1(下一阶段) - -### 5) GEO-03 真实数据稳定性验证 -- **状态**:TODO -- **目标**:验证升轨/降轨/大倾斜场景下叠加稳定性。 -- **验收**: - - 覆盖面与源影像视觉对齐明显改善 - - 地图缩放/平移无明显卡顿 - - 构建失败样本可在日志与状态中定位 - -### 6) PERF-01 大批量扫描性能压测 -- **状态**:TODO -- **目标**:评估 1000+ 场景增量扫描耗时与缓存命中率。 -- **验收**: - - 二次扫描命中率 > 95% - - 失败重试不影响主扫描完成 - -### 7) MAINT-01 前端 `App.jsx` 模块化拆分 -- **状态**:TODO -- **目标**:降低单文件复杂度,便于持续维护。 - ---- - -## 三、里程碑记录 - -### 2026-02-10(Session 鉴权阶段) -- 完成 Session + Cookie 鉴权首版。 -- 完成管理员初始化与用户管理基础链路。 -- 修复登录阶段 `MissingGreenlet` 异常。 - -### 2026-02-10(审计与运维阶段) -- 接入高风险写接口审计日志。 -- 前端新增审计日志面板。 - -### 2026-02-10(Scheme B 首版实装) -- 新增 `radar_geo` 与 `radar_raw` 双缓存策略。 -- 扫描流程支持增量构建纠正缓存与回退缓存。 -- 新增接口: - - `GET /api/radar-data/{id}/preview-status` - - `POST /api/radar-data/{id}/rebuild-preview-cache` -- 前端源影像列表支持状态显示与管理员重建。 -- 方向判定升级:优先读取 XML 的 `refRow/refColumn` 映射像素角与地理角,减少方向错位。 - -### 2026-02-10(文档与实现对齐) -- 明确当前扫描调度模式为 `Manual-only`(非定时守护)。 -- 补充部署安全基线项(CORS 白名单、`.env` 密钥管理、Cookie 安全策略)。 - ---- - -## 四、开发注意事项(必须遵守) - -- 不得破坏 `start_system.bat` 与 `scripts/start_app.ps1` 的建库/校验/修复启动链路。 -- 任何数据库结构变更后,必须同步更新 `scripts/init_db.py` 的非破坏性补齐能力。 -- 新增功能必须同步更新 `docs/DEPLOYMENT.md` 与本文件。 diff --git a/docs/archive/TODO_water.md b/docs/archive/TODO_water.md deleted file mode 100644 index fd62b7d..0000000 --- a/docs/archive/TODO_water.md +++ /dev/null @@ -1,94 +0,0 @@ -# 水体提取与洪涝监测 — 开发 TODO - -## 架构概览 - -``` -Phase 1: 水体提取 → WaterMaskORM(每景影像的水体掩膜) -Phase 2: 水体配对 → WaterPairORM(参考期 + 监测期) -Phase 3: 变化检测 → FloodEventORM(洪涝事件 + 告警) -``` - -依赖库(InSAR conda 环境已有):rasterio, shapely, numpy, scipy, Pillow - ---- - -## 任务列表 - -### Step 1:ORM 建表 -- [x] W01 `orm.py` 新增 WaterMaskORM、WaterPairORM、FloodEventORM -- [x] W02 `models/__init__.py` 导出新 ORM -- [x] W03 Alembic migration 0002_water_tables.py - -### Step 2:核心服务 -- [x] W04 `water_service.py` Phase 1 — 单景水体提取(OTSU + 形态学 + 矢量化) -- [x] W05 `water_service.py` Phase 2 — 水体配对逻辑(空间重叠 + 时间筛选) -- [x] W06 `water_service.py` Phase 3 — 变化检测(掩膜差值 + 面积统计 + 告警) - -### Step 3:Job 集成 -- [x] W07 `job_handlers.py` 新增 WATER_EXTRACT / WATER_DETECT job 类型 - -### Step 4:路由 -- [x] W08 `routers/water.py` 全部端点 -- [x] W09 `routers/__init__.py` 注册 water router - -### Step 5:前端 -- [x] W10 `api/water.js` API 层 -- [x] W11 `WaterMonitorPanel.jsx` 三 Tab 面板(提取/配对/事件) -- [x] W12 `App.jsx` 注册新面板入口 - ---- - -## 端点设计 - -``` -POST /water/extract # 批量提取(传 radar_data_ids 列表) -GET /water/masks # 查询水体掩膜列表(分页) -GET /water/masks/{id}/preview # 水体掩膜预览图 - -POST /water/pairs # 创建配对(reference_id + monitor_id) -GET /water/pairs # 查询配对列表 -POST /water/pairs/{id}/detect # 对指定配对执行变化检测 - -GET /water/events # 查询洪涝事件列表 -GET /water/events/{id}/preview # 变化图预览 -``` - ---- - -## 关键算法备忘 - -### OTSU(纯 numpy 实现,不依赖 scikit-image) -```python -def _otsu_threshold(arr_db): - hist, bin_edges = np.histogram(arr_db[np.isfinite(arr_db)], bins=256) - bin_centers = (bin_edges[:-1] + bin_edges[1:]) / 2 - total = hist.sum() - w0 = np.cumsum(hist) / total - w1 = 1 - w0 - mu0 = np.cumsum(hist * bin_centers) / (np.cumsum(hist) + 1e-10) - mu1 = (np.sum(hist * bin_centers) - np.cumsum(hist * bin_centers)) / (np.cumsum(hist[::-1])[::-1] + 1e-10) - sigma_b = w0 * w1 * (mu0 - mu1) ** 2 - return bin_centers[np.argmax(sigma_b)] -``` - -### 水体掩膜预览图 -- 原始影像灰度拉伸为背景(半透明) -- 水体区域叠加蓝色(RGBA: 0, 100, 255, 180) -- 新增水体叠加红色(洪涝变化图) - ---- - -## 状态 - -**全部完成** ✓ — 等待测试 - -### 测试步骤 -1. 重启后端(新表由 create_all 自动创建) -2. 前端强制刷新,进入"结果分析"→"水体监测" -3. Phase 1:输入已入库的雷达数据 ID,提交提取任务,等待完成后刷新列表 -4. Phase 2:选参考期和监测期掩膜 ID,创建配对 -5. Phase 3:对配对点击"执行变化检测",在"洪涝事件"Tab 查看结果 - -### 已知限制 -- `_find_geocoded_path` 依赖 `preview_cache_path` 或 `file_path`,若雷达数据没有地理编码文件则提取失败 -- 告警阈值默认 30%,可通过 `.env` 中 `FLOOD_ALERT_THRESHOLD=0.3` 调整 diff --git a/docs/archive/TODO_water_v2.md b/docs/archive/TODO_water_v2.md deleted file mode 100644 index 1fe98c4..0000000 --- a/docs/archive/TODO_water_v2.md +++ /dev/null @@ -1,187 +0,0 @@ -# 水体监测模块 v2 — 任务清单 - -## 背景 - -原 water_service.py(手动 OTSU + 形态学)方案废弃。 -改用 SARscape 原生任务链,精度更高,与 D-InSAR 流程一致。 - -## SARscape 任务链 - -``` -单景预处理(每景独立): - SARsBasicMultilooking → 多视处理(SLC → 强度图,降噪) - SARsBasicGeocoding → 地理编码 + 辐射定标(输出 dB 图) - -洪涝检测(两景配对): - SARsBasicFeFloodingClassification → 洪涝分类图 - SARsBasicFeFloodingClassificationRefinement → MRF 精化(可选) -``` - -## 关键 Task 参数速查 - -### SARsBasicMultilooking -- INPUT_SARSCAPEDATA (required) -- OUTPUT_SARSCAPEDATA (output) -- RANGE_MULTILOOK, AZIMUTH_MULTILOOK (可选,默认自动) -- GRID_SIZE_FOR_SUGGESTED_LOOKS (目标分辨率 m) -- ROOT_URI_FOR_OUTPUT - -### SARsBasicGeocoding -- INPUT_SARSCAPEDATA (required) -- OUTPUT_SARSCAPEDATA, OUTPUT_DB_SARSCAPEDATA (output) -- DEM_SARSCAPEDATA (可选,有 DEM 精度更高) -- GEOCODE_GRID_SIZE_X, GEOCODE_GRID_SIZE_Y (像素大小 m) -- CALIBRATION: true(辐射定标) -- OUTPUT_TYPE: "output_type_db"(输出 dB) -- ROOT_URI_FOR_OUTPUT - -### SARsBasicFeFloodingClassification -- INPUT_SARSCAPEDATA (required, 灾前) -- POST_EVENT_FILE (required, 灾后) -- OUTPUT_SARSCAPEDATA (output, 分类图) -- DEM_FILE, SLOPE_FILE (可选,提升精度) -- SWL_TH: 水体阈值 dB(默认约 -14) -- RATIO_TH: 比值阈值 dB -- HIGH_SCATT_POINT_TH: 高散射点阈值 dB -- RATIO_SARSCAPEDATA, PRE_EVENT_SARSCAPEDATA, POST_EVENT_SARSCAPEDATA (output) -- ROOT_URI_FOR_OUTPUT - -### SARsBasicFeFloodingClassificationRefinement -- PRE_EVENT_FILE, POST_EVENT_FILE, CLASSIFIED_FILE, RATIO_FILE (required) -- OUTPUT_SARSCAPEDATA (output) -- DEM_FILE, SLOPE_FILE (可选) -- MRF 参数:M_STABLE_WATER, M_FLOOD, ALPHA_STABLE_WATER, ALPHA_FLOOD 等 -- ROOT_URI_FOR_OUTPUT - ---- - -## 任务列表 - -### W2-01 [TODO] 清理旧 water 代码 -- 删除 backend/app/services/water_service.py -- 删除 backend/app/routers/water.py -- 清理 backend/app/models/orm.py 中的 WaterMaskORM / WaterPairORM / FloodEventORM -- 清理 backend/app/models/__init__.py 中的 water 导入 -- 清理 backend/app/services/job_handlers.py 中的 WATER_EXTRACT / WATER_DETECT -- 删除 backend/alembic/versions/0002_water_tables.py -- 删除 frontend/src/api/water.js -- 删除 frontend/src/WaterMonitorPanel.jsx -- 清理 frontend/src/config/appConstants.js(移除 'water' tab) -- 清理 frontend/src/utils/appUiHelpers.js(移除 water case) -- 清理 frontend/src/App.jsx(移除 WaterMonitorPanel 引用) - -### W2-02 [TODO] 设计新 ORM 模型 -新增两张表: - -**SARSceneGeoORM** (sar_scene_geo) -- id, radar_data_id (FK → radar_data.id) -- geo_path: 地理编码 dB 文件路径(无扩展名,ENVI 格式) -- pixel_size_m: 像素大小 -- status: PENDING / RUNNING / DONE / FAILED -- error_msg -- created_at, updated_at - -**FloodDetectionORM** (flood_detections) -- id -- pre_scene_id (FK → sar_scene_geo.id) -- post_scene_id (FK → sar_scene_geo.id) -- output_dir: 输出目录 -- classified_path: 分类图路径 -- flood_area_km2: 洪涝面积 -- stable_water_area_km2: 稳定水体面积 -- status: PENDING / RUNNING / DONE / FAILED -- error_msg -- created_at, updated_at - -### W2-03 [TODO] 编写 Alembic 迁移 -- 新建 backend/alembic/versions/0002_water_v2.py -- down_revision = "0001" -- 创建 sar_scene_geo 和 flood_detections 表 -- 注意:旧 water_masks / water_pairs / flood_events 表如存在需 drop - -### W2-04 [TODO] 实现单景预处理服务 -新建 backend/app/services/water_service.py(全新) - -函数:`run_geocoding_workflow_sync(radar_data_id, db_session, job_id)` -1. 查 RadarDataORM 获取 file_path -2. 找 SLC 文件(_slc 后缀,ENVI 格式) -3. 创建输出目录:`water_results/{radar_unique_id}/` -4. 调用 execute_envi_task("SARsBasicMultilooking", ...) -5. 调用 execute_envi_task("SARsBasicGeocoding", ..., OUTPUT_TYPE="output_type_db") -6. 解析输出路径,更新 SARSceneGeoORM.status = DONE -7. 写进度文件(复用 _write_progress 机制) - -### W2-05 [TODO] 实现洪涝检测服务 -在 water_service.py 中新增: - -函数:`run_flood_detection_sync(pre_scene_id, post_scene_id, db_session, job_id, refine=False)` -1. 查两个 SARSceneGeoORM,验证 status=DONE -2. 创建输出目录:`water_results/flood_{pre_id}_{post_id}/` -3. 调用 execute_envi_task("SARsBasicFeFloodingClassification", ...) -4. 可选:调用 SARsBasicFeFloodingClassificationRefinement -5. 用 rasterio 读分类图,统计各类像素面积(km²) -6. 更新 FloodDetectionORM - -### W2-06 [TODO] 更新 job_handlers.py -- 注册 JOB_TYPE_WATER_GEOCODE = "WATER_GEOCODE" -- 注册 JOB_TYPE_WATER_FLOOD = "WATER_FLOOD" -- 实现 _handle_water_geocode / _handle_water_flood -- 加入 _HANDLERS 字典 - -### W2-07 [TODO] 更新 envi_runner_cli.py -- choices 增加 "water_geocode" / "water_flood" -- main() 中路由到对应 water_service 函数 - -### W2-08 [TODO] 新建 water.py router -- POST /water/geocode (admin): 提交单景地理编码任务 -- GET /water/scenes: 列出已处理场景(关联 radar_data 的 satellite/date) -- POST /water/flood-detect (admin): 提交洪涝检测任务 -- GET /water/flood-events: 列出洪涝检测结果 -- GET /water/flood-events/{id}/preview: 返回分类图预览 - -### W2-09 [TODO] 新建 WaterMonitorPanel.jsx(前端) -三个 Tab: - -**Tab 1 — 单景预处理** -- 从雷达数据列表选择一景(或输入 radar_data_id) -- 显示已处理场景列表(状态、像素大小、处理时间) -- 提交按钮 → POST /water/geocode - -**Tab 2 — 洪涝检测** -- 选择灾前场景 + 灾后场景(从已处理列表选) -- 可选:是否启用 MRF 精化 -- 提交按钮 → POST /water/flood-detect - -**Tab 3 — 洪涝事件** -- 列表:灾前日期、灾后日期、洪涝面积、稳定水体面积、状态 -- 点击查看分类图预览 - -### W2-10 [TODO] 注册路由 + 前端 Tab -- backend/app/routers/__init__.py 引入 water router -- frontend/src/config/appConstants.js 加 'water' tab -- frontend/src/utils/appUiHelpers.js 加 case -- frontend/src/App.jsx 引入 WaterMonitorPanel -- frontend/src/api/water.js(新建 API 封装) - -### W2-11 [TODO] 测试验证 -- 用 Image_Pool_2025 中一景 LT1 数据测试单景预处理 -- 选两景配对测试洪涝检测 -- 验证分类图输出和面积统计 - ---- - -## 进度 - -| 任务 | 状态 | -|------|------| -| W2-01 清理旧代码 | DONE | -| W2-02 新 ORM 模型 | DONE | -| W2-03 Alembic 迁移 | DONE | -| W2-04 单景预处理服务 | DONE | -| W2-05 洪涝检测服务 | DONE | -| W2-06 job_handlers | DONE | -| W2-07 envi_runner_cli | DONE | -| W2-08 water.py router | DONE | -| W2-09 WaterMonitorPanel | DONE | -| W2-10 路由注册+前端Tab | DONE | -| W2-11 测试验证 | TODO | diff --git a/docs/archive/WSL2_ISCE2_MINTPY_SBAS_INTEGRATION_PLAN_20260412.md b/docs/archive/WSL2_ISCE2_MINTPY_SBAS_INTEGRATION_PLAN_20260412.md deleted file mode 100644 index a7e3072..0000000 --- a/docs/archive/WSL2_ISCE2_MINTPY_SBAS_INTEGRATION_PLAN_20260412.md +++ /dev/null @@ -1,393 +0,0 @@ -# WSL2 + ISCE2 + MintPy SBAS 集成方案 - -Updated: 2026-04-12 - -## 1. 背景 - -当前仓库已经具备 SBAS/PS-InSAR 的部分系统骨架: - -- 已有 `ps_timeseries_runs` 业务表与基础 API -- 已有 `prepare -> stack_prep_initial -> materialize -> stack_prep_refresh` 四步 workflow -- 已有 `psinsar.publish.v1` manifest 解析与 `psinsar` catalog -- 已有 `ps_production`、`ps_products`、`psinsar_results` 前端入口 - -同时,实验层已经验证: - -- LT-1 stack 可以在 WSL2 `Ubuntu-24.04` 下跑通到 `run_08_igram` -- MintPy SBAS 可以产出 `timeseries.h5`、`velocity.h5` -- geocode/export 可以产出 `psinsar.publish.v1` publish bundle -- 统一环境 `isce2_mintpy_v1` 可以同时导入 `isce` 与 `mintpy` - -因此当前最合理的工作不是重写处理算法,而是把已经验证过的 WSL2 运行链稳定接入现有系统。 - -## 2. 本机环境核对 - -本方案基于 2026-04-12 的本机实际核对结果。 - -### 2.1 WSL2 发行版 - -- 可见发行版: - - `Ubuntu` - - `Ubuntu-24.04` -- SBAS 集成目标发行版: - - `Ubuntu-24.04` - -### 2.2 已确认 conda 环境 - -- `isce2` - - Python: - - `/home/administrator/miniconda3/envs/isce2/bin/python` - - 可导入: - - `isce` -- `mintpy` - - Python: - - `/home/administrator/miniconda3/envs/mintpy/bin/python` - - 可导入: - - `mintpy` -- `isce2_mintpy_v1` - - Python: - - `/home/administrator/miniconda3/envs/isce2_mintpy_v1/bin/python` - - 可导入: - - `isce` - - `mintpy` - -### 2.3 当前判断 - -- `isce2_mintpy_v1` 已满足“单环境跑 stack + MintPy”的基础条件 -- 现有 `isce2` 环境仍应保留给当前 D-InSAR 生产链 -- 运行时不应依赖交互式 `sudo` - - 所有系统依赖、conda 环境、脚本权限应在部署阶段一次性准备完成 - -## 3. 设计结论 - -### 3.1 主方案 - -SBAS 系统集成默认采用: - -- WSL distro: - - `Ubuntu-24.04` -- runtime env: - - `isce2_mintpy_v1` - -### 3.2 备用方案 - -保留实验期的双环境桥接链路作为 fallback: - -- `isce2` - - 负责 ISCE2 stack -- `mintpy` - - 负责 MintPy SBAS - -但 fallback 不应作为系统默认路径,只在统一环境故障时使用。 - -### 3.3 产品边界 - -系统正式接入边界固定为 publish bundle: - -- `manifest.json` -- `assets/` -- `preview/` -- `metadata/` - -系统不直接把以下目录视为正式产品: - -- `stack_work/` -- `mintpy_sbas_*` -- `Igrams/` -- 其他中间目录 - -## 4. 现状与缺口 - -### 4.1 已完成 - -- 运行记录模型: - - `ps_timeseries_runs` -- 基础生产 API: - - `timeseries-production` -- 基础产品 API: - - `ps-products` -- `psinsar` catalog 解析与重建 -- 前端生产页、产品页、结果页入口 - -### 4.2 未完成 - -当前系统还没有把下面四步接入正式 workflow: - -1. `run_isce2_stack` -2. `run_mintpy_sbas` -3. `export_publish_bundle` -4. `register_psinsar_product` - -这意味着当前系统是“桥接层已接入”,但还不是“完整自动生产闭环”。 - -## 5. 目标架构 - -```text -Windows FastAPI / Worker - -> submit run / record metadata / monitor progress - -> dispatch WSL jobs - -> register publish bundle into psinsar catalog - -> serve API to frontend - -WSL2 Ubuntu-24.04 - -> ISCE2 stripmapStack execution - -> MintPy SBAS inversion - -> geocode / export / preview generation - -> build manifest.json - -Formal Product Boundary - -> PSINSAR_PRODUCT_DIR// - manifest.json - assets/ - preview/ - metadata/ -``` - -设计原则: - -- 系统负责编排,不负责重计算 -- WSL2 负责执行,不负责业务登记 -- 正式产品只认 publish bundle -- D-InSAR 与 SBAS 运行时保持隔离 - -## 6. 推荐 workflow - -### 6.1 Phase 1 已有步骤 - -1. `prepare_stack_input` -2. `build_stack_prep_initial` -3. `materialize_stack_scenes` -4. `refresh_stack_ready` - -### 6.2 需要新增的正式步骤 - -5. `run_isce2_stack` - - 在 `Ubuntu-24.04` 中执行生成的 `run_01` 至 `run_08_igram` - - 产出 `Igrams/`、`geom_reference/`、`baselines/` - -6. `run_mintpy_sbas` - - 在 `isce2_mintpy_v1` 中执行 MintPy SBAS - - 产出: - - `timeseries.h5` - - `velocity.h5` - - `temporalCoherence.h5` - - `maskTempCoh.h5` - -7. `export_publish_bundle` - - geocode - - GeoTIFF 导出 - - 生成预览图 - - 生成 `manifest.json` - -8. `register_psinsar_product` - - 将 publish bundle 复制或落到正式 `PSINSAR_PRODUCT_DIR` - - 增量登记到 `psinsar` catalog - -## 7. 后端设计 - -### 7.1 配置项 - -建议显式配置以下参数,不再依赖默认继承: - -```dotenv -TIMESERIES_ENABLED=true -TIMESERIES_WSL_DISTRO=Ubuntu-24.04 -TIMESERIES_ENV_NAME=isce2_mintpy_v1 -TIMESERIES_PYTHON=/home/administrator/miniconda3/envs/isce2_mintpy_v1/bin/python -TIMESERIES_WORK_ROOT= -TIMESERIES_DEM_PATH= -TIMESERIES_ORBIT_POOL_ISCE2= -TIMESERIES_EXPERIMENT_ROOT=/experiments/isce2_sbas_timeseries -TIMESERIES_STACK_PREP_SCRIPT=/experiments/isce2_sbas_timeseries/scripts/build_lt1_stack_prep.py -TIMESERIES_MATERIALIZE_SCRIPT=/experiments/isce2_sbas_timeseries/scripts/materialize_lt1_stack_scenes.py -PSINSAR_PRODUCT_DIR= -``` - -关键要求: - -- `TIMESERIES_ENV_NAME` 与 `TIMESERIES_PYTHON` 必须指向同一环境 -- 不再让 `TIMESERIES_PYTHON` 默认继承 `ISCE2_PYTHON` - -### 7.2 模块拆分 - -建议新增或补强以下模块: - -- `timeseries_paths.py` - - 统一维护 Windows/WSL 路径转换 - - 统一生成 work/publish/logs 路径 - -- `timeseries_runtime_service.py` - - 统一执行 WSL 命令 - - 统一处理 timeout、stdout/stderr、返回码 - -- `isce2_stack_runtime_service.py` - - 封装 `run_01` 至 `run_08_igram` 执行 - -- `mintpy_runtime_service.py` - - 封装 MintPy SBAS 运行 - - 封装统一环境 runner - -- `psinsar_publish_service.py` - - 封装 export/publish bundle 生成 - - 封装产品落库前的目录检查 - -### 7.3 建议新增 job types - -- `TIMESERIES_ISCE2_STACK_RUN` -- `TIMESERIES_MINTPY_SBAS_RUN` -- `TIMESERIES_EXPORT_PUBLISH` -- `TIMESERIES_REGISTER_PRODUCT` - -当前已有: - -- `TIMESERIES_PREPARE` -- `TIMESERIES_STACK_PREP` -- `TIMESERIES_MATERIALIZE` - -### 7.4 运行记录要求 - -`ps_timeseries_runs` 至少应稳定记录: - -- `wsl_distro` -- `env_name` -- `python_path` -- `work_root` -- `publish_dir` -- `manifest_path` -- `reference_date` -- `stack_dates` -- `dem_path` -- `orbit_pool` -- `water_mask_mode` -- `step logs` -- `quality summary` -- `command snapshots` - -目标不是“只知道跑过”,而是“知道当时用什么环境、什么 DEM、什么 orbit、怎么跑出来的”。 - -## 8. 结果目录与 catalog 策略 - -### 8.1 正式产品目录 - -正式产品目录统一使用: - -- `PSINSAR_PRODUCT_DIR` - -实验目录: - -- `experiments/.../publish/...` - -只作为验证来源,不应继续充当正式产品根目录。 - -### 8.2 推荐策略 - -第一阶段先支持两种接入方式: - -1. `import_experiment_bundle` - - 将已有实验成功 bundle 导入正式产品目录 - - 用于快速验证 catalog 和前端显示 - -2. `publish_from_workflow` - - 由 workflow 自动生成 bundle 并注册 - - 作为正式闭环目标 - -### 8.3 catalog 设计要求 - -`psinsar_catalog_service` 建议补充: - -- 单 manifest 增量注册 -- 单产品刷新 -- publish 目录健康检查 -- run_id 到 product_id 的稳定关联 - -## 9. 前端设计 - -### 9.1 `ps_production` - -第一阶段重点不是分析,而是运维可用: - -- 显示 WSL/环境状态 -- 显示 workflow 步骤状态 -- 显示 stdout/stderr 日志位置 -- 失败后支持步骤级重试 -- 成功后跳转到产品详情 - -### 9.2 `ps_products` - -第一阶段重点: - -- list/filter -- 查看 manifest -- 查看 preview -- 手动 rebuild catalog -- 从 run 跳到 product - -### 9.3 `psinsar_results` - -第一阶段继续复用 catalog 只读视图即可。 - -### 9.4 `psinsar_analysis` - -继续预留,不在当前阶段展开。 - -## 10. 稳定性约束 - -必须遵守以下边界: - -- 不修改当前 D-InSAR 的默认 `ISCE2_PYTHON` -- 不把当前 D-InSAR 脚本改指向 `isce2_mintpy_v1` -- 不让业务层代码直接拼 `/mnt/z` -- 不把 `MintPy` 工作目录直接暴露为正式产品 -- 不依赖交互式 `sudo` - -## 11. 推荐实施顺序 - -### Phase A - -先做“实验成果纳入系统”: - -- 支持导入已有 experiment publish bundle -- 落到正式 `PSINSAR_PRODUCT_DIR` -- 让 catalog 和前端先可见 - -### Phase B - -再做“完整 WSL2 自动执行闭环”: - -- 接入 `run_isce2_stack` -- 接入 `run_mintpy_sbas` -- 接入 `export_publish_bundle` -- 接入 `register_psinsar_product` - -### Phase C - -最后做“生产级稳定化”: - -- 失败重试 -- 恢复执行 -- 健康检查 -- 环境快照 -- provenance 补齐 - -## 12. 验收标准 - -达到以下条件后,才算 SBAS 正式接入完成: - -1. 系统可从一个已有 `PS stack batch` 发起 SBAS 运行。 -2. Worker 可在 `Ubuntu-24.04` 中自动驱动 ISCE2 stack。 -3. Worker 可在 `isce2_mintpy_v1` 中自动驱动 MintPy。 -4. 系统可自动生成 publish bundle。 -5. bundle 可自动注册到 `psinsar` catalog。 -6. 前端可查看运行状态、产品列表、preview 和 manifest。 -7. 失败步骤可重试,且不会污染现有 D-InSAR 生产链。 - -## 13. 当前推荐 - -当前最稳的方向是: - -- 继续以 `isce2_mintpy_v1` 作为 SBAS 主运行时 -- 保留 `isce2` 与 `mintpy` 双环境桥接链路作为 fallback -- 先把 experiment publish bundle 纳入正式系统 -- 再补自动执行后四步 - -这条路线最符合当前仓库状态,也最符合“稳定优先、不破坏现有 D-InSAR”的约束。 diff --git a/docs/archive/项目汇报.md b/docs/archive/项目汇报.md deleted file mode 100644 index 0c86cc6..0000000 --- a/docs/archive/项目汇报.md +++ /dev/null @@ -1,403 +0,0 @@ -# InSAR 管理系统项目汇报 - -版本日期:2026-03-13 - -## 一、汇报目的 - -本文用于项目汇报和方案评审,重点说明三件事: - -1. 当前 InSAR 管理系统的建设内容和系统价值。 -2. 系统在 D-InSAR 生产侧已经形成的技术架构与扩展能力。 -3. 如何将 LANDSAR 系统作为第三个 D-InSAR 生产核心嵌入本系统,以及这件事对采购方案的直接影响。 - -本文是单独汇报材料,强调整体把握、系统边界、实施路径和采购判断,不展开到底层代码细节。 - -## 二、项目总体定位 - -本项目不是单一的数据展示平台,而是一个面向 InSAR 业务的综合管理与生产支撑平台,核心目标是把“数据入库、生产组织、结果管理、运行监控、质量分析”放到同一套系统中统一管理。 - -从业务定位看,系统承担了以下角色: - -1. 数据管理平台:统一管理雷达源数据、轨道数据、D-InSAR 结果、水体监测结果等。 -2. 生产组织平台:支持配对、批次管理、任务下发、后台处理、结果回收。 -3. 运维监控平台:支持健康检查、任务状态、日志、鉴权、许可证控制。 -4. 分析辅助平台:支持 AI 质量分析、AI 诊断、统计看板、地图可视化。 - -因此,这个项目的价值不只是“能不能跑一次处理”,而是把整个业务流程固化为可持续运行的平台能力。 - -## 三、当前系统建设情况 - -### 3.1 技术架构 - -当前系统采用前后端分离架构: - -1. 前端:React + Vite,使用 Leaflet 负责地图展示,Chart.js 负责统计图表,Zustand 负责状态管理。 -2. 后端:FastAPI 提供 REST API,SQLAlchemy 负责 ORM,Pydantic 负责数据模型校验。 -3. 数据库:PostgreSQL + PostGIS,用于业务数据存储和空间能力支撑。 -4. 后台任务:独立 Job Worker 进程,负责执行扫描、生产、AI、解包等异步任务。 -5. 部署入口:Nginx 负责前端静态资源和反向代理。 -6. 外部能力:可选接入 ENVI/SARscape、ISCE2、Ollama 等外部组件。 - -### 3.2 主要功能模块 - -系统目前已经具备较完整的业务模块: - -1. 雷达数据管理 - 支持监控目录扫描、元数据解析、预览图生成、空间范围展示、检索和分页浏览。 -2. 轨道数据管理 - 支持轨道文件关联和本地轨道池同步,为生产链路提供基础输入。 -3. 配对与批次管理 - 支持 D-InSAR 配对策略、批量任务组织、批次项管理和后续复制导出。 -4. D-InSAR 生产中心 - 已经从单引擎思路升级为多引擎生产中心,支持统一查看引擎状态、提交任务、查看运行记录。 -5. D-InSAR 结果管理 - 支持结果扫描入库、地图展示、缓存、导出、AI 评分与人工标注。 -6. AI 能力 - 支持质量模型训练、质量预测、影像诊断、报告生成。 -7. 水体监测 - 已形成相对独立的处理链路和界面。 -8. 运维与权限 - 包含健康检查、任务管理、审计日志、用户管理、登录限流、许可证控制等。 - -### 3.3 当前系统成熟度判断 - -从工程状态看,项目已经不是原型阶段,而是进入“可持续扩展的平台阶段”: - -1. 业务边界已明确。 -2. 前后端结构已稳定。 -3. 后台任务与数据库模型已成体系。 -4. 多引擎 D-InSAR 生产框架已经搭起来。 -5. 系统具备继续接入第三方生产核心的基础。 - -这意味着后续采购不应再按“买一个孤立的软件工具”来考虑,而应按“纳入统一生产平台的外部生产核心”来考虑。 - -## 四、系统运行主线 - -从全流程看,当前系统的主线可以概括为: - -1. 数据进入系统 - 扫描雷达目录、轨道目录、结果目录,完成入库和索引。 -2. 用户在前端组织任务 - 选择 AOI、筛选源数据、执行配对、形成批次、下发生产任务。 -3. 后端进入异步处理模式 - 系统创建任务记录和作业记录,由 Worker 执行具体处理。 -4. 外部引擎执行生产 - 当前已支持 ENVI/SARscape 和 ISCE2 两类生产能力。 -5. 结果回流系统 - 产物落地后可扫描入库、生成缓存、展示到地图和列表,并进入后续分析。 -6. 运维与质量闭环 - 通过健康检查、日志、审计、AI 分析和人工核查,形成可运维闭环。 - -这一主线决定了本系统天然适合接入多个生产引擎,只要外部引擎能被标准化调用。 - -## 五、D-InSAR 生产体系现状 - -### 5.1 已形成的多引擎框架 - -当前项目已经不再把 D-InSAR 生产写死在某一套工具链上,而是抽象成统一的“生产引擎”框架。对每个引擎,系统要求至少具备三类能力: - -1. 可用性检查:系统要知道该引擎当前能不能用。 -2. Profile 列表:系统要知道该引擎支持哪些处理链路。 -3. 统一运行入口:系统要能按同样的方式下发任务并接收结果。 - -在这个框架下,系统现在已有三类引擎标识: - -1. `sarscape` - 已可运行,对接现有 ENVI/SARscape 流程。 -2. `isce2` - 已可运行,通过 WSL 调用 ISCE2 链路。 -3. `landsar` - 目前仅做接口预留和前端占位,尚未真正接入生产能力。 - -### 5.2 当前已落地的两个生产核心 - -#### 1. ENVI / SARscape - -这是当前系统最成熟的生产核心,主要特点: - -1. 与现有历史流程兼容度高。 -2. 支持 `metatask` 和 `custom6` 两种 profile。 -3. 已具备运行、日志、结果回收和健康检查能力。 -4. 适合作为当前生产主力链路。 - -#### 2. ISCE2 - -这是当前系统第二个生产核心,主要特点: - -1. 通过 WSL 方式运行,避免强依赖本机 Windows 图形环境。 -2. 已具备环境检查、路径转换、脚本调用、任务执行能力。 -3. 适合承接 LT-1 等标准化链路。 -4. 为系统后续接入更多异构引擎提供了样板。 - -### 5.3 LANDSAR 当前状态 - -这一点在汇报和采购中必须明确说明: - -截至 2026-03-13,`LANDSAR` 在本项目中属于“架构已预留、能力未接入”的状态,具体表现为: - -1. 后端已存在 `landsar` 引擎占位。 -2. 前端生产中心已预留 `LANDSAR` 状态卡位置。 -3. 健康检查体系已将 `LANDSAR` 视为可纳入统一监控的引擎类型。 -4. 轨道池体系中已预留 `ORBIT_POOL_LANDSAR` 的配置位置。 -5. 但任务提交、执行、日志、结果回收尚未真正实现。 -6. 当前生产提交接口实际仍只支持已落地的引擎,`LANDSAR` 还不能像现有生产核心那样直接投入运行。 - -这意味着:系统架构已经准备好接纳 LANDSAR,但采购后的接入工作仍需要正式实施,不能误判为“买来即可无缝运行”。 - -## 六、为什么要把 LANDSAR 作为第三个生产核心 - -从平台演进角度,引入 LANDSAR 的意义主要有四点: - -1. 降低对单一商业引擎的依赖。 -2. 提升不同场景下的处理适配能力。 -3. 形成多引擎并行的生产格局,增强方案弹性。 -4. 使采购的软件能力真正纳入平台统一调度,而不是形成新的信息孤岛。 - -如果 LANDSAR 只是作为独立软件单独运行,那么它对现有系统的价值有限,最终仍然会回到“人工切换工具、人工导入结果、人工追踪过程”的旧模式。只有把它嵌入本系统,成为第三个生产核心,采购价值才能最大化。 - -## 七、LANDSAR 接入的目标定位 - -本项目对 LANDSAR 的目标定位不是“重写 LANDSAR 算法”,而是“把 LANDSAR 系统现有 D-InSAR 生产服务纳入统一调度平台”。 - -因此,推荐的定位是: - -1. 本系统负责任务组织、权限控制、日志审计、状态展示、结果入库和运维闭环。 -2. LANDSAR 系统负责实际 D-InSAR 生产处理。 -3. 双方通过标准接口交互,而不是把两边代码强行揉成一体。 - -这也是最符合采购落地的模式,因为它对供应商最明确,对我方平台风险最可控。 - -## 八、LANDSAR 与本系统的推荐交互模式 - -### 8.1 推荐模式:服务嵌入式接入 - -建议将 LANDSAR 以“外部生产服务”的方式接入本系统,而不是以人工操作方式接入。 - -推荐架构如下: - -1. 用户仍然只在本系统前端操作。 -2. 用户在 D-InSAR 生产中心中选择 `LANDSAR` 作为引擎。 -3. 本系统后端创建标准任务和作业记录。 -4. `landsar` 适配引擎把任务参数发送给 LANDSAR 生产服务。 -5. LANDSAR 返回运行编号并执行处理。 -6. 本系统轮询任务状态,或者接收 LANDSAR 回调通知。 -7. 处理完成后,产物输出到约定目录,或由本系统拉取到指定目录。 -8. 本系统完成结果扫描、入库、展示和后续分析。 - -这样做的最大好处是:对用户来说,LANDSAR 不是另一套系统,而是本系统中的第三个生产核心。 - -### 8.2 不推荐模式:纯人工导入导出 - -如果采购的 LANDSAR 只能做到“人工打开软件、手工点运行、手工拷贝结果”,则它不能真正成为第三个生产核心,只能成为一个外部工具。这样会带来以下问题: - -1. 无法统一任务状态。 -2. 无法统一日志与审计。 -3. 无法统一批次管理。 -4. 无法纳入健康检查。 -5. 无法形成稳定的生产闭环。 - -因此,采购时必须把“可集成、可调用、可监控”作为硬条件。 - -## 九、LANDSAR 接入后的业务流程 - -建议将接入流程设计为以下八步: - -1. 任务组织 - 用户在本系统完成配对、批次选择、AOI 约束和参数选择。 -2. 任务提交 - 本系统向 LANDSAR 服务提交标准化请求,包括输入目录、输出目录、profile、处理参数、任务编号等。 -3. 运行登记 - 本系统保存本地任务 ID 和 LANDSAR 侧运行 ID 的映射关系。 -4. 执行监控 - 本系统通过轮询接口或回调机制获取进度、状态和错误信息。 -5. 日志采集 - 本系统读取 LANDSAR 返回的执行日志摘要,必要时保留完整日志文件地址。 -6. 产物回收 - LANDSAR 将产物输出到双方约定目录,或通过下载接口交给本系统。 -7. 结果入库 - 本系统对产物进行扫描、索引、缓存生成和结果入库。 -8. 统一展示 - 用户在现有结果管理界面中查看 LANDSAR 结果,与其他引擎结果统一管理。 - -## 十、LANDSAR 作为第三核心时的系统改造点 - -从本系统角度,真正需要做的改造不是推倒重来,而是在现有多引擎框架上补全 `landsar` 适配层。 - -### 10.1 后端改造点 - -1. 完成 `landsar_engine.py` - 目前这个文件只是占位,需要实现真正的可用性检查、profile 获取和运行逻辑。 -2. 增加 LANDSAR 任务处理器 - 可以新增独立 `JOB_TYPE_LANDSAR_RUN`,也可以复用统一外部引擎作业模式。 -3. 增加运行状态映射 - 需要把 LANDSAR 的状态映射到本系统统一状态,如 `PENDING`、`RUNNING`、`COMPLETED`、`FAILED`。 -4. 增加日志采集与错误归一化 - 要保证前端看到的日志和错误信息可理解、可追踪。 -5. 增加结果回收逻辑 - 需要把 LANDSAR 输出目录纳入结果扫描与入库体系。 -6. 补充轨道池适配 - 当前系统已预留 `ORBIT_POOL_LANDSAR`,采购后需明确 LANDSAR 轨道格式和同步策略。 - -### 10.2 前端改造点 - -1. 让 `LANDSAR` 状态卡从“预留”变成“可选可提交”。 -2. 展示 LANDSAR 专属 profile 和参数项。 -3. 在运行历史中显示 `engine=landsar`。 -4. 在结果列表和详情中标识结果来源于 LANDSAR。 - -### 10.3 运维改造点 - -1. 增加 LANDSAR 服务健康检查。 -2. 增加供应商服务连通性检查。 -3. 增加回调鉴权或调用鉴权。 -4. 增加 LANDSAR 结果目录一致性检查。 - -## 十一、采购时必须明确的接口要求 - -这一部分是采购方案的核心。若这些能力得不到保障,LANDSAR 就无法真正嵌入平台。 - -### 11.1 必须具备的调用方式 - -供应商至少应提供以下三种方式之一,优先级从高到低如下: - -1. HTTP/REST 服务接口 - 最推荐,最适合平台接入。 -2. 命令行接口(CLI) - 可作为次优方案,但需要明确返回码、日志和输出目录约定。 -3. Python/SDK 接口 - 也可接受,但后续部署和版本兼容成本通常更高。 - -如果三者都没有,只提供 GUI 操作,则不建议作为“第三生产核心”采购。 - -### 11.2 必须具备的最小接口能力 - -若采用服务接口,建议采购时要求至少提供以下能力: - -1. 提交任务接口 - 输入参数包括任务编号、profile、输入目录、输出目录、处理参数。 -2. 查询任务状态接口 - 能返回排队、运行中、完成、失败、取消等状态。 -3. 查询进度接口 - 能返回百分比或阶段性进度。 -4. 查询日志接口 - 至少能返回日志摘要,最好支持完整日志文件。 -5. 查询产物接口 - 能返回主产物、辅产物、日志文件、质量文件等路径或下载地址。 -6. 健康检查接口 - 能返回服务可用性、许可证状态、核心依赖状态。 -7. 能力枚举接口 - 能列出当前支持的 profile、版本、约束条件。 - -### 11.3 必须明确的数据约定 - -采购时要和供应商约定清楚以下数据边界: - -1. 输入目录结构由谁负责准备。 -2. 输出目录结构是否固定。 -3. 主结果文件命名规则是否稳定。 -4. 日志文件命名规则和保存期限。 -5. 失败任务是否保留中间文件。 -6. 轨道文件格式和目录组织方式。 -7. 坐标系、DEM、投影和元数据约定。 - -没有这些约定,后续集成必然出现反复沟通和返工。 - -## 十二、建议的采购技术条款 - -为了确保 LANDSAR 真正能嵌入本系统,建议采购文件中写入如下技术条款。 - -### 12.1 必选条款 - -1. 供应商须提供可编程调用接口,不得仅提供人工图形界面。 -2. 供应商须提供任务提交、状态查询、日志查询、结果获取的完整接口说明。 -3. 供应商须支持与第三方业务系统进行集成,并配合联调。 -4. 供应商须提供稳定的版本管理和升级兼容说明。 -5. 供应商须明确许可证机制对服务化调用的限制条件。 -6. 供应商须配合完成至少一个标准 D-InSAR 生产 profile 的端到端联调验收。 - -### 12.2 强烈建议条款 - -1. 支持回调通知机制,减少轮询压力。 -2. 支持独立健康检查接口。 -3. 支持 profile 枚举和参数模板查询。 -4. 支持输出标准化目录结构。 -5. 支持服务部署在内网服务器环境。 -6. 支持批量任务执行和失败重试。 - -### 12.3 验收建议 - -建议把验收拆成两个层次: - -1. 集成验收 - 能在本系统中完成 LANDSAR 任务提交、状态查看、日志查看、结果入库。 -2. 业务验收 - 能稳定完成至少一个真实生产样例,并在前端完成统一展示。 - -### 12.4 采购方案分级建议 - -为了避免采购完成后无法嵌入现有系统,建议把采购方案分成三档判断: - -1. 推荐方案 - 采购“LANDSAR 服务接口版 + 联调实施服务 + 服务化许可”。 - 该方案要求供应商提供 API 或等价服务接口,并配合完成与本系统的任务、状态、日志、结果联通。这是最符合“第三生产核心”目标的方案。 -2. 可接受方案 - 采购“LANDSAR CLI/SDK 版 + 本地部署支持 + 接口适配支持”。 - 该方案在没有标准服务接口时仍可落地,但后续部署、升级、兼容和运维成本会明显高于推荐方案。 -3. 不建议方案 - 仅采购“LANDSAR 桌面 GUI 单机版”。 - 该方案无法稳定嵌入本系统,只能形成独立工具,难以支撑统一任务管理、日志审计和结果治理,不建议作为本项目采购目标。 - -## 十三、LANDSAR 接入对采购决策的影响 - -这一点需要在汇报中重点强调: - -采购 LANDSAR,不是单纯采购一个处理软件,而是在采购本系统的第三个生产核心。 - -因此,采购判断应从以下维度展开: - -1. 不只是看算法效果,还要看是否可集成。 -2. 不只是看桌面端能不能跑,还要看服务端能不能调。 -3. 不只是看一次能不能出结果,还要看能否纳入长期运维。 -4. 不只是看供应商交付软件,还要看是否交付接口和联调能力。 - -如果采购时忽略这些条件,后续极有可能出现“软件买到了,但无法嵌入现有平台”的情况,导致采购价值被打折。 - -## 十四、建议的实施路径 - -建议分两步推进: - -### 第一阶段:完成服务嵌入 - -目标是让 LANDSAR 先成为“可调度、可监控、可回收”的第三生产核心。 - -重点工作: - -1. 完成接口对接。 -2. 完成任务状态联通。 -3. 完成日志与结果回流。 -4. 完成前端选择与展示。 - -### 第二阶段:完成深度治理 - -目标是把 LANDSAR 结果纳入更完整的统一治理体系。 - -重点工作: - -1. 结果版本管理。 -2. 多引擎结果对比。 -3. 质量评价与复核闭环。 -4. 更细粒度的运行审计和运维检查。 - -这样推进的好处是先把采购价值尽快落地,再逐步做深,而不是一开始就把目标定得过重。 - -## 十五、结论 - -综合判断如下: - -1. 当前 InSAR 管理系统已经具备平台化能力,适合承接第三方 D-InSAR 生产核心。 -2. 系统的多引擎架构已经形成,`LANDSAR` 不是从零开始接,而是在既有框架中补齐适配层。 -3. 截至目前,`LANDSAR` 在系统中仍是预留状态,尚未真正接入生产执行。 -4. 因此,采购方案必须把“可编程调用、可集成、可监控、可回收”写成明确要求。 -5. 最推荐的采购与实施方式,是将 LANDSAR 以外部生产服务的形式嵌入本系统,作为第三个 D-InSAR 生产核心统一调度。 - -一句话概括:本项目当前已经具备“接入第三生产核心”的平台基础,下一步采购是否成功,关键不在于是否购买到一个能跑的工具,而在于是否购买到一个能够被本系统稳定接入的服务化能力。