docs: consolidate current project documentation
This commit is contained in:
@@ -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 <repo-url>
|
||||
@@ -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` 是工作笔记,不是当前架构或部署事实的最高依据。
|
||||
|
||||
@@ -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.
|
||||
@@ -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`。
|
||||
- 数据库自维护默认保守,不自动做破坏性重建。
|
||||
@@ -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、健康检查和文档。
|
||||
+29
-20
@@ -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_<timestamp>_<engine>_<profile>_<seq>_<suffix>
|
||||
├─ timeseries
|
||||
│ └─ <stack_key>
|
||||
│ └─ runs
|
||||
│ └─ run_<timestamp>_<engine>_<profile>_<seq>_<suffix>
|
||||
│ └─ sbas
|
||||
│ └─ <managed SBAS result bundles>
|
||||
└─ _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
|
||||
|
||||
@@ -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_<uuid>`
|
||||
- `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 执行:
|
||||
|
||||
- 文件夹模式目标目录:`<dest_dir>/<task_alias>/`
|
||||
- zip 模式目标文件:`<dest_dir>/<task_alias>.zip`
|
||||
- master 目录:`<task_alias>/master`
|
||||
- slave 目录:`<task_alias>/slave`
|
||||
- 如果启用 `include_orbit_files`,从 `radar_data.orbit_file_path` 找 master/slave 精轨并复制到 `<task_alias>/orbit/`
|
||||
- 直接复制配对时保存的原始产品目录;D-InSAR 分发不再优先使用 `envi_import/`
|
||||
- 先复制到临时目录,完成后再替换为最终 Task 目录或 ZIP,避免留下半成品
|
||||
- 写入 `<task_alias>/.dinsar_pair.json`
|
||||
- `skip_existing=true` 时,文件夹模式检查 `<task_alias>/master` 和 `<task_alias>/slave` 非空即跳过;ZIP 模式只检查 `<task_alias>.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
|
||||
<dest_dir>/
|
||||
data/
|
||||
orbit/
|
||||
pairs.json
|
||||
manifest.json
|
||||
```
|
||||
|
||||
规则:
|
||||
|
||||
- `data/` 只复制唯一源影像目录或文件,命名为 `scene_<source_path_hash>_<source_name>`。
|
||||
- `orbit/` 只复制唯一精密轨道文件,命名为 `orbit_<source_path_hash>_<orbit_name>`。
|
||||
- `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/<engine>__<profile>.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 先跑输入资产预检
|
||||
@@ -1,168 +0,0 @@
|
||||
# D-InSAR 去重源数据包反向还原工具任务书
|
||||
|
||||
日期:2026-05-11
|
||||
|
||||
## 背景
|
||||
|
||||
本系统新增“去重源数据包”分发模式。该模式不直接生成每个干涉对的 `Task_*` 目录,而是只分发唯一源影像、唯一精密轨道文件和配对关系文件,减少外部分发时的重复复制量。
|
||||
|
||||
反向还原工具由任务接收方本地运行,将去重源数据包还原为传统 D-InSAR `Task_*` 目录结构。
|
||||
|
||||
## 输入目录结构
|
||||
|
||||
```text
|
||||
BundleRoot/
|
||||
data/
|
||||
scene_<hash>_<source_name>/
|
||||
...
|
||||
orbit/
|
||||
orbit_<hash>_<orbit_name>.txt
|
||||
...
|
||||
pairs.json
|
||||
manifest.json
|
||||
```
|
||||
|
||||
`orbit/` 可能不存在,或 `pairs.json` 内某些配对的轨道字段为空。
|
||||
|
||||
## 输出目录结构
|
||||
|
||||
```text
|
||||
OutputRoot/
|
||||
Task_YYYYMMDD_YYYYMMDD/
|
||||
master/
|
||||
<master source product content>
|
||||
slave/
|
||||
<slave source product content>
|
||||
orbit/
|
||||
<master/slave orbit files, if present>
|
||||
.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:<scene_pair_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。
|
||||
@@ -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. 逐步减少无明确角色的过程型文件
|
||||
|
||||
@@ -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。
|
||||
```
|
||||
@@ -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 运行时仍是外部部署前提。
|
||||
```
|
||||
@@ -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`)
|
||||
```
|
||||
|
||||
|
||||
@@ -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 `<productInfo>...</productInfo>`.
|
||||
- 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 <scene.tiff> <scene.meta.xml> <yyyymmdd>.slc.par <yyyymmdd>.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 \
|
||||
<yyyymmdd>.slc.par <orbit_txt_or_orbit_folder>
|
||||
```
|
||||
|
||||
### 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.
|
||||
+48
-112
@@ -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
|
||||
需要判断当前实现时,优先看代码入口和本索引列出的文档。
|
||||
|
||||
@@ -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.
|
||||
@@ -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/<run_id>/...`
|
||||
- publish root:
|
||||
- `TIMESERIES_PRODUCT_DIR/<stack_key>/runs/<run_id>/...`
|
||||
|
||||
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:
|
||||
|
||||
- `<publish_dir>/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`
|
||||
@@ -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.
|
||||
@@ -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
|
||||
<PS_TIMESERIES_WORK_ROOT>/<batch_id>/<run_id>/
|
||||
stack_input/
|
||||
isce2_stack/
|
||||
mintpy/
|
||||
export/
|
||||
logs/
|
||||
```
|
||||
|
||||
Recommended publish layout:
|
||||
|
||||
```text
|
||||
<PSINSAR_PRODUCT_DIR>/<product_id>/
|
||||
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
|
||||
@@ -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__<profile>.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
|
||||
@@ -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 | 时间顺序配对策略 |
|
||||
|
||||
---
|
||||
|
||||
**文档结束**
|
||||
@@ -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。
|
||||
@@ -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 精配对`。
|
||||
|
||||
@@ -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 导入命令,并明确产出:
|
||||
|
||||
- `<date>_<product>.slc`
|
||||
- `<date>_<product>.slc.par`
|
||||
- 可选 `<date>_<product>.slc.update`
|
||||
- 可选 `<date>_<product>.slc.update.par`
|
||||
|
||||
拼接时:
|
||||
|
||||
- 如果所有分片都有 update SLC,则优先用 update SLC tab 调用 `SLC_cat_list.py`
|
||||
- 如果 update SLC 不可用,则回退到普通 SLC tab,并打印 warning
|
||||
- 最终 `<date>.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/<date>/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"
|
||||
```
|
||||
|
||||
@@ -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/<pair_key>/<run_key>/
|
||||
input_assets/
|
||||
task_manifest.json
|
||||
orbits/
|
||||
dem/
|
||||
<project_name>/
|
||||
DOWNLOAD/
|
||||
ifgram_list.txt
|
||||
...
|
||||
|
||||
backend/runtime/pyint_templates/<pair_key>/<run_key>/
|
||||
<project_name>.template
|
||||
|
||||
backend/runtime/pyint_output/<pair_key>/<run_key>/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/<project_name>/...`
|
||||
- 被 `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 产物”。
|
||||
@@ -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 官方文档是:
|
||||
|
||||
- <https://www.gamma-rs.ch/uploads/media/2023-1_TR_China_LT1_Support_in_GAMMA.pdf>
|
||||
|
||||
其中与本设计直接相关的结论有两点:
|
||||
|
||||
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`,所以方案必须覆盖:
|
||||
- 每个分片导入后的参数文件
|
||||
- 最终拼接得到的 `<date>.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` 生成最终 `<date>.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 配对”来设计接口。
|
||||
@@ -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、预览图、监测点曲线和点矢量下载;
|
||||
- 前端生产页和结果页构建通过。
|
||||
@@ -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.
|
||||
@@ -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 导出。
|
||||
- 自动点只适合作为快速检查和报告初稿候选点,正式报告应支持用户指定点、导入点位或专家确认点位。
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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/<date>/` 数据,并在运行前手动修改日期、阈值、宽高和种子点等参数。
|
||||
|
||||
文档中的 `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 可作为下一步高级入口接入,但不应在普通页面展示开发诊断信息。
|
||||
@@ -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. 是否接受新增少量数据库字段和一份幂等迁移。
|
||||
|
||||
@@ -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/<project>/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/<project>/DOWNLOAD`,放入 master/slave Sentinel-1 ZIP。
|
||||
2. 写入 `$TEMPLATEDIR/<project>.template`,包含 masterDate、slaveDate、swath、burst、look、轨道、DEM、网络参数等。
|
||||
3. 调用 `down2slc_sen.py <project> <masterDate>`。
|
||||
4. 调用 `down2slc_sen.py <project> <slaveDate>`。
|
||||
5. 调用 `extract_s1_bursts.py <project> <masterDate> <slaveDate>`。
|
||||
6. 调用 `generate_rdc_dem.py <project>` 或等价 DEM 生成步骤。
|
||||
7. 调用 `coreg_s1_gamma.py <project> <masterDate> <slaveDate>`。
|
||||
8. 调用 `diff_gamma.py <project> <masterDate> <slaveDate>`。
|
||||
9. 调用 `unwrap_gamma.py <project> <masterDate> <slaveDate>`。
|
||||
10. 调用 `geocode_gamma.py <project> <masterDate>-<slaveDate>`。
|
||||
|
||||
也可以参考 `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 更稳、更快,也更符合“配对不要做得过度复杂,但生产尽可能有精度”的目标。
|
||||
@@ -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/
|
||||
<S1 master zip or SAFE>
|
||||
slave/
|
||||
<S1 slave zip or SAFE>
|
||||
orbit/
|
||||
<matched EOF files>
|
||||
dem/
|
||||
<optional dem reference or config>
|
||||
pyint/
|
||||
<project>.template
|
||||
```
|
||||
|
||||
分发开关建议:
|
||||
|
||||
- 是否复制 EOF 到 Task 目录。
|
||||
- 是否输出 ZIP 压缩包。
|
||||
- 是否保留中间 SLC/RSLC 产物。
|
||||
- 是否只发布核心结果。
|
||||
|
||||
如果生产目标是 PyINT/Gamma,Task 到 PyINT scratch 目录之间可以由 runner 再做一次结构化投放,把 master/slave ZIP 放入:
|
||||
|
||||
```text
|
||||
$SCRATCHDIR/<project>/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,避免在配对阶段过度复杂化。
|
||||
@@ -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.
|
||||
@@ -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/<run_id>/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.
|
||||
@@ -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 <command>`; 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.
|
||||
@@ -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
|
||||
<div className="ai-analysis-panel">
|
||||
{/* 顶部状态卡片 */}
|
||||
<div className="ai-status-cards">
|
||||
<OllamaStatusCard />
|
||||
<ModelSelectorCard />
|
||||
</div>
|
||||
|
||||
{/* 快速诊断 */}
|
||||
<AiQuickDiagnoseCard />
|
||||
|
||||
{/* 诊断历史 */}
|
||||
<div className="ai-diagnosis-history">
|
||||
<div className="history-toolbar">
|
||||
<input placeholder="搜索结果名称..." />
|
||||
<select>
|
||||
<option>全部风险等级</option>
|
||||
<option>高风险</option>
|
||||
<option>中风险</option>
|
||||
<option>低风险</option>
|
||||
</select>
|
||||
<button>导出选中</button>
|
||||
</div>
|
||||
<AiDiagnosisTable />
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### 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(可选)**:
|
||||
- 用户偏好保存
|
||||
- 对比分析
|
||||
- 自动化工作流
|
||||
|
||||
---
|
||||
|
||||
**文档结束**
|
||||
@@ -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 回调 ✅
|
||||
<IDLAutomationPanel
|
||||
apiEndpoint={apiClient.defaults.baseURL}
|
||||
readOnly={isReadOnlyUser}
|
||||
onJobQueued={(taskId) => handleTaskStart(taskId, '任务已入队,等待处理...')}
|
||||
/>
|
||||
|
||||
// DataMonitorPanel - 有 onTaskStart 回调 ✅
|
||||
<DataMonitorPanel
|
||||
apiEndpoint={apiClient.defaults.baseURL}
|
||||
onTaskStart={handleTaskStart}
|
||||
readOnly={isReadOnlyUser}
|
||||
/>
|
||||
|
||||
// DataCopierPanel - 没有任何回调 ❌
|
||||
<DataCopierPanel
|
||||
apiEndpoint={apiClient.defaults.baseURL}
|
||||
readOnly={isReadOnlyUser}
|
||||
/>
|
||||
```
|
||||
|
||||
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' && (
|
||||
<div className="panel-content" style={{ flex: '1 1 auto', padding: 0, overflow: 'hidden' }}>
|
||||
<DataCopierPanel
|
||||
apiEndpoint={apiClient.defaults.baseURL}
|
||||
readOnly={isReadOnlyUser}
|
||||
onJobQueued={(taskId) => handleTaskStart(taskId, '数据分发任务已入队')} // 添加此行
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
```
|
||||
|
||||
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' && (
|
||||
<div className="warning-banner">
|
||||
⚠️ 数据分发任务正在后台运行,请勿关闭浏览器或启动其他耗时任务
|
||||
</div>
|
||||
)}
|
||||
```
|
||||
|
||||
**优点**:
|
||||
- 改动最小
|
||||
- 不影响现有架构
|
||||
|
||||
**缺点**:
|
||||
- 不解决根本问题
|
||||
- 用户仍可能启动冲突任务
|
||||
|
||||
---
|
||||
|
||||
## 扫描任务修复方案(可选)
|
||||
|
||||
### 方案 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` 回调,集成到全局锁定机制
|
||||
@@ -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 文档为总入口。
|
||||
@@ -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 修复,待验证)
|
||||
@@ -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
|
||||
// 修改前
|
||||
<DataCopierPanel
|
||||
apiEndpoint={apiClient.defaults.baseURL}
|
||||
readOnly={isReadOnlyUser}
|
||||
/>
|
||||
|
||||
// 修改后
|
||||
<DataCopierPanel
|
||||
apiEndpoint={apiClient.defaults.baseURL}
|
||||
readOnly={isReadOnlyUser}
|
||||
onJobQueued={(taskId) => 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
|
||||
**状态**:✅ 代码修改完成,等待测试验证
|
||||
@@ -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 选项灰显,无法选择
|
||||
@@ -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` 管理
|
||||
- ✅ 支持多种部署场景
|
||||
- ✅ 客户环境可自定义配置
|
||||
- ✅ 无需修改代码即可部署
|
||||
@@ -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. 继续第一阶段的其他测试
|
||||
@@ -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 修复
|
||||
@@ -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
|
||||
@@ -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` 的“当前有效”区为准。
|
||||
@@ -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. 添加日志清理脚本
|
||||
|
||||
还是你想先看看这个方案,再决定是否实施?
|
||||
@@ -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 行
|
||||
|
||||
**风险评估**:🟢 低风险
|
||||
- 不影响现有功能
|
||||
- 只读操作无风险
|
||||
- 删除操作有权限控制和确认
|
||||
|
||||
**状态**:✅ 代码完成,等待测试验证
|
||||
@@ -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、跨卫星和多引擎生产时不会继续堆积配对债务
|
||||
@@ -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。
|
||||
@@ -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` 零值来源排查
|
||||
|
||||
这轮实验的价值不在于立刻修好流程,而在于把问题边界收窄到“轨道链条”还是“导入几何链条”。
|
||||
@@ -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 几何链问题”从概念判断变成可测的证据链。
|
||||
@@ -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 本体
|
||||
@@ -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 实际消费字段之间是否存在系统性偏差
|
||||
@@ -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. 发布前执行一次全量重建,提升线上命中率
|
||||
@@ -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 能力。
|
||||
@@ -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 生产环境中直接做依赖升级
|
||||
@@ -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 已有正式运行记录、结果目录和前端入口
|
||||
- 后续只需要继续往中间补处理链,而不是推倒重来
|
||||
@@ -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
|
||||
<PSINSAR_PRODUCT_DIR>/<year>/<run_id>/
|
||||
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 成果的基础能力。
|
||||
@@ -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_<group_key>` / `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 旧链路不碰
|
||||
|
||||
按这个边界推进,风险可控,且后续可以逐步把实验能力提升为正式生产能力。
|
||||
@@ -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
|
||||
<TIMESERIES_WORK_ROOT>\{run_id}\
|
||||
input\
|
||||
stack\
|
||||
mintpy\
|
||||
logs\
|
||||
publish\
|
||||
```
|
||||
|
||||
WSL 侧:
|
||||
|
||||
```text
|
||||
/mnt/.../timeseries_work/{run_id}/
|
||||
input/
|
||||
stack/
|
||||
mintpy/
|
||||
logs/
|
||||
publish/
|
||||
```
|
||||
|
||||
正式发布根目录:
|
||||
|
||||
```text
|
||||
<PSINSAR_PRODUCT_DIR>\{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/<date>/`
|
||||
- 显式写入 `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_<run_id>`
|
||||
- `catalog_name = psinsar`
|
||||
- `product_type = psinsar_bundle`
|
||||
- `display_name = SBAS_<direction>_<reference_date>_<run_id short>`
|
||||
- `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 生产
|
||||
|
||||
这样可以做到:
|
||||
|
||||
- 技术上最贴合现有系统
|
||||
- 风险上最小
|
||||
- 后续也最容易把实验链稳稳升级成正式生产链
|
||||
@@ -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 实验稳稳接成系统能力。
|
||||
@@ -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 复制时会构建:
|
||||
- `<dest>/<task_alias>/master`
|
||||
- `<dest>/<task_alias>/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/<basename>`
|
||||
|
||||
它不会生成:
|
||||
|
||||
- 栈 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_<group_key>/
|
||||
stack_input_manifest.json
|
||||
scenes/
|
||||
20250118/
|
||||
source_scene.json
|
||||
<raw scene or link>
|
||||
20250315/
|
||||
source_scene.json
|
||||
<raw scene or link>
|
||||
20250510/
|
||||
source_scene.json
|
||||
<raw scene or link>
|
||||
inputs/
|
||||
dem/
|
||||
logs/
|
||||
```
|
||||
|
||||
后续运行阶段再生成:
|
||||
|
||||
```text
|
||||
Stack_<group_key>/
|
||||
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。
|
||||
@@ -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。
|
||||
|
||||
@@ -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. 异常处理不规范
|
||||
|
||||
建议按照优先级逐步修复,并在修复后进行充分的回归测试。长期来看,应该引入更完善的权限管理、资源配额、审计日志和速率限制机制。
|
||||
@@ -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 数量上限:_个
|
||||
|
||||
### 解包性能
|
||||
- 修复前平均耗时:_秒
|
||||
- 修复后平均耗时:_秒
|
||||
- 性能差异:_%
|
||||
|
||||
---
|
||||
|
||||
## 签名确认
|
||||
|
||||
每完成一个阶段,填写以下信息:
|
||||
|
||||
### 第一阶段
|
||||
- 修复人:_______
|
||||
- 测试人:_______
|
||||
- 完成日期:_______
|
||||
- 签名:_______
|
||||
|
||||
### 第二阶段
|
||||
- 修复人:_______
|
||||
- 测试人:_______
|
||||
- 完成日期:_______
|
||||
- 签名:_______
|
||||
|
||||
### 第三阶段
|
||||
- 修复人:_______
|
||||
- 测试人:_______
|
||||
- 完成日期:_______
|
||||
- 签名:_______
|
||||
@@ -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 <noreply@anthropic.com>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 第二阶段:防御性增强(中风险)
|
||||
|
||||
**目标**:添加保护措施,不改变现有业务逻辑
|
||||
|
||||
### 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 <noreply@anthropic.com>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 第三阶段:安全加固(高风险)
|
||||
|
||||
**目标**:解决权限和安全问题,可能涉及架构调整
|
||||
|
||||
### 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 <noreply@anthropic.com>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 第四阶段:权限重构(待定)
|
||||
|
||||
**目标**:解决只读用户权限问题
|
||||
|
||||
**前置条件**:
|
||||
- 第一、二、三阶段全部完成并稳定运行
|
||||
- 明确只读用户的使用场景和需求
|
||||
- 前端团队配合(如果需要修改 API 调用)
|
||||
|
||||
**待讨论问题**:
|
||||
1. 系统中是否有只读用户?
|
||||
2. 只读用户的典型使用场景是什么?
|
||||
3. 是否可以接受只读用户无法查看预览图?
|
||||
4. 是否需要前端配合修改?
|
||||
|
||||
---
|
||||
|
||||
## 修复时间表
|
||||
|
||||
| 阶段 | 预计时间 | 风险等级 | 依赖 |
|
||||
|------|---------|---------|------|
|
||||
| 第一阶段 | 1 天 | 🟢 低 | 无 |
|
||||
| 第二阶段 | 2-3 天 | 🟡 中 | 第一阶段完成 |
|
||||
| 第三阶段 | 2-3 天 | 🟡 中 | 第二阶段完成 |
|
||||
| 第四阶段 | 待定 | 🔴 高 | 需求确认 |
|
||||
|
||||
---
|
||||
|
||||
## 回滚计划
|
||||
|
||||
每个阶段独立提交,出现问题时可以:
|
||||
|
||||
1. **快速回滚**:
|
||||
```bash
|
||||
git revert <commit-hash>
|
||||
```
|
||||
|
||||
2. **部分回滚**:
|
||||
如果只有某个修复有问题,可以单独回滚该文件:
|
||||
```bash
|
||||
git checkout <previous-commit> -- <file-path>
|
||||
git commit -m "revert: 回滚 <file-path> 的修改"
|
||||
```
|
||||
|
||||
3. **紧急回滚**:
|
||||
如果影响生产环境,立即回滚到上一个稳定版本:
|
||||
```bash
|
||||
git reset --hard <last-stable-commit>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 监控指标
|
||||
|
||||
修复后需要监控的关键指标:
|
||||
|
||||
### 第一阶段
|
||||
- ENVI 工作流成功率(应该提升)
|
||||
- 任务状态准确性
|
||||
- 统计数据准确性
|
||||
|
||||
### 第二阶段
|
||||
- AOI token 内存占用(应该稳定)
|
||||
- 400 vs 500 错误比例(400 应该增加)
|
||||
- 路径扫描结果准确性
|
||||
|
||||
### 第三阶段
|
||||
- 解包成功率(应该不变)
|
||||
- 解包性能(可能略微下降)
|
||||
- 安全事件(应该为 0)
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
这个分阶段修复计划:
|
||||
- ✅ 从低风险到高风险渐进式修复
|
||||
- ✅ 每个阶段独立可测试、可回滚
|
||||
- ✅ 优先修复明确的 bug,暂缓架构性改动
|
||||
- ✅ 保留了灵活性,可以根据实际情况调整
|
||||
|
||||
**建议**:先完成第一阶段,充分测试后再决定是否继续第二阶段。
|
||||
@@ -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. **下周计划**:
|
||||
- [ ] 根据第一阶段测试结果决定是否继续
|
||||
- [ ] 如果继续,开始第二阶段
|
||||
|
||||
---
|
||||
|
||||
## 备注
|
||||
|
||||
- 每次修复后立即更新此文档
|
||||
- 测试结果详细记录,包括成功和失败的情况
|
||||
- 遇到问题及时记录到"问题记录"区域
|
||||
- 回滚操作必须记录原因和影响范围
|
||||
@@ -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 语法检查已通过,现在等待功能测试验证。
|
||||
|
||||
**风险评估**:🟢 低风险
|
||||
**建议**:可以安全地进行测试,不太可能影响现有功能。
|
||||
@@ -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 <commit-hash>
|
||||
```
|
||||
|
||||
### 部分回滚
|
||||
```bash
|
||||
git checkout <previous-commit> -- <file-path>
|
||||
git commit -m "revert: 回滚 <file-path> 的修改"
|
||||
```
|
||||
|
||||
### 紧急回滚
|
||||
```bash
|
||||
git reset --hard <last-stable-commit>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📈 监控指标
|
||||
|
||||
### 关键指标
|
||||
- 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
|
||||
@@ -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` 与本文件。
|
||||
@@ -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` 调整
|
||||
@@ -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 |
|
||||
@@ -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/<product_id>/
|
||||
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=<windows path>
|
||||
TIMESERIES_DEM_PATH=<windows path>
|
||||
TIMESERIES_ORBIT_POOL_ISCE2=<windows path>
|
||||
TIMESERIES_EXPERIMENT_ROOT=<repo>/experiments/isce2_sbas_timeseries
|
||||
TIMESERIES_STACK_PREP_SCRIPT=<repo>/experiments/isce2_sbas_timeseries/scripts/build_lt1_stack_prep.py
|
||||
TIMESERIES_MATERIALIZE_SCRIPT=<repo>/experiments/isce2_sbas_timeseries/scripts/materialize_lt1_stack_scenes.py
|
||||
PSINSAR_PRODUCT_DIR=<formal publish root>
|
||||
```
|
||||
|
||||
关键要求:
|
||||
|
||||
- `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”的约束。
|
||||
@@ -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 生产核心统一调度。
|
||||
|
||||
一句话概括:本项目当前已经具备“接入第三生产核心”的平台基础,下一步采购是否成功,关键不在于是否购买到一个能跑的工具,而在于是否购买到一个能够被本系统稳定接入的服务化能力。
|
||||
Reference in New Issue
Block a user