fix: 多项稳定性修复与打包优化

- PyInstaller: runtime hook 预加载 osgeo DLL 避免 Qt5 符号冲突;
  spec 纳入 osgeo .py 文件 + 运行时 DLL 依赖 (ffi/lzma/bz2/expat/sqlite3);
  移除 DLL 双副本防止 segfault
- GDAL 环境: 新增 _MEIPASS/osgeo/data/gdal 路径搜索,改善打包后 GDAL_DATA 检测
- 预览生成器: 掩膜与底图同分辨率时跳过 Warp 加速读取
- 专题图: ConvexHull 坐标中心化修复 UTM 大坐标精度退化;
  边界采样兜底防止外扩点为空;
  TIFF 已含 NaN 掩膜时跳过矢量擦除;
  形态学闭运算填充掩膜小孔洞
- NDWI: int16→float32 防止减法溢出
- 面板注册表: 步骤模块分组重构 (模块一/二/三/四重划分)
This commit is contained in:
duxin
2026-07-26 20:35:50 +08:00
parent 0bd54d5fdd
commit b609a64811
7 changed files with 177 additions and 46 deletions

View File

@ -74,16 +74,12 @@ PANEL_REGISTRY = [
},
'constructor_kwargs': None,
},
# ═══════════════════════════════════════════════════════════════
# 模块二 特征工程与数据
# ═══════════════════════════════════════════════════════════════
{
'step_id': 'step4_sampling',
'class_ref': Step4SamplingPanel,
'title': '采样点布设',
'icon': '4.png',
'stage': '模块二 特征工程与数据',
'stage': '模块一 影像预处理',
'display_name': '4. 采样点布设',
# 架构解耦2026-06-22dict 键名 = 下游目标控件真实属性名;
# 第三元素 source_attr = 上游源控件真实属性名panel_factory 回放端使用)
@ -95,12 +91,15 @@ PANEL_REGISTRY = [
},
'constructor_kwargs': None,
},
# ═══════════════════════════════════════════════════════════════
# 模块二 光谱建模与反演
# ═══════════════════════════════════════════════════════════════
{
'step_id': 'step5_clean',
'class_ref': Step5CleanPanel,
'title': '数据清洗',
'icon': '5.png',
'stage': '模块二 特征工程与数据',
'stage': '模块二 光谱建模与反演',
'display_name': '5. 数据清洗',
# 业务要求保持输入源独立,不自动抓取 step4_sampling 的输出
'dependencies': None,
@ -111,7 +110,7 @@ PANEL_REGISTRY = [
'class_ref': Step6FeaturePanel,
'title': '光谱特征',
'icon': '6.png',
'stage': '模块二 特征工程与数据',
'stage': '模块二 光谱建模与反演',
'display_name': '6. 光谱特征提取',
# 架构解耦2026-06-22dict 键名 = 下游目标控件真实属性名
'dependencies': {
@ -131,7 +130,7 @@ PANEL_REGISTRY = [
'class_ref': Step7InversionPanel,
'title': '水质光谱指数计算',
'icon': '7.png',
'stage': '模块二 特征工程与数据',
'stage': '模块二 光谱建模与反演',
'display_name': '7. 水质指数计算',
# 架构解耦2026-06-22dict 键名 = 下游目标控件真实属性名;
# 第三元素 source_attr = 上游源控件真实属性名
@ -142,15 +141,12 @@ PANEL_REGISTRY = [
'constructor_kwargs': None,
},
# ═══════════════════════════════════════════════════════════════
# 模块三 模型训练与反演
# ═══════════════════════════════════════════════════════════════
{
'step_id': 'step8_ml_train',
'class_ref': Step8MlTrainPanel,
'title': '机器学习建模',
'icon': '8.png',
'stage': '模块三 模型训练与反演',
'stage': '模块二 光谱建模与反演',
'display_name': '8. 机器学习建模',
# 架构解耦2026-06-22dict 键名 = 下游目标控件真实属性名
'dependencies': {
@ -160,15 +156,12 @@ PANEL_REGISTRY = [
'constructor_kwargs': None,
},
# ═══════════════════════════════════════════════════════════════
# 模块三 模型训练与反演(续)→ 模块四 制图与成果汇编
# ═══════════════════════════════════════════════════════════════
{
'step_id': 'step9_ml_predict',
'class_ref': Step9MlPredictPanel,
'title': '机器学习预测',
'icon': '9.png',
'stage': '模块三 模型训练与反演',
'stage': '模块二 光谱建模与反演',
'display_name': '9. 机器学习预测',
# 架构解耦2026-06-22dict 键名 = 下游目标控件真实属性名;
# 第三元素 source_attr = 上游源控件真实属性名
@ -178,12 +171,16 @@ PANEL_REGISTRY = [
},
'constructor_kwargs': None,
},
# ═══════════════════════════════════════════════════════════════
# 模块三 物理公式反演
# ═══════════════════════════════════════════════════════════════
{
'step_id': 'step10_watercolor',
'class_ref': Step10WatercolorPanel,
'title': '水色指数反演',
'icon': '10.png',
'stage': '模块三 模型训练与反演',
'stage': '模块三 物理公式反演',
'display_name': '10. 水色指数反演',
# 架构解耦2026-06-22dict 键名 = 下游目标控件真实属性名;
# 第三元素 source_attr = 上游源控件真实属性名
@ -194,12 +191,16 @@ PANEL_REGISTRY = [
},
'constructor_kwargs': None,
},
# ═══════════════════════════════════════════════════════════════
# 模块四 制图与成果输出
# ═══════════════════════════════════════════════════════════════
{
'step_id': 'step11_map',
'class_ref': Step11MapPanel,
'title': '专题图生成',
'icon': '11.png',
'stage': '模块四 制图与成果汇编',
'stage': '模块四 制图与成果输出',
'display_name': '11. 分布图生成',
# 架构解耦2026-06-22dict 键名 = 下游目标控件真实属性名
'dependencies': {
@ -217,7 +218,7 @@ PANEL_REGISTRY = [
'class_ref': Step12VizPanel,
'title': '可视化',
'icon': '9.png',
'stage': '模块四 制图与成果汇编',
'stage': '模块四 制图与成果输出',
'display_name': '12. 可视化展示',
'dependencies': None,
'constructor_kwargs': None,
@ -227,7 +228,7 @@ PANEL_REGISTRY = [
'class_ref': Step13ReportPanel,
'title': '报告生成',
'icon': '13.png',
'stage': '模块四 制图与成果汇编',
'stage': '模块四 制图与成果输出',
'display_name': '13. 分析报告生成',
'dependencies': None,
'constructor_kwargs': {'main_window'}, # 需要注入 main_window=self

View File

@ -78,9 +78,12 @@ def _find_and_set_gdal_env():
return None
def _find_gdal_in_prefix(prefix):
"""在指定前缀下查找 Library/share/gdal"""
"""在指定前缀下查找 GDAL data 目录(Library/share/gdal 或 osgeo/data/gdal"""
# Conda 路径Library/share/gdal
# pip wheel / PyInstaller 打包后osgeo/data/gdal
for sub in ("Library/share/gdal", "share/gdal",
"Library\\share\\gdal", "share\\gdal"):
"Library\\share\\gdal", "share\\gdal",
"osgeo/data/gdal", "osgeo\\data\\gdal"):
path = os.path.join(prefix, sub)
if os.path.isdir(path):
for marker in ("gdalvrt.xsd", "pcs.csv", "gcs.csv"):
@ -110,6 +113,12 @@ def _find_and_set_gdal_env():
except Exception:
pass
# 2b) PyInstaller 打包后的解包目录 (_MEIPASS)
# sys.prefix 在 PyInstaller 中已指向 _MEIPASS但显式加入确保不被遗漏
_meipass = getattr(sys, '_MEIPASS', None)
if _meipass and _meipass not in prefixes:
prefixes.insert(0, _meipass)
# 3) 从 sys.path 中 site-packages 位置回推所有可能的 conda/env 前缀
try:
for sp in sys.path:
@ -236,10 +245,10 @@ def _find_and_set_gdal_env():
# 不设置的话 Fiona 会使用自带的旧版 proj.db (VERSION.MINOR=2)
# 导致投影解析错误 → 矢量图层错位 → 水体被错误擦除 → 有效像元为 0
os.environ["PROJ_DATA"] = proj_lib
print(f"[ENV] PROJ_LIB {proj_lib}")
print(f"[ENV] PROJ_DATA {proj_lib}")
print(f"[ENV] PROJ_LIB -> {proj_lib}")
print(f"[ENV] PROJ_DATA -> {proj_lib}")
else:
print("[ENV] 未找到兼容的 proj.db (需要 PROJ v6+)PROJ_LIB/PROJ_DATA 保持系统默认")
print("[ENV] [WARN] 未找到兼容的 proj.db (需要 PROJ v6+)PROJ_LIB/PROJ_DATA 保持系统默认")
print(f"[ENV] (已搜索 {len(prefixes)} 个前缀: {[os.path.basename(p) for p in prefixes[:5]]}...)")
print("[ENV] 如果 step11 地图渲染报 PROJ 版本错误,")
print("[ENV] 请手动设置: set PROJ_LIB=<conda环境>\\Library\\share\\proj")
@ -247,9 +256,9 @@ def _find_and_set_gdal_env():
if gdal_data:
os.environ["GDAL_DATA"] = gdal_data
print(f"[ENV] GDAL_DATA {gdal_data}")
print(f"[ENV] GDAL_DATA -> {gdal_data}")
else:
print("[ENV] 未找到 GDAL data 目录GDAL_DATA 保持系统默认")
print("[ENV] [WARN] 未找到 GDAL data 目录GDAL_DATA 保持系统默认")
print(f"[ENV] (已搜索 {len(prefixes)} 个前缀)")
# ── 屏蔽 Fiona 内部旧版 PROJ 路径 ──
@ -260,7 +269,7 @@ def _find_and_set_gdal_env():
if "PROJ_DATA" in os.environ:
print("[ENV] 已屏蔽 Fiona 内部旧版 PROJ 数据库,全进程统一使用上述 proj.db")
if _fiona_v2_path:
print(f"[ENV] 检测到 Fiona 自带的旧版 proj.db (v2) 位于:")
print(f"[ENV] [WARN] 检测到 Fiona 自带的旧版 proj.db (v2) 位于:")
print(f" {_fiona_v2_path}")
print(f" PROJ_DATA 已指向 v6+ 版本,此旧版将被忽略")
if proj_lib: