Compare commits
4 Commits
f496b28c8c
...
303f202547
| Author | SHA1 | Date | |
|---|---|---|---|
| 303f202547 | |||
| 9fa60e2995 | |||
| 1d051c9fec | |||
| d18da41505 |
@ -146,23 +146,35 @@ def validate_projections(proj_a: Optional[str],
|
|||||||
proj_b: Optional[str],
|
proj_b: Optional[str],
|
||||||
label_a: str = "栅格A",
|
label_a: str = "栅格A",
|
||||||
label_b: str = "栅格B") -> None:
|
label_b: str = "栅格B") -> None:
|
||||||
"""验证两个投影坐标系 (WKT) 一致
|
"""验证两个投影坐标系语义一致(非字符串比较)
|
||||||
|
|
||||||
|
使用 GDAL SpatialReference.IsSame() 做语义比对。
|
||||||
|
同一坐标系但 WKT 格式不同(不同软件生成)不会误报。
|
||||||
|
|
||||||
空字符串视为"无投影",不验证。
|
空字符串视为"无投影",不验证。
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
SpatialAlignmentError: 投影不一致时
|
SpatialAlignmentError: 投影语义不一致时
|
||||||
"""
|
"""
|
||||||
if not proj_a or not proj_b:
|
if not proj_a or not proj_b:
|
||||||
return # 至少一方无投影,跳过
|
return # 至少一方无投影,跳过
|
||||||
|
|
||||||
# 标准化比较(去除空白差异)
|
try:
|
||||||
norm_a = " ".join(proj_a.split())
|
from osgeo import osr
|
||||||
norm_b = " ".join(proj_b.split())
|
sr_a = osr.SpatialReference(proj_a)
|
||||||
if norm_a != norm_b:
|
sr_b = osr.SpatialReference(proj_b)
|
||||||
raise SpatialAlignmentError.from_projection_mismatch(
|
if sr_a.IsSame(sr_b):
|
||||||
label_a, label_b, proj_a, proj_b
|
return # 语义相同,OK
|
||||||
)
|
except Exception:
|
||||||
|
# osr 解析失败 → 回退到字符串标准化比较
|
||||||
|
norm_a = " ".join(proj_a.split())
|
||||||
|
norm_b = " ".join(proj_b.split())
|
||||||
|
if norm_a == norm_b:
|
||||||
|
return
|
||||||
|
|
||||||
|
raise SpatialAlignmentError.from_projection_mismatch(
|
||||||
|
label_a, label_b, proj_a, proj_b
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def validate_spatial_alignment(
|
def validate_spatial_alignment(
|
||||||
|
|||||||
@ -7,7 +7,7 @@ Step13 后端计算服务(Word 报告生成)
|
|||||||
|
|
||||||
1. 从 ``config`` 字典读取参数;
|
1. 从 ``config`` 字典读取参数;
|
||||||
2. 调用 ``WaterQualityReportGenerator.generate_report`` 把工作目录
|
2. 调用 ``WaterQualityReportGenerator.generate_report`` 把工作目录
|
||||||
下的可视化结果(14_visualization 等)拼装成 Word 文档;
|
下的可视化结果(12_visualization 等)拼装成 Word 文档;
|
||||||
3. AI 配置(Provider / API Key / Model / Timeout)从环境变量读取,
|
3. AI 配置(Provider / API Key / Model / Timeout)从环境变量读取,
|
||||||
与 ``ReportGenerationConfig`` 默认行为完全一致;调用方可在 dispatch
|
与 ``ReportGenerationConfig`` 默认行为完全一致;调用方可在 dispatch
|
||||||
前将 QSettings 内容写入环境变量(如 ``AI_PROVIDER`` / ``MINIMAX_API_KEY``);
|
前将 QSettings 内容写入环境变量(如 ``AI_PROVIDER`` / ``MINIMAX_API_KEY``);
|
||||||
@ -17,7 +17,7 @@ Step13 后端计算服务(Word 报告生成)
|
|||||||
|
|
||||||
execute_step13({
|
execute_step13({
|
||||||
"work_dir": "D:/workspace", # 工作目录(必填)
|
"work_dir": "D:/workspace", # 工作目录(必填)
|
||||||
"output_dir": "D:/workspace/14_visualization", # 输出目录(可省 → work_dir/14_visualization)
|
"output_dir": "D:/workspace/12_visualization", # 输出目录(可省 → work_dir/12_visualization)
|
||||||
"report_title": "水质参数反演分析报告", # 报告标题
|
"report_title": "水质参数反演分析报告", # 报告标题
|
||||||
"enable_ai_analysis": True, # 是否启用 AI 解读
|
"enable_ai_analysis": True, # 是否启用 AI 解读
|
||||||
# --- AI 配置(可省;缺省从环境变量 AI_PROVIDER / MINIMAX_API_KEY 等读) ---
|
# --- AI 配置(可省;缺省从环境变量 AI_PROVIDER / MINIMAX_API_KEY 等读) ---
|
||||||
@ -60,7 +60,7 @@ def _resolve_output_dir(config: Dict[str, Any], work_dir: str) -> tuple[Path, st
|
|||||||
"""根据 output_dir / work_dir 计算 Word 报告输出目录
|
"""根据 output_dir / work_dir 计算 Word 报告输出目录
|
||||||
|
|
||||||
使用共享解析器强制执行"用户优先"规则——用户指定 output_dir 时直接用其值
|
使用共享解析器强制执行"用户优先"规则——用户指定 output_dir 时直接用其值
|
||||||
(step13 的 output_dir 本身就是一个目录),否则用 work_dir/14_visualization 默认。
|
(step13 的 output_dir 本身就是一个目录),否则用 work_dir/12_visualization 默认。
|
||||||
|
|
||||||
注意:step13 与其他步骤不同——output_dir 直接表示目录而非文件路径,
|
注意:step13 与其他步骤不同——output_dir 直接表示目录而非文件路径,
|
||||||
所以使用 Path(user_path) 而非 .parent。
|
所以使用 Path(user_path) 而非 .parent。
|
||||||
@ -68,7 +68,7 @@ def _resolve_output_dir(config: Dict[str, Any], work_dir: str) -> tuple[Path, st
|
|||||||
user_path = get_user_output_path(config, "output_dir", "output_path")
|
user_path = get_user_output_path(config, "output_dir", "output_path")
|
||||||
if user_path:
|
if user_path:
|
||||||
return Path(user_path), "user"
|
return Path(user_path), "user"
|
||||||
return Path(work_dir) / "14_visualization", "default"
|
return Path(work_dir) / "12_visualization", "default"
|
||||||
|
|
||||||
|
|
||||||
def _apply_ai_env(config: Dict[str, Any]) -> None:
|
def _apply_ai_env(config: Dict[str, Any]) -> None:
|
||||||
@ -153,7 +153,7 @@ def execute_step13(config: Dict[str, Any]) -> Dict[str, Any]:
|
|||||||
"mode": mode,
|
"mode": mode,
|
||||||
}
|
}
|
||||||
|
|
||||||
vis_dir = Path(work_dir) / "14_visualization"
|
vis_dir = Path(work_dir) / "12_visualization"
|
||||||
if not vis_dir.is_dir():
|
if not vis_dir.is_dir():
|
||||||
return {
|
return {
|
||||||
"status": "error",
|
"status": "error",
|
||||||
|
|||||||
@ -900,15 +900,17 @@ class ContentMapper:
|
|||||||
print(f" ⚠ 未识别到标准坐标列名,按位置回退: "
|
print(f" ⚠ 未识别到标准坐标列名,按位置回退: "
|
||||||
f"X={lon_col}, Y={lat_col}")
|
f"X={lon_col}, Y={lat_col}")
|
||||||
|
|
||||||
# 含量列:跳过坐标列后的第一列
|
# 动态识别含量列(跳过已知的坐标列和特殊列)
|
||||||
coord_cols = {lon_col, lat_col}
|
_exclude_cols = {'proj_x', 'proj_y', 'longitude', 'latitude',
|
||||||
|
'x', 'y', 'lon', 'lat', 'geometry', 'uncertainty',
|
||||||
|
'x_coord', 'y_coord', 'pixel_x', 'pixel_y'}
|
||||||
content_col = None
|
content_col = None
|
||||||
for c in df.columns:
|
for col in df.columns:
|
||||||
if c not in coord_cols and not c.startswith('pixel_'):
|
if col.lower() not in _exclude_cols and not col.startswith('pixel_'):
|
||||||
content_col = c
|
content_col = col
|
||||||
break
|
break
|
||||||
if content_col is None:
|
if content_col is None:
|
||||||
content_col = df.columns[2]
|
content_col = df.columns[-1]
|
||||||
|
|
||||||
print(f"检测到列名:X({lon_col}),Y({lat_col}),含量({content_col})")
|
print(f"检测到列名:X({lon_col}),Y({lat_col}),含量({content_col})")
|
||||||
|
|
||||||
|
|||||||
@ -268,6 +268,38 @@ class WaterQualityReportGenerator:
|
|||||||
if cfg.enable_ai_analysis is not None:
|
if cfg.enable_ai_analysis is not None:
|
||||||
self.enable_ai_analysis = bool(cfg.enable_ai_analysis)
|
self.enable_ai_analysis = bool(cfg.enable_ai_analysis)
|
||||||
|
|
||||||
|
def _detect_pipeline_mode(self, vis_dir, parameters):
|
||||||
|
"""检测管线模式:ML 还是水色指数公式
|
||||||
|
|
||||||
|
ML 模式标志:vis_dir 中存在 {param}_scatter_with_confidence.png。
|
||||||
|
若不存在 → 水色指数模式 (steps 10,11,12 无 ML)。
|
||||||
|
"""
|
||||||
|
for p in parameters[:3]: # 检查前 3 个参数即可
|
||||||
|
if (vis_dir / f"{p}_scatter_with_confidence.png").exists():
|
||||||
|
return "ml"
|
||||||
|
return "water_index"
|
||||||
|
|
||||||
|
def _get_available_image_types(self, vis_dir, parameters):
|
||||||
|
"""扫描 vis_dir 中实际存在的图片类型,返回类型名列表"""
|
||||||
|
_all_types = ["histogram", "spectrum_comparison", "scatter_with_confidence",
|
||||||
|
"boxplot", "distribution_rendered"]
|
||||||
|
available = set()
|
||||||
|
for p in parameters[:3]:
|
||||||
|
for t in _all_types:
|
||||||
|
fname = f"{p}_{t}.png"
|
||||||
|
if (vis_dir / fname).exists():
|
||||||
|
available.add(t)
|
||||||
|
# 也检查子目录
|
||||||
|
if not available or t not in available:
|
||||||
|
for sub in ("boxplots", "scatter_plots", "distribution_maps"):
|
||||||
|
if (vis_dir / sub / fname).exists():
|
||||||
|
available.add(t)
|
||||||
|
break
|
||||||
|
# 保证至少有 distribution_rendered(来自 11_Thematic_Map)
|
||||||
|
if "distribution_rendered" not in available:
|
||||||
|
available.add("distribution_rendered")
|
||||||
|
return sorted(available)
|
||||||
|
|
||||||
def _style_heading(self, heading, level: int):
|
def _style_heading(self, heading, level: int):
|
||||||
"""统一一级/二级/三级标题字体(黑体)与字号。"""
|
"""统一一级/二级/三级标题字体(黑体)与字号。"""
|
||||||
size_map = {1: Pt(16), 2: Pt(14), 3: Pt(12)}
|
size_map = {1: Pt(16), 2: Pt(14), 3: Pt(12)}
|
||||||
@ -760,6 +792,18 @@ class WaterQualityReportGenerator:
|
|||||||
if not vis_dir.exists():
|
if not vis_dir.exists():
|
||||||
raise FileNotFoundError(f"可视化目录不存在: {vis_dir}")
|
raise FileNotFoundError(f"可视化目录不存在: {vis_dir}")
|
||||||
|
|
||||||
|
# ── 管线模式自动检测 ──
|
||||||
|
self._pipeline_mode = self._detect_pipeline_mode(vis_dir, parameters)
|
||||||
|
print(f"[报告] 管线模式: {self._pipeline_mode}")
|
||||||
|
if self._pipeline_mode == "water_index":
|
||||||
|
# 水色指数模式:只保留实际存在的图片类型
|
||||||
|
_available_types = self._get_available_image_types(vis_dir, parameters)
|
||||||
|
self.parameter_images = {
|
||||||
|
p: [f"{p}_{t}.png" for t in _available_types]
|
||||||
|
for p in parameters
|
||||||
|
}
|
||||||
|
print(f"[报告] 水色指数可用图片类型: {_available_types}")
|
||||||
|
|
||||||
if output_path is None:
|
if output_path is None:
|
||||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||||
output_path = self.output_dir / f"水质参数反演分析报告_{timestamp}.docx"
|
output_path = self.output_dir / f"水质参数反演分析报告_{timestamp}.docx"
|
||||||
@ -1613,65 +1657,113 @@ class WaterQualityReportGenerator:
|
|||||||
h2 = doc.add_heading("4.1 水质参数统计分析", level=2)
|
h2 = doc.add_heading("4.1 水质参数统计分析", level=2)
|
||||||
self._style_heading(h2, level=2)
|
self._style_heading(h2, level=2)
|
||||||
|
|
||||||
# 从工作目录的4_processed_data文件夹查找CSV文件
|
# 从工作目录查找 CSV 统计数据文件(根据管线模式选择不同目录)
|
||||||
work_dir_path = vis_dir.parent
|
work_dir_path = vis_dir.parent
|
||||||
processed_data_dir = work_dir_path / "5_Data_Cleaning"
|
if getattr(self, '_pipeline_mode', 'ml') == 'water_index':
|
||||||
|
stats_dir = work_dir_path / "10_WaterIndex_CSV"
|
||||||
|
else:
|
||||||
|
stats_dir = work_dir_path / "5_Data_Cleaning"
|
||||||
|
|
||||||
if not processed_data_dir.exists():
|
if not stats_dir.exists():
|
||||||
doc.add_paragraph(f"未找到数据处理目录: {processed_data_dir}")
|
alt_name = "10_WaterIndex_CSV" if self._pipeline_mode == 'water_index' else "5_Data_Cleaning"
|
||||||
|
doc.add_paragraph(f"未找到数据目录: {stats_dir}")
|
||||||
doc.add_page_break()
|
doc.add_page_break()
|
||||||
return start_figure_num
|
return start_figure_num
|
||||||
|
|
||||||
csv_files = list(processed_data_dir.glob("*.csv"))
|
csv_files = list(stats_dir.glob("*.csv"))
|
||||||
if not csv_files:
|
if not csv_files:
|
||||||
doc.add_paragraph(f"在 {processed_data_dir} 目录下未找到CSV统计数据文件。")
|
doc.add_paragraph(f"在 {stats_dir} 目录下未找到CSV统计数据文件。")
|
||||||
doc.add_page_break()
|
doc.add_page_break()
|
||||||
return start_figure_num
|
return start_figure_num
|
||||||
|
|
||||||
csv_path = csv_files[0] # 使用找到的第一个CSV文件
|
csv_path = csv_files[0] # 使用找到的第一个CSV文件
|
||||||
|
|
||||||
try:
|
try:
|
||||||
df_full = pd.read_csv(csv_path, sep=',')
|
if getattr(self, '_pipeline_mode', 'ml') == 'water_index':
|
||||||
df = df_full.iloc[:, 2:] # 跳过前两列(纬度、经度),直接用列号
|
# 水色指数模式:遍历所有 CSV,每个公式一行统计
|
||||||
|
stats_rows = []
|
||||||
|
for cp in sorted(csv_files):
|
||||||
|
try:
|
||||||
|
df_one = pd.read_csv(cp, sep=',')
|
||||||
|
# 水色指数 CSV: proj_x, proj_y, longitude, latitude, value
|
||||||
|
# 取最后一列作为参数值
|
||||||
|
val_col = df_one.columns[-1]
|
||||||
|
vals = pd.to_numeric(df_one[val_col], errors='coerce').dropna()
|
||||||
|
if len(vals) > 0:
|
||||||
|
stats_rows.append({
|
||||||
|
'参数': Path(cp).stem,
|
||||||
|
'数量': len(vals),
|
||||||
|
'最小值': round(float(vals.min()), 4),
|
||||||
|
'最大值': round(float(vals.max()), 4),
|
||||||
|
'平均值': round(float(vals.mean()), 4),
|
||||||
|
'标准差': round(float(vals.std(ddof=0)), 4) if len(vals) > 1 else 0,
|
||||||
|
})
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
if stats_rows:
|
||||||
|
df = pd.DataFrame(stats_rows)
|
||||||
|
else:
|
||||||
|
doc.add_paragraph("水色指数 CSV 文件无法解析。")
|
||||||
|
doc.add_page_break()
|
||||||
|
return start_figure_num
|
||||||
|
else:
|
||||||
|
df_full = pd.read_csv(csv_path, sep=',')
|
||||||
|
df = df_full.iloc[:, 2:] # 跳过前两列(纬度、经度),直接用列号
|
||||||
|
|
||||||
# 自动统计剩余列
|
if getattr(self, '_pipeline_mode', 'ml') == 'water_index':
|
||||||
stats_data = []
|
# 水色指数模式:df 已是统计汇总表,直接写出
|
||||||
for i in range(df.shape[1]):
|
if not stats_rows:
|
||||||
col = df.columns[i]
|
raise ValueError("无水色指数统计数据")
|
||||||
clean_col = str(col).strip()
|
|
||||||
try:
|
|
||||||
data = df.iloc[:, i].dropna()
|
|
||||||
if len(data) > 0:
|
|
||||||
stats_data.append({
|
|
||||||
'参数': clean_col,
|
|
||||||
'点位数': len(data),
|
|
||||||
'最大值': f"{data.max():.4f}",
|
|
||||||
'最小值': f"{data.min():.4f}",
|
|
||||||
'平均值': f"{data.mean():.4f}",
|
|
||||||
'标准差': f"{data.std():.4f}"
|
|
||||||
})
|
|
||||||
except Exception as e:
|
|
||||||
print(f"跳过列 {col}: {e}")
|
|
||||||
|
|
||||||
if stats_data:
|
|
||||||
# 创建统计表格
|
|
||||||
table = doc.add_table(rows=1, cols=6, style='Table Grid')
|
table = doc.add_table(rows=1, cols=6, style='Table Grid')
|
||||||
hdr_cells = table.rows[0].cells
|
hdr_cells = table.rows[0].cells
|
||||||
hdr_cells[0].text = '参数'
|
for j, h in enumerate(['参数', '点位数', '最小值', '最大值', '平均值', '标准差']):
|
||||||
hdr_cells[1].text = '点位数'
|
hdr_cells[j].text = h
|
||||||
hdr_cells[2].text = '最大值'
|
header_map = {'参数': '参数', '数量': '点位数', '最小值': '最小值',
|
||||||
hdr_cells[3].text = '最小值'
|
'最大值': '最大值', '平均值': '平均值', '标准差': '标准差'}
|
||||||
hdr_cells[4].text = '平均值'
|
for row_dict in stats_rows:
|
||||||
hdr_cells[5].text = '标准差'
|
|
||||||
|
|
||||||
for stat in stats_data:
|
|
||||||
row_cells = table.add_row().cells
|
row_cells = table.add_row().cells
|
||||||
row_cells[0].text = stat['参数']
|
for j, (col_name, hdr_name) in enumerate(header_map.items()):
|
||||||
row_cells[1].text = str(stat['点位数'])
|
row_cells[j].text = str(row_dict.get(col_name, ''))
|
||||||
row_cells[2].text = stat['最大值']
|
stats_data = [{'参数': r['参数'], '点位数': r['数量']} for r in stats_rows]
|
||||||
row_cells[3].text = stat['最小值']
|
else:
|
||||||
row_cells[4].text = stat['平均值']
|
# ML 模式:逐列统计
|
||||||
row_cells[5].text = stat['标准差']
|
stats_data = []
|
||||||
|
for i in range(df.shape[1]):
|
||||||
|
col = df.columns[i]
|
||||||
|
clean_col = str(col).strip()
|
||||||
|
try:
|
||||||
|
data = df.iloc[:, i].dropna()
|
||||||
|
if len(data) > 0:
|
||||||
|
stats_data.append({
|
||||||
|
'参数': clean_col,
|
||||||
|
'点位数': len(data),
|
||||||
|
'最大值': f"{data.max():.4f}",
|
||||||
|
'最小值': f"{data.min():.4f}",
|
||||||
|
'平均值': f"{data.mean():.4f}",
|
||||||
|
'标准差': f"{data.std():.4f}"
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
print(f"跳过列 {col}: {e}")
|
||||||
|
|
||||||
|
if stats_data:
|
||||||
|
if getattr(self, '_pipeline_mode', 'ml') != 'water_index':
|
||||||
|
# ML 模式创建表格(水色指数模式表格已在上方创建)
|
||||||
|
table = doc.add_table(rows=1, cols=6, style='Table Grid')
|
||||||
|
hdr_cells = table.rows[0].cells
|
||||||
|
hdr_cells[0].text = '参数'
|
||||||
|
hdr_cells[1].text = '点位数'
|
||||||
|
hdr_cells[2].text = '最大值'
|
||||||
|
hdr_cells[3].text = '最小值'
|
||||||
|
hdr_cells[4].text = '平均值'
|
||||||
|
hdr_cells[5].text = '标准差'
|
||||||
|
for stat in stats_data:
|
||||||
|
row_cells = table.add_row().cells
|
||||||
|
row_cells[0].text = stat['参数']
|
||||||
|
row_cells[1].text = str(stat['点位数'])
|
||||||
|
row_cells[2].text = stat['最大值']
|
||||||
|
row_cells[3].text = stat['最小值']
|
||||||
|
row_cells[4].text = stat['平均值']
|
||||||
|
row_cells[5].text = stat['标准差']
|
||||||
|
|
||||||
else:
|
else:
|
||||||
doc.add_paragraph("CSV文件中未找到有效的参数数据。")
|
doc.add_paragraph("CSV文件中未找到有效的参数数据。")
|
||||||
@ -1687,39 +1779,41 @@ class WaterQualityReportGenerator:
|
|||||||
|
|
||||||
doc.add_paragraph() # 表格和热力图之间的空行
|
doc.add_paragraph() # 表格和热力图之间的空行
|
||||||
|
|
||||||
# 2. 添加相关性热力图(放在表格下方)
|
# 2. 添加相关性热力图(放在表格下方)—— 仅 ML 模式
|
||||||
h3 = doc.add_heading("4.2 水质参数相关性分析", level=2)
|
if getattr(self, '_pipeline_mode', 'ml') == 'ml':
|
||||||
self._style_heading(h3, level=2)
|
h3 = doc.add_heading("4.2 水质参数相关性分析", level=2)
|
||||||
heatmap_path = vis_dir / "correlation_heatmap.png"
|
self._style_heading(h3, level=2)
|
||||||
figure_num = start_figure_num
|
heatmap_path = vis_dir / "correlation_heatmap.png"
|
||||||
if heatmap_path.exists():
|
figure_num = start_figure_num
|
||||||
try:
|
if heatmap_path.exists():
|
||||||
# 使用统一的图像插入方法
|
try:
|
||||||
caption_text = f"图{figure_num} 水质参数相关性热力图"
|
caption_text = f"图{figure_num} 水质参数相关性热力图"
|
||||||
self._add_image_with_caption(doc, str(heatmap_path), caption_text, width=Inches(6.0))
|
self._add_image_with_caption(doc, str(heatmap_path), caption_text, width=Inches(6.0))
|
||||||
doc.add_paragraph("(颜色越深表示相关性越强,红色为正相关,蓝色为负相关)")
|
doc.add_paragraph("(颜色越深表示相关性越强,红色为正相关,蓝色为负相关)")
|
||||||
|
|
||||||
analysis_text = self._analyze_and_cache_image(
|
analysis_text = self._analyze_and_cache_image(
|
||||||
image_path=heatmap_path,
|
image_path=heatmap_path,
|
||||||
image_type="correlation_heatmap",
|
image_type="correlation_heatmap",
|
||||||
param="综合",
|
param="综合",
|
||||||
figure_num=figure_num,
|
figure_num=figure_num,
|
||||||
)
|
|
||||||
self._add_ai_analysis_paragraph(doc, analysis_text)
|
|
||||||
if all_image_analyses is not None:
|
|
||||||
all_image_analyses.append(
|
|
||||||
{
|
|
||||||
"figure_num": figure_num,
|
|
||||||
"param": "综合",
|
|
||||||
"image_type": "correlation_heatmap",
|
|
||||||
"image_name": heatmap_path.name,
|
|
||||||
"analysis": analysis_text,
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
except Exception as e:
|
self._add_ai_analysis_paragraph(doc, analysis_text)
|
||||||
doc.add_paragraph(f"[相关性热力图插入失败: {e}]")
|
if all_image_analyses is not None:
|
||||||
|
all_image_analyses.append(
|
||||||
|
{
|
||||||
|
"figure_num": figure_num,
|
||||||
|
"param": "综合",
|
||||||
|
"image_type": "correlation_heatmap",
|
||||||
|
"image_name": heatmap_path.name,
|
||||||
|
"analysis": analysis_text,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
doc.add_paragraph(f"[相关性热力图插入失败: {e}]")
|
||||||
|
else:
|
||||||
|
doc.add_paragraph(f"[未找到相关性热力图: {heatmap_path.name}]")
|
||||||
else:
|
else:
|
||||||
doc.add_paragraph(f"[未找到相关性热力图: {heatmap_path.name}]")
|
doc.add_paragraph("(水色指数模式:参数相关性分析仅适用于机器学习预测流程,当前为非 ML 模式,已跳过。)")
|
||||||
|
|
||||||
# 热力图处理结束(无论成功/失败)更新进度条
|
# 热力图处理结束(无论成功/失败)更新进度条
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in New Issue
Block a user