diff --git a/README.md b/README.md index 2ddf3e6..d99dc8f 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,10 @@ Before processing each scene, the wrapper checks the scene output directory for More details: [docs/sarscape_go_wrapper.md](docs/sarscape_go_wrapper.md) +## Data Management Roadmap + +The processing workflow is expected to handle mixed GF3/GF3B/GF3C inputs, both L1A and L2 products, archive-name metadata parsing, and persistent product/task tracking. The proposed upgrade path is documented in [docs/data_management_upgrade_plan.md](docs/data_management_upgrade_plan.md). + ## Python Pipeline Use an environment that already has working GDAL/OSGeo Python bindings. On Windows, Conda/Miniforge is usually the least fragile route. diff --git a/docs/data_management_upgrade_plan.md b/docs/data_management_upgrade_plan.md new file mode 100644 index 0000000..3262f4d --- /dev/null +++ b/docs/data_management_upgrade_plan.md @@ -0,0 +1,271 @@ +# GF3 数据管理升级方案 + +本文档记录 GF3 L1A 到 L2 管线升级为数据管理系统的设计方向。当前仓库已经包含两条处理路径: + +- SARscape Go wrapper:面向 Windows、ENVI/IDL Runtime 和 SARscape 的生产处理路径。 +- Python pipeline:基于 GDAL/Rasterio/NumPy 的纯 Python 处理路径。 + +后续业务不再只是单批次 L1A 处理。输入可能已经是 L2,也可能是 L1A;卫星平台可能是 GF3、GF3B、GF3C;数据需要被发现、登记、查询、调度、归档和复跑。因此建议把项目从“处理脚本”升级为“产品识别 + 数据库存档 + 处理任务编排 + 执行器”的结构。 + +## 目标 + +1. 统一识别 GF3、GF3B、GF3C 产品。 +2. 同时管理 L1A 原始产品和 L2 成果产品。 +3. 尽量从压缩包名或文件名中解析产品基本信息,包括中心经纬度。 +4. 用数据库记录产品、任务、产物和失败原因,避免只依赖目录结构。 +5. 保留现有 SARscape Go wrapper 作为生产执行器,避免一次性重写稳定处理链。 +6. 为后续 Web 管理界面、地图查询、批处理调度、报表导出预留数据模型。 + +## 总体架构 + +```text +输入目录 / NAS / 历史输出 + | + v +产品发现与命名解析 + | + v +产品库 SQLite + | + +--> L2 产品登记、索引、查询 + | + +--> L1A 待处理任务生成 + | + v + 执行器调度层 + | + +--> SARscape Go wrapper + +--> Python pipeline + | + v + L2 产物登记和任务状态更新 +``` + +## 产品识别模型 + +建议新增统一产品模型,例如 `ProductInfo`: + +| 字段 | 说明 | +| --- | --- | +| `product_id` | 产品唯一标识,优先使用产品号或完整场景名 | +| `scene_name` | 不含压缩包扩展名的场景名 | +| `satellite` | `GF3`、`GF3B`、`GF3C` | +| `level` | `L1A`、`L2`,必要时扩展到其他级别 | +| `mode` | 成像模式,例如 `FSI`、`FSII` 等 | +| `polarizations` | 标准化后的极化列表,例如 `HH,HV` | +| `acquisition_date` | 成像日期 | +| `center_lon` | 从文件名或元数据解析的中心经度 | +| `center_lat` | 从文件名或元数据解析的中心纬度 | +| `source_type` | `archive`、`metadata_xml`、`directory`、`sarscape_product` | +| `source_path` | 当前发现的源路径 | +| `metadata_path` | 元数据 XML 路径,未解压时可为空 | +| `archive_path` | 压缩包路径,非压缩产品可为空 | +| `size_bytes` | 文件或目录大小 | +| `mtime` | 源数据最后修改时间 | +| `checksum` | 可选,后续用于完整性校验 | +| `status` | `discovered`、`registered`、`processing`、`processed`、`failed`、`archived` | + +产品识别应允许“文件名解析先行,元数据补全兜底”。压缩包尚未解压时,系统也应能建立基本索引;等需要处理或做精确校验时,再从 metadata XML 补充字段。 + +## 文件名解析 + +现有样例: + +```text +GF3_MH1_FSII_051377_E132.3_N48.2_20260514_L1A_HHHV_L10007356478.tar.gz +``` + +可解析为: + +| 字段 | 值 | +| --- | --- | +| `satellite` | `GF3` | +| `mode` | `FSII` | +| `center_lon` | `132.3` | +| `center_lat` | `48.2` | +| `acquisition_date` | `2026-05-14` | +| `level` | `L1A` | +| `polarizations` | `HH,HV` | +| `product_id` | `L10007356478` | + +解析规则建议: + +1. 卫星字段匹配 `GF3`、`GF3B`、`GF3C`。 +2. 经度匹配 `E132.3` 或 `W132.3`,转换为正负浮点数。 +3. 纬度匹配 `N48.2` 或 `S48.2`,转换为正负浮点数。 +4. 日期匹配连续 8 位数字,并转换为 `YYYY-MM-DD`。 +5. 级别匹配 `L1A`、`L2`,后续可扩展。 +6. 极化字段支持 `HH`、`HV`、`VH`、`VV` 及组合字段,例如 `HHHV`。 +7. 解析失败不能阻止登记,应记录 `parse_status` 和 `parse_message`。 + +GF3B/GF3C 的命名如果与 GF3 不完全一致,应通过多个 parser 版本兼容,而不是把所有规则压进一个不可维护的正则。 + +## 数据库设计 + +第一阶段建议使用 SQLite。它部署简单、便于随仓库工具使用,也能支撑批量目录索引和本地管理。未来如果需要多用户并发或 Web 服务,可以迁移到 PostgreSQL/PostGIS。 + +### `products` + +记录发现到的 L1A、L2 和其他可识别产品。 + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `id` | INTEGER PRIMARY KEY | 内部 ID | +| `product_id` | TEXT | 产品号或业务唯一标识 | +| `scene_name` | TEXT | 场景名 | +| `satellite` | TEXT | GF3/GF3B/GF3C | +| `level` | TEXT | L1A/L2 | +| `mode` | TEXT | 成像模式 | +| `polarizations` | TEXT | 逗号分隔的极化 | +| `acquisition_date` | TEXT | ISO 日期 | +| `center_lon` | REAL | 中心经度 | +| `center_lat` | REAL | 中心纬度 | +| `source_type` | TEXT | archive/metadata_xml/directory/sarscape_product | +| `source_path` | TEXT UNIQUE | 源路径 | +| `metadata_path` | TEXT | 元数据路径 | +| `archive_path` | TEXT | 压缩包路径 | +| `size_bytes` | INTEGER | 大小 | +| `mtime` | TEXT | 修改时间 | +| `checksum` | TEXT | 可选校验 | +| `parse_status` | TEXT | ok/partial/failed | +| `parse_message` | TEXT | 解析说明 | +| `created_at` | TEXT | 入库时间 | +| `updated_at` | TEXT | 更新时间 | + +### `processing_tasks` + +记录 L1A 到 L2 的处理任务。 + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `id` | INTEGER PRIMARY KEY | 任务 ID | +| `input_product_id` | INTEGER | 输入产品 | +| `output_product_id` | INTEGER | 输出产品,可为空 | +| `executor` | TEXT | `sarscape_go` 或 `python_gdal` | +| `status` | TEXT | pending/running/succeeded/failed/skipped | +| `requested_polarizations` | TEXT | 请求处理的极化 | +| `dem_path` | TEXT | DEM 路径 | +| `output_dir` | TEXT | 输出目录 | +| `attempt_count` | INTEGER | 尝试次数 | +| `started_at` | TEXT | 开始时间 | +| `finished_at` | TEXT | 结束时间 | +| `error_message` | TEXT | 失败原因 | +| `config_json` | TEXT | 处理参数快照 | + +### `artifacts` + +记录最终成果、中间成果、日志和快视图。 + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `id` | INTEGER PRIMARY KEY | 产物 ID | +| `product_id` | INTEGER | 所属产品 | +| `task_id` | INTEGER | 所属任务 | +| `artifact_type` | TEXT | geo/sml/hdr/quicklook/log/intermediate | +| `polarization` | TEXT | HH/HV/VH/VV | +| `path` | TEXT UNIQUE | 文件路径 | +| `size_bytes` | INTEGER | 文件大小 | +| `mtime` | TEXT | 修改时间 | +| `is_final` | INTEGER | 是否最终交付成果 | + +## CLI 演进 + +建议在现有 `gf3-l1a2l2` CLI 下新增 `inventory` 和 `tasks` 子命令: + +```powershell +gf3-l1a2l2 inventory init --db .\gf3_inventory.sqlite +gf3-l1a2l2 inventory scan --input D:\GF3 --db .\gf3_inventory.sqlite +gf3-l1a2l2 inventory list --level L1A --satellite GF3B --db .\gf3_inventory.sqlite +gf3-l1a2l2 inventory export --format geojson --output scenes.geojson --db .\gf3_inventory.sqlite + +gf3-l1a2l2 tasks create-l2 --input-query "level=L1A,status=registered" --output E:\GF3\L2 --db .\gf3_inventory.sqlite +gf3-l1a2l2 tasks run --executor sarscape_go --db .\gf3_inventory.sqlite +gf3-l1a2l2 tasks retry --failed --db .\gf3_inventory.sqlite +``` + +Go wrapper 可以保留现在的命令行接口,同时后续增加一种任务配置输入: + +```powershell +gf3wrapper.exe -task task_123.json +``` + +这样数据库调度层负责生成标准任务文件,Go wrapper 只负责稳定执行 SARscape 处理。 + +## L1A 与 L2 的处理策略 + +| 输入类型 | 策略 | +| --- | --- | +| L1A 压缩包 | 登记产品;如缺 L2,则生成处理任务 | +| L1A 解压目录 | 登记产品;可直接进入处理任务 | +| L1A metadata XML | 登记产品;以所在目录作为场景目录 | +| L2 SARscape 输出 | 登记最终产物;不再重复处理 | +| L2 压缩包或目录 | 登记为 L2 产品;解析中心点和时间后进入可查询状态 | +| 无法识别数据 | 记录为 `parse_status=failed`,留给人工处理 | + +对于已经存在的 L2 数据,系统应优先入库并校验完整性,而不是重新从 L1A 处理。完整性校验可以沿用现有 Go wrapper 的思路:最终 `*_geo.sml` 和匹配主数据文件必须存在且非空。 + +## 执行器边界 + +### SARscape Go wrapper + +继续作为生产主执行器。后续增强重点: + +1. 处理完成后校验请求极化的最终 `*_geo.sml` 和主数据文件。 +2. 若 IDL/SARscape 只打印错误但进程返回 0,仍应根据产物校验判定失败。 +3. `-keep-extracted` 需要真正控制 `.gf3_extract` 是否保留。 +4. 输出任务结果 JSON,便于数据库层读取。 + +### Python pipeline + +继续作为可测试、可扩展的备用执行器。后续增强重点: + +1. 增加 inventory 数据模型和扫描命令。 +2. 解压失败应进入任务失败状态,而不只是写日志。 +3. 明确 HH/HV/VH/VV 支持范围,与 SARscape 路径保持文档一致。 +4. 补充发现、解压、命名解析、任务状态的单元测试。 + +## 分阶段实施计划 + +### 阶段 1:产品识别和解析 + +- 新增产品模型。 +- 新增文件名 parser。 +- 覆盖 GF3、GF3B、GF3C、L1A、L2、E/W/N/S 坐标测试。 +- 新增 `inventory scan` 的只读扫描能力,不改变现有处理链。 + +### 阶段 2:SQLite 产品库 + +- 新增数据库初始化和 upsert。 +- 扫描目录后写入 `products`。 +- 支持按卫星、级别、日期、中心点范围查询。 +- 支持 CSV/GeoJSON 导出。 + +### 阶段 3:任务管理 + +- 新增 `processing_tasks` 和 `artifacts`。 +- 根据 L1A 产品和缺失 L2 的条件生成任务。 +- 记录失败原因、重试次数和处理参数。 +- 处理已有 L2 产品的入库和完整性校验。 + +### 阶段 4:执行器整合 + +- 数据库层调用 SARscape Go wrapper 或 Python pipeline。 +- Go wrapper 输出结构化结果。 +- 处理完成后自动登记 L2 产物和日志。 +- 支持断点续跑和失败重试。 + +### 阶段 5:管理界面和报表 + +- 增加简单 Web UI 或桌面管理工具。 +- 支持地图查看中心点。 +- 支持按日期、卫星、级别、状态筛选。 +- 支持导出批处理报告和归档清单。 + +## 当前仓库需要优先修正的点 + +1. Python 测试需要标准化运行方式。当前直接运行 `python -m unittest discover -s tests` 时,`src/` 不在 import path 中。 +2. Python 环境文件已有依赖,但当前系统 Python 未安装 `pytest`。应在 README 中强调使用 Conda 环境或 editable install。 +3. Go wrapper 的 `-keep-extracted` 当前主要是配置字段,后续应补实际行为。 +4. SARscape/IDL 脚本失败是否能反映到进程退出码需要验证,建议增加处理后产物完整性校验。 +5. 现有未跟踪的目录报告 `docs/20260514_directory_report.md` 可以作为真实输出组织结构参考,确认后再决定是否纳入版本库。 +