Refactor flood disaster analysis workspace
This commit is contained in:
@@ -10,6 +10,7 @@ from . import (
|
|||||||
dinsar,
|
dinsar,
|
||||||
dinsar_products,
|
dinsar_products,
|
||||||
dinsar_production,
|
dinsar_production,
|
||||||
|
flood,
|
||||||
hazard,
|
hazard,
|
||||||
health,
|
health,
|
||||||
idl,
|
idl,
|
||||||
@@ -58,5 +59,6 @@ def include_all_routers(router: APIRouter) -> None:
|
|||||||
router.include_router(stats.router)
|
router.include_router(stats.router)
|
||||||
router.include_router(idl.router)
|
router.include_router(idl.router)
|
||||||
router.include_router(hazard.router)
|
router.include_router(hazard.router)
|
||||||
|
router.include_router(flood.router)
|
||||||
router.include_router(water.router)
|
router.include_router(water.router)
|
||||||
router.include_router(logs.router)
|
router.include_router(logs.router)
|
||||||
|
|||||||
@@ -0,0 +1,183 @@
|
|||||||
|
"""Flood disaster analysis router.
|
||||||
|
|
||||||
|
This router exposes the flood-analysis business API while reusing the
|
||||||
|
existing water/flood processing records and jobs during migration.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from fastapi import APIRouter, Depends, HTTPException
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
|
from ..database import get_db
|
||||||
|
from ..models import AuthUserORM
|
||||||
|
from . import water as water_compat
|
||||||
|
from .dependencies import _get_current_user, _require_admin
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
class FloodPreprocessRequest(BaseModel):
|
||||||
|
radar_data_id: int = Field(..., description="RadarDataORM 主键")
|
||||||
|
|
||||||
|
|
||||||
|
class FloodWaterExtractionRequest(BaseModel):
|
||||||
|
scene_id: Optional[int] = Field(default=None, description="SARSceneGeoORM 主键")
|
||||||
|
input_path: Optional[str] = Field(default=None, description="直接指定 GeoTIFF/ENVI 路径")
|
||||||
|
|
||||||
|
|
||||||
|
class FloodPairSearchRequest(BaseModel):
|
||||||
|
pre_start: Optional[str] = Field(default=None, description="灾前开始日期 YYYYMMDD")
|
||||||
|
pre_end: Optional[str] = Field(default=None, description="灾前结束日期 YYYYMMDD")
|
||||||
|
post_start: Optional[str] = Field(default=None, description="灾后开始日期 YYYYMMDD")
|
||||||
|
post_end: Optional[str] = Field(default=None, description="灾后结束日期 YYYYMMDD")
|
||||||
|
overlap_threshold: float = Field(default=0.3, ge=0.0, le=1.0, description="最小重叠比例")
|
||||||
|
|
||||||
|
|
||||||
|
class FloodDetectionRequest(BaseModel):
|
||||||
|
pre_scene_id: int = Field(..., description="灾前 SARSceneGeoORM 主键")
|
||||||
|
post_scene_id: int = Field(..., description="灾后 SARSceneGeoORM 主键")
|
||||||
|
refine: bool = Field(default=False, description="是否启用 MRF 精化")
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/flood/preprocess", status_code=202)
|
||||||
|
async def submit_flood_preprocess(
|
||||||
|
req: FloodPreprocessRequest,
|
||||||
|
db: AsyncSession = Depends(get_db),
|
||||||
|
admin_user: AuthUserORM = Depends(_require_admin),
|
||||||
|
):
|
||||||
|
"""提交水体提取前置处理任务,当前复用旧 water geocode 链路。"""
|
||||||
|
return await water_compat.submit_geocode(req, db=db, admin_user=admin_user)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/flood/scenes")
|
||||||
|
async def list_flood_scenes(
|
||||||
|
limit: int = 20,
|
||||||
|
offset: int = 0,
|
||||||
|
db: AsyncSession = Depends(get_db),
|
||||||
|
current_user: AuthUserORM = Depends(_get_current_user),
|
||||||
|
):
|
||||||
|
"""列出可作为水体提取输入的地理编码场景。"""
|
||||||
|
return await water_compat.list_scenes(limit=limit, offset=offset, db=db, current_user=current_user)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/flood/scenes/done-radar-ids")
|
||||||
|
async def list_flood_done_radar_ids(
|
||||||
|
db: AsyncSession = Depends(get_db),
|
||||||
|
current_user: AuthUserORM = Depends(_get_current_user),
|
||||||
|
):
|
||||||
|
return await water_compat.list_done_scene_radar_ids(db=db, current_user=current_user)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/flood/scenes/active-radar-ids")
|
||||||
|
async def list_flood_active_radar_ids(
|
||||||
|
db: AsyncSession = Depends(get_db),
|
||||||
|
current_user: AuthUserORM = Depends(_get_current_user),
|
||||||
|
):
|
||||||
|
return await water_compat.list_active_scene_radar_ids(db=db, current_user=current_user)
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/flood/scenes/{scene_id}/reset")
|
||||||
|
async def reset_flood_scene(
|
||||||
|
scene_id: int,
|
||||||
|
db: AsyncSession = Depends(get_db),
|
||||||
|
admin_user: AuthUserORM = Depends(_require_admin),
|
||||||
|
):
|
||||||
|
return await water_compat.reset_scene_status(scene_id=scene_id, db=db, admin_user=admin_user)
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/flood/water-extractions", status_code=202)
|
||||||
|
async def submit_flood_water_extraction(
|
||||||
|
req: FloodWaterExtractionRequest,
|
||||||
|
db: AsyncSession = Depends(get_db),
|
||||||
|
admin_user: AuthUserORM = Depends(_require_admin),
|
||||||
|
):
|
||||||
|
"""提交单景水体提取任务,当前复用 Otsu 快速水体检测实现。"""
|
||||||
|
return await water_compat.submit_water_detect(req, db=db, admin_user=admin_user)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/flood/water-extractions")
|
||||||
|
async def list_flood_water_extractions(
|
||||||
|
limit: int = 20,
|
||||||
|
offset: int = 0,
|
||||||
|
status: Optional[str] = None,
|
||||||
|
db: AsyncSession = Depends(get_db),
|
||||||
|
current_user: AuthUserORM = Depends(_get_current_user),
|
||||||
|
):
|
||||||
|
return await water_compat.list_water_detections(
|
||||||
|
limit=limit,
|
||||||
|
offset=offset,
|
||||||
|
status=status,
|
||||||
|
db=db,
|
||||||
|
current_user=current_user,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/flood/water-extractions/{extraction_id}/preview")
|
||||||
|
async def get_flood_water_extraction_preview(
|
||||||
|
extraction_id: int,
|
||||||
|
db: AsyncSession = Depends(get_db),
|
||||||
|
current_user: AuthUserORM = Depends(_get_current_user),
|
||||||
|
):
|
||||||
|
return await water_compat.get_water_detection_preview(
|
||||||
|
detection_id=extraction_id,
|
||||||
|
db=db,
|
||||||
|
current_user=current_user,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/flood/pairs/search")
|
||||||
|
async def search_flood_pairs(
|
||||||
|
req: FloodPairSearchRequest,
|
||||||
|
db: AsyncSession = Depends(get_db),
|
||||||
|
current_user: AuthUserORM = Depends(_get_current_user),
|
||||||
|
):
|
||||||
|
return await water_compat.find_water_pairs(req, db=db, current_user=current_user)
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/flood/detections", status_code=202)
|
||||||
|
async def submit_flood_detection(
|
||||||
|
req: FloodDetectionRequest,
|
||||||
|
db: AsyncSession = Depends(get_db),
|
||||||
|
admin_user: AuthUserORM = Depends(_require_admin),
|
||||||
|
):
|
||||||
|
return await water_compat.submit_flood_detect(req, db=db, admin_user=admin_user)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/flood/detections")
|
||||||
|
async def list_flood_detections(
|
||||||
|
db: AsyncSession = Depends(get_db),
|
||||||
|
current_user: AuthUserORM = Depends(_get_current_user),
|
||||||
|
):
|
||||||
|
return await water_compat.list_flood_events(db=db, current_user=current_user)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/flood/detections/{detection_id}/preview/{layer}")
|
||||||
|
async def get_flood_detection_preview(
|
||||||
|
detection_id: int,
|
||||||
|
layer: str,
|
||||||
|
db: AsyncSession = Depends(get_db),
|
||||||
|
current_user: AuthUserORM = Depends(_get_current_user),
|
||||||
|
):
|
||||||
|
normalized = layer.strip().lower()
|
||||||
|
if normalized == "pre":
|
||||||
|
return await water_compat.flood_event_pre_preview(
|
||||||
|
event_id=detection_id,
|
||||||
|
db=db,
|
||||||
|
current_user=current_user,
|
||||||
|
)
|
||||||
|
if normalized == "post":
|
||||||
|
return await water_compat.flood_event_post_preview(
|
||||||
|
event_id=detection_id,
|
||||||
|
db=db,
|
||||||
|
current_user=current_user,
|
||||||
|
)
|
||||||
|
if normalized == "classified":
|
||||||
|
return await water_compat.flood_event_classified_preview(
|
||||||
|
event_id=detection_id,
|
||||||
|
db=db,
|
||||||
|
current_user=current_user,
|
||||||
|
)
|
||||||
|
raise HTTPException(status_code=404, detail=f"不支持的洪涝预览图层: {layer}")
|
||||||
@@ -0,0 +1,304 @@
|
|||||||
|
# 水体提取与洪涝灾害分析设计
|
||||||
|
|
||||||
|
更新日期:2026-05-14
|
||||||
|
|
||||||
|
## 1. 设计结论
|
||||||
|
|
||||||
|
这个模块不应该被设计成一堆遥感处理工具的集合。它的业务目标很简单:
|
||||||
|
|
||||||
|
```text
|
||||||
|
提取水体 -> 检测洪涝 -> 套合已有矢量数据 -> 输出结果
|
||||||
|
```
|
||||||
|
|
||||||
|
因此前端和后端都应围绕这条流水线组织,而不是暴露过多工程阶段。用户关心的是:
|
||||||
|
|
||||||
|
- 哪些数据可以拿来提水体。
|
||||||
|
- 哪两期数据可以用来判断洪涝。
|
||||||
|
- 洪涝范围在哪里、面积多大。
|
||||||
|
- 影响了哪些灾害点、行政区、AOI 或其他矢量对象。
|
||||||
|
- 结果能不能上图、导出、形成报告。
|
||||||
|
|
||||||
|
## 2. 当前真实边界
|
||||||
|
|
||||||
|
现有代码不能理解为已经具备多源 ENVI 洪涝系统。
|
||||||
|
|
||||||
|
当前事实:
|
||||||
|
|
||||||
|
- LT-1 有 `SARsImportLuTan1` 相关 ENVI/SARscape 链路,可作为第一条精密检测主线。
|
||||||
|
- GF3 有纯 Python/GDAL 的 L1A 到 L2 处理,不是 ENVI/SARscape 洪涝导入。
|
||||||
|
- Sentinel-1 有源产品、ZIP/SAFE、EOF、PyINT/Gamma 相关管理设计,不是 ENVI/SARscape 洪涝导入。
|
||||||
|
- 当前 `water` 模块已经有旧的单景 geocode、Otsu 快速检测、精密洪涝检测和地图预览能力,可作为迁移基础。
|
||||||
|
|
||||||
|
所以第一阶段不能承诺 GF3/Sentinel-1 可直接做 ENVI 精密洪涝检测。页面上必须显示为“待接入导入适配器”。
|
||||||
|
|
||||||
|
## 3. 产品信息架构
|
||||||
|
|
||||||
|
左侧一级入口合并为:
|
||||||
|
|
||||||
|
```text
|
||||||
|
洪涝灾害分析
|
||||||
|
├─ 洪涝灾害分析
|
||||||
|
└─ 水体监测(旧入口)
|
||||||
|
```
|
||||||
|
|
||||||
|
新的“洪涝灾害分析”工作台只保留四个视图:
|
||||||
|
|
||||||
|
```text
|
||||||
|
1. 水体提取
|
||||||
|
2. 洪涝检测
|
||||||
|
3. 套合分析
|
||||||
|
4. 结果与任务
|
||||||
|
```
|
||||||
|
|
||||||
|
旧“水体监测”入口短期保留,承载当前 `WaterMonitorPanel` 的功能。后续功能成熟后再逐步迁入新工作台。
|
||||||
|
|
||||||
|
## 4. 地图设计
|
||||||
|
|
||||||
|
不新建第二套地图。
|
||||||
|
|
||||||
|
当前中间地图大屏已经有底图、行政区定位、导出、雷达覆盖、洪涝影像叠加能力。新工作台应复用 `AppMapWorkspace` 和 `App.jsx` 中的 Leaflet 实例,通过回调控制图层:
|
||||||
|
|
||||||
|
```text
|
||||||
|
FloodAnalysisWorkspace
|
||||||
|
-> onShowSourceSceneOnMap
|
||||||
|
-> onShowFloodOnMap
|
||||||
|
-> onToggleFloodLayer
|
||||||
|
```
|
||||||
|
|
||||||
|
地图只负责展示,业务按钮放在左侧工作台。
|
||||||
|
|
||||||
|
## 5. 流水线设计
|
||||||
|
|
||||||
|
### 5.1 水体提取
|
||||||
|
|
||||||
|
目标:把单期雷达影像处理成水体范围。
|
||||||
|
|
||||||
|
用户动作:
|
||||||
|
|
||||||
|
- 选择数据。
|
||||||
|
- 查看数据是否可处理。
|
||||||
|
- 提交水体提取。
|
||||||
|
- 查看水体范围。
|
||||||
|
- 加载到地图。
|
||||||
|
|
||||||
|
数据状态建议:
|
||||||
|
|
||||||
|
- `已入库`:系统只管理了原始/解压数据。
|
||||||
|
- `可提取`:已有可用处理器。
|
||||||
|
- `处理中`:任务正在运行。
|
||||||
|
- `已提取`:已有水体范围产品。
|
||||||
|
- `待接入`:数据存在,但缺少对应导入/处理适配器。
|
||||||
|
|
||||||
|
传感器边界:
|
||||||
|
|
||||||
|
- LT-1:优先接旧 ENVI/SARscape 链路。
|
||||||
|
- GF3:待补 ENVI/SARscape 洪涝导入;现有 Python/GDAL 成果可作为后续快速路线输入。
|
||||||
|
- Sentinel-1:待补 ENVI/SARscape 洪涝导入,或后续明确非 ENVI 检测路线。
|
||||||
|
|
||||||
|
### 5.2 洪涝检测
|
||||||
|
|
||||||
|
目标:比较灾前/灾后水体变化,识别新增水体作为洪涝范围。
|
||||||
|
|
||||||
|
用户动作:
|
||||||
|
|
||||||
|
- 选择灾前数据。
|
||||||
|
- 选择灾后数据。
|
||||||
|
- 自动推荐配对。
|
||||||
|
- 提交洪涝检测。
|
||||||
|
- 查看洪涝面积和稳定水体面积。
|
||||||
|
- 加载灾前、灾后、分类图到地图。
|
||||||
|
|
||||||
|
检测路线:
|
||||||
|
|
||||||
|
- 精密路线:ENVI/SARscape `SARsBasicFeFloodingClassification`。
|
||||||
|
- 快速路线:Otsu/GeoTIFF 水体掩膜差异,作为轻量能力。
|
||||||
|
|
||||||
|
输出类别:
|
||||||
|
|
||||||
|
```text
|
||||||
|
1 = 稳定水体
|
||||||
|
2 = 洪涝/新增水体
|
||||||
|
3 = 高散射
|
||||||
|
4 = 非水体
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.3 套合分析
|
||||||
|
|
||||||
|
目标:把洪涝范围与已有矢量数据叠加,回答“影响了什么”。
|
||||||
|
|
||||||
|
第一阶段套合对象:
|
||||||
|
|
||||||
|
- 灾害点。
|
||||||
|
- 行政区。
|
||||||
|
- 当前 AOI。
|
||||||
|
- 自定义矢量。
|
||||||
|
|
||||||
|
输出:
|
||||||
|
|
||||||
|
- 洪涝范围矢量。
|
||||||
|
- 受影响灾害点清单。
|
||||||
|
- 行政区/AOI 洪涝面积统计。
|
||||||
|
- 距离洪涝范围一定阈值内的风险点。
|
||||||
|
- GeoJSON 和表格导出。
|
||||||
|
|
||||||
|
灾害点关系建议:
|
||||||
|
|
||||||
|
- `inside_flood`:点落在洪涝范围内。
|
||||||
|
- `near_flood`:点距离洪涝边界小于阈值。
|
||||||
|
- `inside_scene_only`:点在影像覆盖范围内,但未受洪涝影响。
|
||||||
|
|
||||||
|
面积统计必须使用合适投影,不能直接用经纬度面积作为正式统计。
|
||||||
|
|
||||||
|
### 5.4 结果与任务
|
||||||
|
|
||||||
|
目标:统一查看任务、图层、结果和导出。
|
||||||
|
|
||||||
|
展示字段:
|
||||||
|
|
||||||
|
- 结果 ID。
|
||||||
|
- 灾前日期。
|
||||||
|
- 灾后日期。
|
||||||
|
- 卫星组合。
|
||||||
|
- 处理器。
|
||||||
|
- 洪涝面积。
|
||||||
|
- 稳定水体面积。
|
||||||
|
- 影响灾害点数量。
|
||||||
|
- 状态。
|
||||||
|
- 更新时间。
|
||||||
|
|
||||||
|
操作:
|
||||||
|
|
||||||
|
- 加载图层。
|
||||||
|
- 打开产品包。
|
||||||
|
- 导出 GeoTIFF/GeoJSON。
|
||||||
|
- 生成报告。
|
||||||
|
|
||||||
|
## 6. 后端域模型
|
||||||
|
|
||||||
|
后端可以继续兼容 `/water/*`,但新能力建议逐步进入 `/flood/*`。
|
||||||
|
|
||||||
|
最小模型不要过度复杂,先围绕四类对象:
|
||||||
|
|
||||||
|
```text
|
||||||
|
WaterExtractionRun
|
||||||
|
FloodDetectionRun
|
||||||
|
FloodOverlayRun
|
||||||
|
FloodResultProduct
|
||||||
|
```
|
||||||
|
|
||||||
|
如果后续需要多源产品治理,再扩展:
|
||||||
|
|
||||||
|
```text
|
||||||
|
FloodReadyProduct
|
||||||
|
FloodPair
|
||||||
|
FloodVectorAsset
|
||||||
|
FloodReport
|
||||||
|
```
|
||||||
|
|
||||||
|
## 7. API 草案
|
||||||
|
|
||||||
|
### 水体提取
|
||||||
|
|
||||||
|
```text
|
||||||
|
GET /flood/sources
|
||||||
|
POST /flood/water-extractions
|
||||||
|
GET /flood/water-extractions
|
||||||
|
GET /flood/water-extractions/{id}
|
||||||
|
GET /flood/water-extractions/{id}/preview
|
||||||
|
```
|
||||||
|
|
||||||
|
### 洪涝检测
|
||||||
|
|
||||||
|
```text
|
||||||
|
POST /flood/pairs/search
|
||||||
|
POST /flood/detections
|
||||||
|
GET /flood/detections
|
||||||
|
GET /flood/detections/{id}
|
||||||
|
GET /flood/detections/{id}/preview/{layer}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 套合分析
|
||||||
|
|
||||||
|
```text
|
||||||
|
POST /flood/detections/{id}/vectorize
|
||||||
|
POST /flood/detections/{id}/overlay
|
||||||
|
GET /flood/detections/{id}/impact
|
||||||
|
```
|
||||||
|
|
||||||
|
### 结果导出
|
||||||
|
|
||||||
|
```text
|
||||||
|
GET /flood/results
|
||||||
|
GET /flood/results/{id}
|
||||||
|
POST /flood/reports
|
||||||
|
```
|
||||||
|
|
||||||
|
## 8. 前端按钮流
|
||||||
|
|
||||||
|
### 水体提取
|
||||||
|
|
||||||
|
- 查询数据。
|
||||||
|
- 显示覆盖。
|
||||||
|
- 提交水体提取。
|
||||||
|
- 批量提取。
|
||||||
|
- 查看水体结果。
|
||||||
|
|
||||||
|
### 洪涝检测
|
||||||
|
|
||||||
|
- 自动推荐配对。
|
||||||
|
- 选择灾前/灾后。
|
||||||
|
- 提交检测。
|
||||||
|
- MRF 精化开关。
|
||||||
|
- 加载图层。
|
||||||
|
- 去套合分析。
|
||||||
|
|
||||||
|
### 套合分析
|
||||||
|
|
||||||
|
- 运行套合分析。
|
||||||
|
- 加载影响点。
|
||||||
|
- 导出影响清单。
|
||||||
|
- 导出 GeoJSON。
|
||||||
|
|
||||||
|
### 结果与任务
|
||||||
|
|
||||||
|
- 刷新结果。
|
||||||
|
- 加载全部图层。
|
||||||
|
- 打开产品包。
|
||||||
|
- 生成报告。
|
||||||
|
- 导出成果。
|
||||||
|
|
||||||
|
## 9. 分阶段实施
|
||||||
|
|
||||||
|
### Phase 1:前端流水线壳层
|
||||||
|
|
||||||
|
- 合并“水体监测”和“洪涝灾害分析”为一个业务组。
|
||||||
|
- 新工作台只保留水体提取、洪涝检测、套合分析、结果与任务。
|
||||||
|
- 复用旧 `/water/*` 数据展示当前已有能力。
|
||||||
|
- 明确 GF3/Sentinel-1 为待接入,不显示为可精密检测。
|
||||||
|
|
||||||
|
### Phase 2:LT-1 主线打通
|
||||||
|
|
||||||
|
- 把当前 `water_service.py` 中 LT-1 ENVI 链路整理成清晰的水体/洪涝任务。
|
||||||
|
- 输出标准结果。
|
||||||
|
- 完成地图预览和结果列表。
|
||||||
|
|
||||||
|
### Phase 3:套合分析
|
||||||
|
|
||||||
|
- 分类栅格转洪涝矢量。
|
||||||
|
- 接入灾害点套合。
|
||||||
|
- 接入行政区/AOI 统计。
|
||||||
|
- 输出影响清单。
|
||||||
|
|
||||||
|
### Phase 4:多源扩展
|
||||||
|
|
||||||
|
- GF3 ENVI/SARscape 导入适配器或替代检测路线。
|
||||||
|
- Sentinel-1 ENVI/SARscape 导入适配器或替代检测路线。
|
||||||
|
- 多源配对策略和质量提示。
|
||||||
|
|
||||||
|
## 10. 设计原则
|
||||||
|
|
||||||
|
- 页面按业务问题组织,不按算法步骤堆控件。
|
||||||
|
- 数据已入库不代表可检测,必须明确可用性状态。
|
||||||
|
- 地图只保留一套,避免图层和状态重复。
|
||||||
|
- 旧 `water` 模块保留迁移期入口,但新功能进入 `FloodAnalysisWorkspace`。
|
||||||
|
- 套合分析是核心能力,不是结果页上的附属按钮。
|
||||||
@@ -23,7 +23,7 @@ The current first-level menu groups are:
|
|||||||
- `production_management`: 生产管理
|
- `production_management`: 生产管理
|
||||||
- `insar_analysis`: InSAR形变分析
|
- `insar_analysis`: InSAR形变分析
|
||||||
- `ai_analysis`: AI分析
|
- `ai_analysis`: AI分析
|
||||||
- `water`: 水体监测
|
- `flood_analysis`: 洪涝灾害分析
|
||||||
- `ops`: 运行维护
|
- `ops`: 运行维护
|
||||||
|
|
||||||
Definition files:
|
Definition files:
|
||||||
@@ -125,8 +125,8 @@ The following groups do not define second-level sections:
|
|||||||
|
|
||||||
- `data`
|
- `data`
|
||||||
leaf tabs: `ingest`, `data`, `hazard`
|
leaf tabs: `ingest`, `data`, `hazard`
|
||||||
- `water`
|
- `flood_analysis`
|
||||||
leaf tabs: `water`
|
leaf tabs: `flood_analysis`
|
||||||
- `ops`
|
- `ops`
|
||||||
leaf tabs: `health`, `users`, `audit`
|
leaf tabs: `health`, `users`, `audit`
|
||||||
|
|
||||||
@@ -197,6 +197,7 @@ These aliases exist for compatibility, but they are not first-class left-nav ent
|
|||||||
|
|
||||||
Recommended future additions:
|
Recommended future additions:
|
||||||
|
|
||||||
|
- Use `flood_analysis` as the combined first-level group for water extraction, flood detection, overlay analysis, and flood results. The legacy `water` route may remain in code for compatibility, but it is no longer a first-class left-nav entry.
|
||||||
- Put new production execution or product-governance capability under `production_management` as an internal workspace view unless a separate first-level domain is clearly required.
|
- Put new production execution or product-governance capability under `production_management` as an internal workspace view unless a separate first-level domain is clearly required.
|
||||||
- Put planning, batching, pairing, and dispatch preparation capability under `production_planning`.
|
- Put planning, batching, pairing, and dispatch preparation capability under `production_planning`.
|
||||||
- Put result browsing and analyst-facing deformation interpretation under `insar_analysis`.
|
- Put result browsing and analyst-facing deformation interpretation under `insar_analysis`.
|
||||||
|
|||||||
@@ -36,6 +36,9 @@
|
|||||||
- [PRODUCTION_RESULTS_MULTI_ENGINE_DESIGN_20260423.md](PRODUCTION_RESULTS_MULTI_ENGINE_DESIGN_20260423.md)
|
- [PRODUCTION_RESULTS_MULTI_ENGINE_DESIGN_20260423.md](PRODUCTION_RESULTS_MULTI_ENGINE_DESIGN_20260423.md)
|
||||||
多引擎结果目录、发布结构和 catalog 治理设计。
|
多引擎结果目录、发布结构和 catalog 治理设计。
|
||||||
|
|
||||||
|
- [FLOOD_DISASTER_ANALYSIS_SYSTEM_DESIGN_20260514.md](FLOOD_DISASTER_ANALYSIS_SYSTEM_DESIGN_20260514.md)
|
||||||
|
洪涝灾害分析独立系统设计,定义多源 SAR 数据、ENVI/SARscape 洪涝流程、标准产品包和矢量套合分析边界。
|
||||||
|
|
||||||
- [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 设计。
|
WSL 共享运行时和 Broker 设计。
|
||||||
|
|
||||||
|
|||||||
@@ -1642,9 +1642,12 @@ function App() {
|
|||||||
onToggleVisibility: setShowHazardPoints,
|
onToggleVisibility: setShowHazardPoints,
|
||||||
onScanComplete: fetchHazardPoints,
|
onScanComplete: fetchHazardPoints,
|
||||||
};
|
};
|
||||||
const waterPanel = {
|
const floodPanel = {
|
||||||
|
onShowSourceSceneOnMap: handleWaterSceneOnMap,
|
||||||
|
onShowReadyProductOnMap: handleWaterSceneOnMap,
|
||||||
onShowOnMap: handleWaterSceneOnMap,
|
onShowOnMap: handleWaterSceneOnMap,
|
||||||
onShowFloodOnMap: handleFloodEventOnMap,
|
onShowFloodOnMap: handleFloodEventOnMap,
|
||||||
|
onShowFloodRunOnMap: handleFloodEventOnMap,
|
||||||
onToggleFloodLayer: toggleFloodEventLayer,
|
onToggleFloodLayer: toggleFloodEventLayer,
|
||||||
};
|
};
|
||||||
const dinsarPanel = {
|
const dinsarPanel = {
|
||||||
@@ -1736,7 +1739,7 @@ function App() {
|
|||||||
pairingPanel={pairingPanel}
|
pairingPanel={pairingPanel}
|
||||||
taskPanel={taskPanel}
|
taskPanel={taskPanel}
|
||||||
hazardPanel={hazardPanel}
|
hazardPanel={hazardPanel}
|
||||||
waterPanel={waterPanel}
|
floodPanel={floodPanel}
|
||||||
dinsarPanel={dinsarPanel}
|
dinsarPanel={dinsarPanel}
|
||||||
aiPanel={aiPanel}
|
aiPanel={aiPanel}
|
||||||
pairsPanel={pairsPanel}
|
pairsPanel={pairsPanel}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,91 @@
|
|||||||
|
import apiClient from './client';
|
||||||
|
|
||||||
|
export const getFloodSources = (params = {}) =>
|
||||||
|
apiClient.get('/flood/sources', { params });
|
||||||
|
|
||||||
|
export const refreshFloodSources = () =>
|
||||||
|
apiClient.post('/flood/sources/refresh');
|
||||||
|
|
||||||
|
export const getFloodSourceReadiness = (id) =>
|
||||||
|
apiClient.get(`/flood/sources/${id}/readiness`);
|
||||||
|
|
||||||
|
export const submitFloodPreprocess = (payload) =>
|
||||||
|
apiClient.post('/flood/preprocess', payload);
|
||||||
|
|
||||||
|
export const getFloodScenes = (limit = 20, offset = 0) =>
|
||||||
|
apiClient.get('/flood/scenes', { params: { limit, offset } });
|
||||||
|
|
||||||
|
export const getFloodDoneRadarIds = () =>
|
||||||
|
apiClient.get('/flood/scenes/done-radar-ids').then(r => r.data.ids);
|
||||||
|
|
||||||
|
export const getFloodActiveRadarIds = () =>
|
||||||
|
apiClient.get('/flood/scenes/active-radar-ids').then(r => r.data.ids);
|
||||||
|
|
||||||
|
export const resetFloodScene = (sceneId) =>
|
||||||
|
apiClient.post(`/flood/scenes/${sceneId}/reset`);
|
||||||
|
|
||||||
|
export const submitFloodWaterExtraction = (payload) =>
|
||||||
|
apiClient.post('/flood/water-extractions', payload);
|
||||||
|
|
||||||
|
export const getFloodWaterExtractions = (limit = 20, offset = 0, status = null) =>
|
||||||
|
apiClient.get('/flood/water-extractions', { params: { limit, offset, ...(status ? { status } : {}) } });
|
||||||
|
|
||||||
|
export const getFloodWaterExtractionPreview = (id) =>
|
||||||
|
apiClient.get(`/flood/water-extractions/${id}/preview`).then(r => r.data);
|
||||||
|
|
||||||
|
export const getFloodPreprocessRuns = (params = {}) =>
|
||||||
|
apiClient.get('/flood/preprocess-runs', { params });
|
||||||
|
|
||||||
|
export const getFloodReadyProducts = (params = {}) =>
|
||||||
|
apiClient.get('/flood/ready-products', { params });
|
||||||
|
|
||||||
|
export const getFloodReadyProductPreview = (id) =>
|
||||||
|
apiClient.get(`/flood/ready-products/${id}/preview`).then(r => r.data);
|
||||||
|
|
||||||
|
export const searchFloodPairs = (payload) =>
|
||||||
|
apiClient.post('/flood/pairs/search', payload);
|
||||||
|
|
||||||
|
export const saveFloodPair = (payload) =>
|
||||||
|
apiClient.post('/flood/pairs', payload);
|
||||||
|
|
||||||
|
export const getFloodPairs = (params = {}) =>
|
||||||
|
apiClient.get('/flood/pairs', { params });
|
||||||
|
|
||||||
|
export const deleteFloodPair = (id) =>
|
||||||
|
apiClient.delete(`/flood/pairs/${id}`);
|
||||||
|
|
||||||
|
export const submitFloodDetection = (payload) =>
|
||||||
|
apiClient.post('/flood/detections', payload);
|
||||||
|
|
||||||
|
export const getFloodDetections = (params = {}) =>
|
||||||
|
apiClient.get('/flood/detections', { params });
|
||||||
|
|
||||||
|
export const getFloodDetection = (id) =>
|
||||||
|
apiClient.get(`/flood/detections/${id}`);
|
||||||
|
|
||||||
|
export const getFloodDetectionPreview = (id, layer) =>
|
||||||
|
apiClient.get(`/flood/detections/${id}/preview/${layer}`).then(r => r.data);
|
||||||
|
|
||||||
|
export const vectorizeFloodDetection = (id, payload = {}) =>
|
||||||
|
apiClient.post(`/flood/detections/${id}/vectorize`, payload);
|
||||||
|
|
||||||
|
export const runFloodOverlay = (id, payload = {}) =>
|
||||||
|
apiClient.post(`/flood/detections/${id}/overlay`, payload);
|
||||||
|
|
||||||
|
export const getFloodImpact = (id) =>
|
||||||
|
apiClient.get(`/flood/detections/${id}/impact`).then(r => r.data);
|
||||||
|
|
||||||
|
export const getFloodResults = (params = {}) =>
|
||||||
|
apiClient.get('/flood/results', { params });
|
||||||
|
|
||||||
|
export const getFloodResult = (id) =>
|
||||||
|
apiClient.get(`/flood/results/${id}`);
|
||||||
|
|
||||||
|
export const getFloodResultManifest = (id) =>
|
||||||
|
apiClient.get(`/flood/results/${id}/manifest`).then(r => r.data);
|
||||||
|
|
||||||
|
export const createFloodReport = (payload) =>
|
||||||
|
apiClient.post('/flood/reports', payload);
|
||||||
|
|
||||||
|
export const getFloodReport = (id) =>
|
||||||
|
apiClient.get(`/flood/reports/${id}`).then(r => r.data);
|
||||||
@@ -19,7 +19,7 @@ const LazyDataCopierPanel = lazy(() => import('../../DataCopierPanel'));
|
|||||||
const LazyIDLAutomationPanel = lazy(() => import('../../IDLAutomationPanel'));
|
const LazyIDLAutomationPanel = lazy(() => import('../../IDLAutomationPanel'));
|
||||||
const LazyHazardPointPanel = lazy(() => import('../../HazardPointPanel'));
|
const LazyHazardPointPanel = lazy(() => import('../../HazardPointPanel'));
|
||||||
const LazyHealthCheckPanel = lazy(() => import('../../HealthCheckPanel'));
|
const LazyHealthCheckPanel = lazy(() => import('../../HealthCheckPanel'));
|
||||||
const LazyWaterMonitorPanel = lazy(() => import('../../WaterMonitorPanel'));
|
const LazyFloodAnalysisWorkspace = lazy(() => import('../../FloodAnalysisWorkspace'));
|
||||||
const LazyUserAdminPanel = lazy(() => import('../../UserAdminPanel'));
|
const LazyUserAdminPanel = lazy(() => import('../../UserAdminPanel'));
|
||||||
const LazyAuditLogPanel = lazy(() => import('../../AuditLogPanel'));
|
const LazyAuditLogPanel = lazy(() => import('../../AuditLogPanel'));
|
||||||
const LazyAiQualityPanel = lazy(() => import('../../panels/AiQualityPanel'));
|
const LazyAiQualityPanel = lazy(() => import('../../panels/AiQualityPanel'));
|
||||||
@@ -57,7 +57,7 @@ export default function AppSidePanel({
|
|||||||
pairingPanel,
|
pairingPanel,
|
||||||
taskPanel,
|
taskPanel,
|
||||||
hazardPanel,
|
hazardPanel,
|
||||||
waterPanel,
|
floodPanel,
|
||||||
dinsarPanel,
|
dinsarPanel,
|
||||||
aiPanel,
|
aiPanel,
|
||||||
pairsPanel,
|
pairsPanel,
|
||||||
@@ -327,16 +327,13 @@ export default function AppSidePanel({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{leftPanelTab === 'water' && (
|
{leftPanelTab === 'flood_analysis' && (
|
||||||
<div className="panel-content" style={{ flex: '1 1 auto', padding: 0, overflow: 'auto' }}>
|
<div className="panel-content" style={{ flex: '1 1 auto', padding: 0, overflow: 'auto' }}>
|
||||||
<Suspense fallback={<PanelLoadingBody message="正在加载水体监测面板..." />}>
|
<Suspense fallback={<PanelLoadingBody message="正在加载洪涝灾害分析工作台..." />}>
|
||||||
<LazyWaterMonitorPanel
|
<LazyFloodAnalysisWorkspace
|
||||||
readOnly={isReadOnlyUser}
|
readOnly={isReadOnlyUser}
|
||||||
onShowOnMap={waterPanel.onShowOnMap}
|
|
||||||
onShowFloodOnMap={waterPanel.onShowFloodOnMap}
|
|
||||||
onToggleFloodLayer={waterPanel.onToggleFloodLayer}
|
|
||||||
onTaskStart={taskPanel.onTaskStart}
|
onTaskStart={taskPanel.onTaskStart}
|
||||||
language={language}
|
floodPanel={floodPanel}
|
||||||
/>
|
/>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ export const LEFT_GROUP_LABELS = {
|
|||||||
production_management: '生产管理',
|
production_management: '生产管理',
|
||||||
insar_analysis: 'InSAR形变分析',
|
insar_analysis: 'InSAR形变分析',
|
||||||
ai_analysis: 'AI分析',
|
ai_analysis: 'AI分析',
|
||||||
water: '水体监测',
|
flood_analysis: '洪涝灾害分析',
|
||||||
ops: '运行维护',
|
ops: '运行维护',
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -150,7 +150,7 @@ export const LEFT_GROUP_TABS = {
|
|||||||
production_management: [PRODUCTION_WORKSPACE_TAB],
|
production_management: [PRODUCTION_WORKSPACE_TAB],
|
||||||
insar_analysis: LEFT_GROUP_SECTIONS.insar_analysis.flatMap(section => section.tabs),
|
insar_analysis: LEFT_GROUP_SECTIONS.insar_analysis.flatMap(section => section.tabs),
|
||||||
ai_analysis: LEFT_GROUP_SECTIONS.ai_analysis.flatMap(section => section.tabs),
|
ai_analysis: LEFT_GROUP_SECTIONS.ai_analysis.flatMap(section => section.tabs),
|
||||||
water: ['water'],
|
flood_analysis: ['flood_analysis'],
|
||||||
ops: ['health', 'users', 'audit'],
|
ops: ['health', 'users', 'audit'],
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -188,7 +188,6 @@ export const ADMIN_ONLY_TABS = new Set([
|
|||||||
'copier',
|
'copier',
|
||||||
PRODUCTION_WORKSPACE_TAB,
|
PRODUCTION_WORKSPACE_TAB,
|
||||||
...PRODUCTION_WORKSPACE_LEGACY_TABS,
|
...PRODUCTION_WORKSPACE_LEGACY_TABS,
|
||||||
'water',
|
|
||||||
'users',
|
'users',
|
||||||
'audit',
|
'audit',
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
{ zh: '数据管理', en: 'Data' },
|
{ zh: '数据管理', en: 'Data' },
|
||||||
{ zh: '生产规划', en: 'Production' },
|
{ zh: '生产规划', en: 'Production' },
|
||||||
{ zh: 'D-InSAR 分析', en: 'D-InSAR Analysis' },
|
{ zh: 'D-InSAR 分析', en: 'D-InSAR Analysis' },
|
||||||
{ zh: '水体监测', en: 'Water Monitoring' },
|
{ zh: '水体监测(旧)', en: 'Water Monitoring (Legacy)' },
|
||||||
{ zh: '运行维护', en: 'Operations' },
|
{ zh: '运行维护', en: 'Operations' },
|
||||||
|
|
||||||
{ zh: '入库监控', en: 'Ingest Monitor' },
|
{ zh: '入库监控', en: 'Ingest Monitor' },
|
||||||
|
|||||||
@@ -78,7 +78,9 @@ export const getLeftTabLabel = (tabKey, metrics = {}) => {
|
|||||||
case 'uav_image_analysis':
|
case 'uav_image_analysis':
|
||||||
return '无人机影像分析';
|
return '无人机影像分析';
|
||||||
case 'water':
|
case 'water':
|
||||||
return '水体监测';
|
return '水体监测(旧)';
|
||||||
|
case 'flood_analysis':
|
||||||
|
return '洪涝灾害分析';
|
||||||
case 'health':
|
case 'health':
|
||||||
return '运维自检';
|
return '运维自检';
|
||||||
case 'users':
|
case 'users':
|
||||||
|
|||||||
Reference in New Issue
Block a user