refactor: 中间层消除硬编码波段号+CRS动态探测
- GlintRemovalStep/handler/service 全部改为传递波长参数而非波段索引 - ContentMapper 移除 EPSG:32651 硬编码, 新增 _ensure_crs() + _probe_crs_from_file() - _prepare_shared_context 在 read_csv_data 前先探测边界文件 CRS - GUI QSpinBox→QDoubleSpinBox,默认值改为波长(nm),placeholder 提示自动探测 - 消除 38/36/49/47/25/37/65/91 等所有魔法数字
This commit is contained in:
@ -40,10 +40,15 @@ def _process_one_map(csv_path: str, base_kwargs: dict, output_dir: str
|
|||||||
if shared_context is not None:
|
if shared_context is not None:
|
||||||
# ★ 快速通道:直接调 ContentMapper,复用预计算的网格/掩膜
|
# ★ 快速通道:直接调 ContentMapper,复用预计算的网格/掩膜
|
||||||
from src.postprocessing.map import ContentMapper
|
from src.postprocessing.map import ContentMapper
|
||||||
mapper = ContentMapper(
|
# ★ CRS 动态探测:从边界文件自动获取,不再硬编码 EPSG:32651
|
||||||
input_crs=base_kwargs.get('input_crs', 'EPSG:32651'),
|
input_crs = base_kwargs.get('input_crs') or None
|
||||||
output_crs=base_kwargs.get('output_crs', base_kwargs.get('input_crs', 'EPSG:32651')),
|
output_crs = base_kwargs.get('output_crs') or None
|
||||||
)
|
boundary_path = base_kwargs.get('boundary_shp_path')
|
||||||
|
mapper = ContentMapper(input_crs=input_crs, output_crs=output_crs)
|
||||||
|
if boundary_path:
|
||||||
|
mapper._ensure_crs(reference_file=str(boundary_path))
|
||||||
|
else:
|
||||||
|
mapper._ensure_crs()
|
||||||
result_path = mapper.process_data(
|
result_path = mapper.process_data(
|
||||||
csv_file=csv_path,
|
csv_file=csv_path,
|
||||||
shp_file=base_kwargs.get('boundary_shp_path'),
|
shp_file=base_kwargs.get('boundary_shp_path'),
|
||||||
@ -103,7 +108,8 @@ def _pre_vectorize_boundary(boundary_path: str, work_dir: str) -> str:
|
|||||||
if proj:
|
if proj:
|
||||||
srs.ImportFromWkt(proj)
|
srs.ImportFromWkt(proj)
|
||||||
else:
|
else:
|
||||||
srs.ImportFromEPSG(32651)
|
# 栅格无投影信息时,兜底 WGS84(而非某个特定 UTM 分区)
|
||||||
|
srs.ImportFromEPSG(4326)
|
||||||
|
|
||||||
layer = out_ds.CreateLayer('boundary', srs, ogr.wkbMultiPolygon)
|
layer = out_ds.CreateLayer('boundary', srs, ogr.wkbMultiPolygon)
|
||||||
gdal.Polygonize(band, band, layer, 0, [], callback=None)
|
gdal.Polygonize(band, band, layer, 0, [], callback=None)
|
||||||
@ -163,8 +169,8 @@ class Step11MapHandler(BaseStepHandler):
|
|||||||
base_kwargs = {
|
base_kwargs = {
|
||||||
'boundary_shp_path': resolved_boundary or None,
|
'boundary_shp_path': resolved_boundary or None,
|
||||||
'resolution': float(config.get('resolution', 10.0)),
|
'resolution': float(config.get('resolution', 10.0)),
|
||||||
'input_crs': config.get('input_crs', 'EPSG:32651'),
|
'input_crs': config.get('input_crs') or None,
|
||||||
'output_crs': config.get('output_crs', config.get('input_crs', 'EPSG:32651')),
|
'output_crs': config.get('output_crs') or None,
|
||||||
'show_sample_points': bool(config.get('show_sample_points', False)),
|
'show_sample_points': bool(config.get('show_sample_points', False)),
|
||||||
'use_distance_diffusion': bool(config.get('use_distance_diffusion', False)),
|
'use_distance_diffusion': bool(config.get('use_distance_diffusion', False)),
|
||||||
}
|
}
|
||||||
|
|||||||
@ -71,11 +71,8 @@ class Step12KrigingHandler(BaseStepHandler):
|
|||||||
boundary_shp_path=boundary_shp_path,
|
boundary_shp_path=boundary_shp_path,
|
||||||
output_image_path=output_image_path,
|
output_image_path=output_image_path,
|
||||||
resolution=config.get('resolution', 30),
|
resolution=config.get('resolution', 30),
|
||||||
input_crs=config.get('input_crs', 'EPSG:32651'),
|
input_crs=config.get('input_crs') or None,
|
||||||
# ★★★ 强制 output_crs = input_crs,避免 ContentMapper 把栅格重投影到 EPSG:4326 ★★★
|
output_crs=config.get('output_crs') or None,
|
||||||
# 旧实现:output_crs=config.get('output_crs', 'EPSG:4326')
|
|
||||||
# 重投影会让栅格和基于投影坐标的掩膜在 visualize_raster 叠加时发生仿射变换撕裂
|
|
||||||
output_crs=config.get('input_crs', 'EPSG:32651'),
|
|
||||||
show_sample_points=config.get('show_sample_points', False),
|
show_sample_points=config.get('show_sample_points', False),
|
||||||
base_map_tif=config.get('base_map_tif'),
|
base_map_tif=config.get('base_map_tif'),
|
||||||
use_distance_diffusion=config.get('use_distance_diffusion', True),
|
use_distance_diffusion=config.get('use_distance_diffusion', True),
|
||||||
|
|||||||
@ -45,16 +45,16 @@ class Step3GlintRemovalHandler(BaseStepHandler):
|
|||||||
interpolation_method=config.get('interpolation_method', 'nearest'),
|
interpolation_method=config.get('interpolation_method', 'nearest'),
|
||||||
enabled=config.get('enabled', True),
|
enabled=config.get('enabled', True),
|
||||||
kutser_shp_path=config.get('kutser_shp_path'),
|
kutser_shp_path=config.get('kutser_shp_path'),
|
||||||
oxy_band=config.get('oxy_band', 38),
|
oxy_wavelength=float(config.get('oxy_wavelength', 760.6)),
|
||||||
lower_oxy=config.get('lower_oxy', 36),
|
lower_wavelength=float(config.get('lower_wavelength', 742.39)),
|
||||||
upper_oxy=config.get('upper_oxy', 49),
|
upper_wavelength=float(config.get('upper_wavelength', 860.48)),
|
||||||
nir_band=config.get('nir_band', 47),
|
nir_wavelength=float(config.get('nir_wavelength', 842.36)),
|
||||||
nir_lower=config.get('nir_lower', 25),
|
nir_lower_wavelength=float(config.get('nir_lower_wavelength', 641.93)),
|
||||||
nir_upper=config.get('nir_upper', 37),
|
nir_upper_wavelength=float(config.get('nir_upper_wavelength', 751.49)),
|
||||||
goodman_A=config.get('goodman_A', 0.000019),
|
goodman_A=float(config.get('goodman_A', 0.000019)),
|
||||||
goodman_B=config.get('goodman_B', 0.1),
|
goodman_B=float(config.get('goodman_B', 0.1)),
|
||||||
hedley_shp_path=config.get('hedley_shp_path'),
|
hedley_shp_path=config.get('hedley_shp_path'),
|
||||||
hedley_nir_band=config.get('hedley_nir_band', 47),
|
hedley_nir_wavelength=float(config.get('hedley_nir_wavelength', 842.36)),
|
||||||
sugar_bounds=config.get('sugar_bounds'),
|
sugar_bounds=config.get('sugar_bounds'),
|
||||||
sugar_sigma=config.get('sugar_sigma', 1.0),
|
sugar_sigma=config.get('sugar_sigma', 1.0),
|
||||||
sugar_estimate_background=config.get('sugar_estimate_background', True),
|
sugar_estimate_background=config.get('sugar_estimate_background', True),
|
||||||
|
|||||||
@ -209,20 +209,20 @@ class GlintRemovalStep:
|
|||||||
interpolate_zeros: bool = False,
|
interpolate_zeros: bool = False,
|
||||||
interpolation_method: str = "nearest",
|
interpolation_method: str = "nearest",
|
||||||
enabled: bool = True,
|
enabled: bool = True,
|
||||||
# Kutser 参数
|
# Kutser 参数(波长驱动:nm → 自动解析波段号)
|
||||||
kutser_shp_path: Optional[str] = None,
|
kutser_shp_path: Optional[str] = None,
|
||||||
oxy_band: int = 38,
|
oxy_wavelength: float = 760.6,
|
||||||
lower_oxy: int = 36,
|
lower_wavelength: float = 742.39,
|
||||||
upper_oxy: int = 49,
|
upper_wavelength: float = 860.48,
|
||||||
nir_band: int = 47,
|
nir_wavelength: float = 842.36,
|
||||||
# Goodman 参数
|
# Goodman 参数(波长驱动:统一为 ~640nm / ~750nm 物理波长)
|
||||||
nir_lower: int = 25,
|
nir_lower_wavelength: float = 641.93,
|
||||||
nir_upper: int = 37,
|
nir_upper_wavelength: float = 751.49,
|
||||||
goodman_A: float = 0.000019,
|
goodman_A: float = 0.000019,
|
||||||
goodman_B: float = 0.1,
|
goodman_B: float = 0.1,
|
||||||
# Hedley 参数
|
# Hedley 参数(波长驱动)
|
||||||
hedley_shp_path: Optional[str] = None,
|
hedley_shp_path: Optional[str] = None,
|
||||||
hedley_nir_band: int = 47,
|
hedley_nir_wavelength: float = 842.36,
|
||||||
# SUGAR 参数
|
# SUGAR 参数
|
||||||
sugar_bounds: Optional[List[tuple]] = None,
|
sugar_bounds: Optional[List[tuple]] = None,
|
||||||
sugar_sigma: float = 1.0,
|
sugar_sigma: float = 1.0,
|
||||||
@ -383,10 +383,10 @@ class GlintRemovalStep:
|
|||||||
|
|
||||||
# ==================== Kutser ====================
|
# ==================== Kutser ====================
|
||||||
if method == "kutser":
|
if method == "kutser":
|
||||||
print(f"使用方法: Kutser (氧吸收波段={oxy_band}, NIR波段={nir_band})")
|
print(f"使用方法: Kutser (波长驱动: oxy={oxy_wavelength}nm, "
|
||||||
|
f"NIR={nir_wavelength}nm)")
|
||||||
hardcoded_bsq = str(deglint_dir / "deglint_kutser.bsq")
|
hardcoded_bsq = str(deglint_dir / "deglint_kutser.bsq")
|
||||||
hardcoded_hdr = hardcoded_bsq.replace(".bsq", ".hdr")
|
hardcoded_hdr = hardcoded_bsq.replace(".bsq", ".hdr")
|
||||||
# 将用户指定的 output_path 标准化为 .bsq 路径
|
|
||||||
if output_path:
|
if output_path:
|
||||||
final_bsq = output_path.replace('.dat', '.bsq').replace('.tif', '.bsq')
|
final_bsq = output_path.replace('.dat', '.bsq').replace('.tif', '.bsq')
|
||||||
final_hdr = final_bsq.replace(".bsq", ".hdr")
|
final_hdr = final_bsq.replace(".bsq", ".hdr")
|
||||||
@ -402,10 +402,10 @@ class GlintRemovalStep:
|
|||||||
kutser = Kutser(
|
kutser = Kutser(
|
||||||
img_path,
|
img_path,
|
||||||
shp_path=None,
|
shp_path=None,
|
||||||
oxy_band=oxy_band,
|
oxy_wavelength=oxy_wavelength,
|
||||||
lower_oxy=lower_oxy,
|
lower_wavelength=lower_wavelength,
|
||||||
upper_oxy=upper_oxy,
|
upper_wavelength=upper_wavelength,
|
||||||
NIR_band=nir_band,
|
nir_wavelength=nir_wavelength,
|
||||||
water_mask=mask_for_algorithm,
|
water_mask=mask_for_algorithm,
|
||||||
output_path=hardcoded_bsq,
|
output_path=hardcoded_bsq,
|
||||||
)
|
)
|
||||||
@ -420,7 +420,9 @@ class GlintRemovalStep:
|
|||||||
|
|
||||||
# ==================== Goodman ====================
|
# ==================== Goodman ====================
|
||||||
elif method == "goodman":
|
elif method == "goodman":
|
||||||
print(f"使用方法: Goodman (NIR波段范围: {nir_lower}-{nir_upper})")
|
print(f"使用方法: Goodman (波长驱动: "
|
||||||
|
f"NIR_lower={nir_lower_wavelength}nm, "
|
||||||
|
f"NIR_upper={nir_upper_wavelength}nm)")
|
||||||
hardcoded_bsq = str(deglint_dir / "deglint_goodman.bsq")
|
hardcoded_bsq = str(deglint_dir / "deglint_goodman.bsq")
|
||||||
hardcoded_hdr = hardcoded_bsq.replace(".bsq", ".hdr")
|
hardcoded_hdr = hardcoded_bsq.replace(".bsq", ".hdr")
|
||||||
if output_path:
|
if output_path:
|
||||||
@ -437,8 +439,8 @@ class GlintRemovalStep:
|
|||||||
|
|
||||||
goodman = Goodman(
|
goodman = Goodman(
|
||||||
img_path,
|
img_path,
|
||||||
NIR_lower=nir_lower,
|
nir_lower_wavelength=nir_lower_wavelength,
|
||||||
NIR_upper=nir_upper,
|
nir_upper_wavelength=nir_upper_wavelength,
|
||||||
A=goodman_A,
|
A=goodman_A,
|
||||||
B=goodman_B,
|
B=goodman_B,
|
||||||
water_mask=mask_for_algorithm,
|
water_mask=mask_for_algorithm,
|
||||||
@ -458,7 +460,7 @@ class GlintRemovalStep:
|
|||||||
|
|
||||||
# ==================== Hedley ====================
|
# ==================== Hedley ====================
|
||||||
elif method == "hedley":
|
elif method == "hedley":
|
||||||
print(f"使用方法: Hedley (NIR波段={hedley_nir_band})")
|
print(f"使用方法: Hedley (波长驱动: NIR={hedley_nir_wavelength}nm)")
|
||||||
hardcoded_bsq = str(deglint_dir / "deglint_hedley.bsq")
|
hardcoded_bsq = str(deglint_dir / "deglint_hedley.bsq")
|
||||||
hardcoded_hdr = hardcoded_bsq.replace(".bsq", ".hdr")
|
hardcoded_hdr = hardcoded_bsq.replace(".bsq", ".hdr")
|
||||||
if output_path:
|
if output_path:
|
||||||
@ -476,7 +478,7 @@ class GlintRemovalStep:
|
|||||||
hedley = Hedley(
|
hedley = Hedley(
|
||||||
img_path,
|
img_path,
|
||||||
shp_path=None,
|
shp_path=None,
|
||||||
NIR_band=hedley_nir_band,
|
nir_wavelength=hedley_nir_wavelength,
|
||||||
water_mask=mask_for_algorithm,
|
water_mask=mask_for_algorithm,
|
||||||
output_path=hardcoded_bsq,
|
output_path=hardcoded_bsq,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -19,11 +19,9 @@ class MappingStep:
|
|||||||
boundary_shp_path: Optional[str] = None, # ★★★ Plan C: None = 不依赖水域掩膜 ★★★
|
boundary_shp_path: Optional[str] = None, # ★★★ Plan C: None = 不依赖水域掩膜 ★★★
|
||||||
output_image_path: Optional[str] = None,
|
output_image_path: Optional[str] = None,
|
||||||
resolution: float = 30,
|
resolution: float = 30,
|
||||||
input_crs: str = "EPSG:32651",
|
input_crs: Optional[str] = None,
|
||||||
# ★★★ 强制默认 output_crs = input_crs,禁止重投影到 EPSG:4326 ★★★
|
# ★ CRS 动态探测:ContentMapper 自动从掩膜/栅格中读取投影信息
|
||||||
# 历史默认值 'EPSG:4326' 会让 ContentMapper 将插值栅格从投影坐标系转到经纬度坐标系,
|
output_crs: Optional[str] = None,
|
||||||
# 与基于 EPSG:32651 的水域掩膜叠加时发生仿射变换撕裂(栅格错位、坐标轴扭曲)。
|
|
||||||
output_crs: str = "EPSG:32651",
|
|
||||||
show_sample_points: bool = False,
|
show_sample_points: bool = False,
|
||||||
base_map_tif: Optional[str] = None,
|
base_map_tif: Optional[str] = None,
|
||||||
use_distance_diffusion: bool = True,
|
use_distance_diffusion: bool = True,
|
||||||
|
|||||||
@ -1163,62 +1163,38 @@ class WaterQualityGUI(QMainWindow):
|
|||||||
if max_band <= 0:
|
if max_band <= 0:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# 3) 不同方法对应不同的波段字段(cfg_key, panel_attr, 推荐值, 标签)
|
# 3) 波长驱动改造:波段号由 find_band_number() 从 HDR 动态解析,
|
||||||
|
# 不再需要波段索引越界检查。仅对极端波长值做合理性软警告。
|
||||||
method = step3_cfg.get('method', 'goodman')
|
method = step3_cfg.get('method', 'goodman')
|
||||||
if method == 'goodman':
|
_wavelength_fields = {
|
||||||
band_fields = [
|
'goodman': [
|
||||||
('nir_lower', 'nir_lower', 65, 'NIR下波段'),
|
('nir_lower_wavelength', 641.93, 'NIR下界波长(nm)'),
|
||||||
('nir_upper', 'nir_upper', 91, 'NIR上波段'),
|
('nir_upper_wavelength', 751.49, 'NIR上界波长(nm)'),
|
||||||
]
|
],
|
||||||
elif method == 'kutser':
|
'kutser': [
|
||||||
band_fields = [
|
('oxy_wavelength', 760.60, '氧吸收波长(nm)'),
|
||||||
('oxy_band', 'oxy_band', 38, '氧吸收波段'),
|
('lower_wavelength', 742.39, '下氧吸收波长(nm)'),
|
||||||
('lower_oxy', 'lower_oxy', 36, '下氧吸收波段'),
|
('upper_wavelength', 860.48, '上氧吸收波长(nm)'),
|
||||||
('upper_oxy', 'upper_oxy', 49, '上氧吸收波段'),
|
('nir_wavelength', 842.36, 'NIR波长(nm)'),
|
||||||
('nir_band', 'nir_band', 47, 'NIR波段'),
|
],
|
||||||
]
|
'hedley': [
|
||||||
elif method == 'hedley':
|
('hedley_nir_wavelength', 842.36, 'NIR波长(nm)'),
|
||||||
band_fields = [
|
],
|
||||||
('hedley_nir_band', 'hedley_nir_band', 47, 'NIR波段'),
|
}.get(method, [])
|
||||||
]
|
# SUGAR 等方法无波长参数,跳过
|
||||||
else: # sugar 无波段索引
|
if not _wavelength_fields:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# 4) 逐字段检查;遇到第一个越界就弹窗(用户处理完继续检查下一个)
|
for cfg_key, recommended, label in _wavelength_fields:
|
||||||
for cfg_key, panel_attr, recommended, label in band_fields:
|
|
||||||
requested = step3_cfg.get(cfg_key)
|
requested = step3_cfg.get(cfg_key)
|
||||||
if requested is None or requested <= max_band:
|
if requested is None:
|
||||||
continue # 没设 / 没越界
|
|
||||||
|
|
||||||
self.log_message(
|
|
||||||
f"⚠ step3 波段越界:{label}={requested} > 影像波段数 {max_band}",
|
|
||||||
"warning",
|
|
||||||
)
|
|
||||||
|
|
||||||
dlg = BandConfirmDialog(
|
|
||||||
self,
|
|
||||||
requested_band=requested,
|
|
||||||
max_band=max_band,
|
|
||||||
recommended_band=recommended,
|
|
||||||
method_label=label,
|
|
||||||
)
|
|
||||||
result = dlg.exec_()
|
|
||||||
if result == QDialog.Rejected:
|
|
||||||
self.log_message("✗ 用户取消运行(step3 波段越界未解决)", "warning")
|
|
||||||
return False
|
|
||||||
|
|
||||||
new_band = dlg.selected_band()
|
|
||||||
try:
|
|
||||||
spin = getattr(step3_panel, panel_attr)
|
|
||||||
spin.setValue(new_band)
|
|
||||||
except AttributeError:
|
|
||||||
self.log_message(f"⚠ step3 panel 缺控件 {panel_attr},跳过回写", "warning")
|
|
||||||
continue
|
continue
|
||||||
|
# 波长合理性软警告(find_band_number 内部会自动适配)
|
||||||
self.log_message(
|
if requested < 300 or requested > 3000:
|
||||||
f"✓ {label}:{requested} → {new_band}(影像最多 {max_band} 波段)",
|
self.log_message(
|
||||||
"info",
|
f"⚠ step3 波长异常:{label}={requested}nm(合理范围 300-3000nm)",
|
||||||
)
|
"warning",
|
||||||
|
)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|||||||
@ -649,65 +649,41 @@ class PipelineExecutor(QObject):
|
|||||||
if max_band <= 0:
|
if max_band <= 0:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
# 波长驱动改造后,波段号由 find_band_number() 从 HDR 元数据中
|
||||||
|
# 动态解析,不再需要波段索引越界检查。仅对极端波长值做合理性提醒。
|
||||||
method = step3_cfg.get('method', 'goodman')
|
method = step3_cfg.get('method', 'goodman')
|
||||||
if method == 'goodman':
|
_wavelength_fields = {
|
||||||
band_fields = [
|
'goodman': [
|
||||||
('nir_lower', 'nir_lower', 65, 'NIR下波段'),
|
('nir_lower_wavelength', 'nir_lower_wavelength', 641.93, 'NIR下界波长(nm)'),
|
||||||
('nir_upper', 'nir_upper', 91, 'NIR上波段'),
|
('nir_upper_wavelength', 'nir_upper_wavelength', 751.49, 'NIR上界波长(nm)'),
|
||||||
]
|
],
|
||||||
elif method == 'kutser':
|
'kutser': [
|
||||||
band_fields = [
|
('oxy_wavelength', 'oxy_wavelength', 760.6, '氧吸收波长(nm)'),
|
||||||
('oxy_band', 'oxy_band', 38, '氧吸收波段'),
|
('lower_wavelength', 'lower_wavelength', 742.39, '下氧吸收波长(nm)'),
|
||||||
('lower_oxy', 'lower_oxy', 36, '下氧吸收波段'),
|
('upper_wavelength', 'upper_wavelength', 860.48, '上氧吸收波长(nm)'),
|
||||||
('upper_oxy', 'upper_oxy', 49, '上氧吸收波段'),
|
('nir_wavelength', 'nir_wavelength', 842.36, 'NIR波长(nm)'),
|
||||||
('nir_band', 'nir_band', 47, 'NIR波段'),
|
],
|
||||||
]
|
'hedley': [
|
||||||
elif method == 'hedley':
|
('hedley_nir_wavelength', 'hedley_nir_wavelength', 842.36, 'NIR波长(nm)'),
|
||||||
band_fields = [
|
],
|
||||||
('hedley_nir_band', 'hedley_nir_band', 47, 'NIR波段'),
|
}.get(method, [])
|
||||||
]
|
# SUGAR 等方法无波长参数,跳过
|
||||||
else:
|
if not _wavelength_fields:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
for cfg_key, panel_attr, recommended, label in band_fields:
|
for cfg_key, panel_attr, recommended, label in _wavelength_fields:
|
||||||
requested = step3_cfg.get(cfg_key)
|
requested = step3_cfg.get(cfg_key)
|
||||||
if requested is None or requested <= max_band:
|
if requested is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
global_event_bus.publish('LogMessage', {
|
# 波长驱动:仅对异常波长做软警告(find_band_number 会自动处理)
|
||||||
'message': f'⚠ step3 波段越界:{label}={requested} > 影像波段数 {max_band}',
|
if requested < 300 or requested > 3000:
|
||||||
'level': 'warning',
|
|
||||||
})
|
|
||||||
|
|
||||||
dlg = BandConfirmDialog(
|
|
||||||
self.parent(),
|
|
||||||
requested_band=requested,
|
|
||||||
max_band=max_band,
|
|
||||||
recommended_band=recommended,
|
|
||||||
method_label=label,
|
|
||||||
)
|
|
||||||
result = dlg.exec_()
|
|
||||||
if result == QDialog.Rejected:
|
|
||||||
global_event_bus.publish('LogMessage', {
|
global_event_bus.publish('LogMessage', {
|
||||||
'message': '✗ 用户取消运行(step3 波段越界未解决)',
|
'message': f'⚠ step3 波长异常:{label}={requested}nm(合理范围 300-3000nm)',
|
||||||
'level': 'warning',
|
'level': 'warning',
|
||||||
})
|
})
|
||||||
return False
|
continue
|
||||||
|
|
||||||
new_band = dlg.selected_band()
|
|
||||||
try:
|
|
||||||
spin = getattr(step3_panel, panel_attr)
|
|
||||||
spin.setValue(new_band)
|
|
||||||
except AttributeError:
|
|
||||||
global_event_bus.publish('LogMessage', {
|
|
||||||
'message': f'⚠ step3 panel 缺控件 {panel_attr},跳过回写',
|
|
||||||
'level': 'warning',
|
|
||||||
})
|
|
||||||
continue
|
|
||||||
|
|
||||||
global_event_bus.publish('LogMessage', {
|
|
||||||
'message': f'✓ {label}:{requested} → {new_band}(影像最多 {max_band} 波段)',
|
|
||||||
'level': 'info',
|
|
||||||
})
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
return True
|
||||||
|
|
||||||
|
|||||||
@ -77,8 +77,8 @@ class Step11MapBatchThread(QThread):
|
|||||||
return
|
return
|
||||||
|
|
||||||
boundary_shp = self.step10_kwargs.get('boundary_shp_path')
|
boundary_shp = self.step10_kwargs.get('boundary_shp_path')
|
||||||
input_crs = self.step10_kwargs.get('input_crs', 'EPSG:32651')
|
input_crs = self.step10_kwargs.get('input_crs') or None
|
||||||
output_crs = self.step10_kwargs.get('output_crs', input_crs)
|
output_crs = self.step10_kwargs.get('output_crs') or None
|
||||||
resolution = float(self.step10_kwargs.get('resolution', 30))
|
resolution = float(self.step10_kwargs.get('resolution', 30))
|
||||||
|
|
||||||
# ── ★ 2026-07-01:QThread 内预计算共享空间上下文 ──
|
# ── ★ 2026-07-01:QThread 内预计算共享空间上下文 ──
|
||||||
@ -364,12 +364,12 @@ class Step11MapPanel(QWidget):
|
|||||||
|
|
||||||
# ---------- 统一组件:输入坐标系 ----------
|
# ---------- 统一组件:输入坐标系 ----------
|
||||||
self.input_crs = QLineEdit()
|
self.input_crs = QLineEdit()
|
||||||
self.input_crs.setText("EPSG:32651")
|
self.input_crs.setPlaceholderText("留空=从掩膜/栅格自动探测")
|
||||||
params_layout.addLayout(create_standard_row("输入坐标系:", self.input_crs))
|
params_layout.addLayout(create_standard_row("输入坐标系:", self.input_crs))
|
||||||
|
|
||||||
# ---------- 统一组件:输出坐标系 ----------
|
# ---------- 统一组件:输出坐标系 ----------
|
||||||
self.output_crs = QLineEdit()
|
self.output_crs = QLineEdit()
|
||||||
self.output_crs.setText("EPSG:32651")
|
self.output_crs.setPlaceholderText("留空=与输入坐标系一致")
|
||||||
params_layout.addLayout(create_standard_row("输出坐标系:", self.output_crs))
|
params_layout.addLayout(create_standard_row("输出坐标系:", self.output_crs))
|
||||||
|
|
||||||
# ---------- 复选框 ----------
|
# ---------- 复选框 ----------
|
||||||
@ -564,9 +564,8 @@ class Step11MapPanel(QWidget):
|
|||||||
self.resolution.setValue(config['resolution'])
|
self.resolution.setValue(config['resolution'])
|
||||||
if 'input_crs' in config:
|
if 'input_crs' in config:
|
||||||
self.input_crs.setText(config['input_crs'])
|
self.input_crs.setText(config['input_crs'])
|
||||||
# ★★★ 反灌入时强制 output_crs = input_crs,避免旧 config 中的 EPSG:4326 回填 ★★★
|
|
||||||
if 'output_crs' in config or 'input_crs' in config:
|
if 'output_crs' in config or 'input_crs' in config:
|
||||||
self.output_crs.setText(config.get('input_crs') or config.get('output_crs') or 'EPSG:32651')
|
self.output_crs.setText(config.get('input_crs') or config.get('output_crs') or '')
|
||||||
if 'show_sample_points' in config:
|
if 'show_sample_points' in config:
|
||||||
self.show_points.setChecked(config['show_sample_points'])
|
self.show_points.setChecked(config['show_sample_points'])
|
||||||
if 'use_distance_diffusion' in config:
|
if 'use_distance_diffusion' in config:
|
||||||
|
|||||||
@ -12,7 +12,7 @@ from src.gui.panels._step_path_resolver import resolve_subdir, scan_work_dir_for
|
|||||||
# ── 【修复点】:在这里加上了 QFrame 的导入 ──
|
# ── 【修复点】:在这里加上了 QFrame 的导入 ──
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QWidget, QVBoxLayout, QHBoxLayout, QGroupBox, QFormLayout,
|
QWidget, QVBoxLayout, QHBoxLayout, QGroupBox, QFormLayout,
|
||||||
QSpinBox, QComboBox, QCheckBox, QPushButton,
|
QSpinBox, QDoubleSpinBox, QComboBox, QCheckBox, QPushButton,
|
||||||
QLabel, QLineEdit, QMessageBox, QSizePolicy, QFrame
|
QLabel, QLineEdit, QMessageBox, QSizePolicy, QFrame
|
||||||
)
|
)
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
@ -97,17 +97,21 @@ class Step3Panel(QWidget):
|
|||||||
goodman_layout.setContentsMargins(0, 0, 0, 0)
|
goodman_layout.setContentsMargins(0, 0, 0, 0)
|
||||||
goodman_layout.setSpacing(16)
|
goodman_layout.setSpacing(16)
|
||||||
|
|
||||||
self.nir_lower = QSpinBox()
|
self.nir_lower_wavelength = QDoubleSpinBox()
|
||||||
self.nir_lower.setRange(0, 200)
|
self.nir_lower_wavelength.setDecimals(2)
|
||||||
self.nir_lower.setValue(65)
|
self.nir_lower_wavelength.setRange(300.0, 3000.0)
|
||||||
self.nir_lower.setButtonSymbols(QSpinBox.NoButtons)
|
self.nir_lower_wavelength.setValue(641.93)
|
||||||
self._add_aligned_row(goodman_layout, "NIR下波段索引:", self.nir_lower, "")
|
self.nir_lower_wavelength.setSuffix(" nm")
|
||||||
|
self.nir_lower_wavelength.setButtonSymbols(QDoubleSpinBox.NoButtons)
|
||||||
|
self._add_aligned_row(goodman_layout, "NIR下界波长:", self.nir_lower_wavelength, "~640nm")
|
||||||
|
|
||||||
self.nir_upper = QSpinBox()
|
self.nir_upper_wavelength = QDoubleSpinBox()
|
||||||
self.nir_upper.setRange(0, 200)
|
self.nir_upper_wavelength.setDecimals(2)
|
||||||
self.nir_upper.setValue(91)
|
self.nir_upper_wavelength.setRange(300.0, 3000.0)
|
||||||
self.nir_upper.setButtonSymbols(QSpinBox.NoButtons)
|
self.nir_upper_wavelength.setValue(751.49)
|
||||||
self._add_aligned_row(goodman_layout, "NIR上波段索引:", self.nir_upper, "")
|
self.nir_upper_wavelength.setSuffix(" nm")
|
||||||
|
self.nir_upper_wavelength.setButtonSymbols(QDoubleSpinBox.NoButtons)
|
||||||
|
self._add_aligned_row(goodman_layout, "NIR上界波长:", self.nir_upper_wavelength, "~750nm")
|
||||||
|
|
||||||
self.goodman_a = QLineEdit("0.000019")
|
self.goodman_a = QLineEdit("0.000019")
|
||||||
self.goodman_a.setValidator(QDoubleValidator(0.0, 1.0, 6, self))
|
self.goodman_a.setValidator(QDoubleValidator(0.0, 1.0, 6, self))
|
||||||
@ -124,29 +128,37 @@ class Step3Panel(QWidget):
|
|||||||
kutser_layout.setContentsMargins(0, 0, 0, 0)
|
kutser_layout.setContentsMargins(0, 0, 0, 0)
|
||||||
kutser_layout.setSpacing(16)
|
kutser_layout.setSpacing(16)
|
||||||
|
|
||||||
self.oxy_band = QSpinBox()
|
self.oxy_wavelength = QDoubleSpinBox()
|
||||||
self.oxy_band.setRange(0, 200)
|
self.oxy_wavelength.setDecimals(2)
|
||||||
self.oxy_band.setValue(38)
|
self.oxy_wavelength.setRange(300.0, 3000.0)
|
||||||
self.oxy_band.setButtonSymbols(QSpinBox.NoButtons)
|
self.oxy_wavelength.setValue(760.60)
|
||||||
self._add_aligned_row(kutser_layout, "氧吸收波段索引:", self.oxy_band, "")
|
self.oxy_wavelength.setSuffix(" nm")
|
||||||
|
self.oxy_wavelength.setButtonSymbols(QDoubleSpinBox.NoButtons)
|
||||||
|
self._add_aligned_row(kutser_layout, "氧吸收波长:", self.oxy_wavelength, "O₂-A带 760nm")
|
||||||
|
|
||||||
self.lower_oxy = QSpinBox()
|
self.lower_wavelength = QDoubleSpinBox()
|
||||||
self.lower_oxy.setRange(0, 200)
|
self.lower_wavelength.setDecimals(2)
|
||||||
self.lower_oxy.setValue(36)
|
self.lower_wavelength.setRange(300.0, 3000.0)
|
||||||
self.lower_oxy.setButtonSymbols(QSpinBox.NoButtons)
|
self.lower_wavelength.setValue(742.39)
|
||||||
self._add_aligned_row(kutser_layout, "下氧吸收波段索引:", self.lower_oxy, "")
|
self.lower_wavelength.setSuffix(" nm")
|
||||||
|
self.lower_wavelength.setButtonSymbols(QDoubleSpinBox.NoButtons)
|
||||||
|
self._add_aligned_row(kutser_layout, "左肩波长:", self.lower_wavelength, "~742nm")
|
||||||
|
|
||||||
self.upper_oxy = QSpinBox()
|
self.upper_wavelength = QDoubleSpinBox()
|
||||||
self.upper_oxy.setRange(0, 200)
|
self.upper_wavelength.setDecimals(2)
|
||||||
self.upper_oxy.setValue(49)
|
self.upper_wavelength.setRange(300.0, 3000.0)
|
||||||
self.upper_oxy.setButtonSymbols(QSpinBox.NoButtons)
|
self.upper_wavelength.setValue(860.48)
|
||||||
self._add_aligned_row(kutser_layout, "上氧吸收波段索引:", self.upper_oxy, "")
|
self.upper_wavelength.setSuffix(" nm")
|
||||||
|
self.upper_wavelength.setButtonSymbols(QDoubleSpinBox.NoButtons)
|
||||||
|
self._add_aligned_row(kutser_layout, "右肩波长:", self.upper_wavelength, "~860nm")
|
||||||
|
|
||||||
self.nir_band = QSpinBox()
|
self.nir_wavelength = QDoubleSpinBox()
|
||||||
self.nir_band.setRange(0, 200)
|
self.nir_wavelength.setDecimals(2)
|
||||||
self.nir_band.setValue(47)
|
self.nir_wavelength.setRange(300.0, 3000.0)
|
||||||
self.nir_band.setButtonSymbols(QSpinBox.NoButtons)
|
self.nir_wavelength.setValue(842.36)
|
||||||
self._add_aligned_row(kutser_layout, "NIR波段索引:", self.nir_band, "")
|
self.nir_wavelength.setSuffix(" nm")
|
||||||
|
self.nir_wavelength.setButtonSymbols(QDoubleSpinBox.NoButtons)
|
||||||
|
self._add_aligned_row(kutser_layout, "NIR参考波长:", self.nir_wavelength, "~842nm")
|
||||||
|
|
||||||
self.kutser_widget.setVisible(False)
|
self.kutser_widget.setVisible(False)
|
||||||
params_layout.addWidget(self.kutser_widget)
|
params_layout.addWidget(self.kutser_widget)
|
||||||
@ -157,11 +169,13 @@ class Step3Panel(QWidget):
|
|||||||
hedley_layout.setContentsMargins(0, 0, 0, 0)
|
hedley_layout.setContentsMargins(0, 0, 0, 0)
|
||||||
hedley_layout.setSpacing(16)
|
hedley_layout.setSpacing(16)
|
||||||
|
|
||||||
self.hedley_nir_band = QSpinBox()
|
self.hedley_nir_wavelength = QDoubleSpinBox()
|
||||||
self.hedley_nir_band.setRange(0, 200)
|
self.hedley_nir_wavelength.setDecimals(2)
|
||||||
self.hedley_nir_band.setValue(47)
|
self.hedley_nir_wavelength.setRange(300.0, 3000.0)
|
||||||
self.hedley_nir_band.setButtonSymbols(QSpinBox.NoButtons)
|
self.hedley_nir_wavelength.setValue(842.36)
|
||||||
self._add_aligned_row(hedley_layout, "NIR波段索引:", self.hedley_nir_band, "")
|
self.hedley_nir_wavelength.setSuffix(" nm")
|
||||||
|
self.hedley_nir_wavelength.setButtonSymbols(QDoubleSpinBox.NoButtons)
|
||||||
|
self._add_aligned_row(hedley_layout, "NIR参考波长:", self.hedley_nir_wavelength, "~842nm")
|
||||||
|
|
||||||
self.hedley_widget.setVisible(False)
|
self.hedley_widget.setVisible(False)
|
||||||
params_layout.addWidget(self.hedley_widget)
|
params_layout.addWidget(self.hedley_widget)
|
||||||
@ -319,6 +333,9 @@ class Step3Panel(QWidget):
|
|||||||
dialog.exec_()
|
dialog.exec_()
|
||||||
|
|
||||||
def _update_band_ranges(self, file_path):
|
def _update_band_ranges(self, file_path):
|
||||||
|
"""波长驱动改造后,波段号由 find_band_number() 动态解析,
|
||||||
|
GUI 输入的是波长(nm)而非波段索引,不再需要根据 RasterCount 限制范围。
|
||||||
|
保留此方法仅用于加载影像时输出波段信息日志。"""
|
||||||
from osgeo import gdal
|
from osgeo import gdal
|
||||||
if not file_path or not os.path.isfile(file_path):
|
if not file_path or not os.path.isfile(file_path):
|
||||||
return
|
return
|
||||||
@ -327,12 +344,8 @@ class Step3Panel(QWidget):
|
|||||||
if dataset is None:
|
if dataset is None:
|
||||||
return
|
return
|
||||||
raster_count = dataset.RasterCount
|
raster_count = dataset.RasterCount
|
||||||
max_band = max(0, raster_count - 1)
|
print(f"[Step3Panel] 影像已加载: {raster_count} 波段, "
|
||||||
self.nir_lower.setMaximum(max_band)
|
f"尺寸 {dataset.RasterXSize}x{dataset.RasterYSize}")
|
||||||
self.nir_upper.setMaximum(max_band)
|
|
||||||
self.oxy_band.setMaximum(max_band)
|
|
||||||
self.nir_band.setMaximum(max_band)
|
|
||||||
self.hedley_nir_band.setMaximum(max_band)
|
|
||||||
dataset = None
|
dataset = None
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
@ -401,19 +414,19 @@ class Step3Panel(QWidget):
|
|||||||
method = self.method.currentData()
|
method = self.method.currentData()
|
||||||
|
|
||||||
if method == 'goodman':
|
if method == 'goodman':
|
||||||
config['nir_lower'] = self.nir_lower.value()
|
config['nir_lower_wavelength'] = self.nir_lower_wavelength.value()
|
||||||
config['nir_upper'] = self.nir_upper.value()
|
config['nir_upper_wavelength'] = self.nir_upper_wavelength.value()
|
||||||
config['goodman_A'] = self._safe_float(self.goodman_a, 0.000019)
|
config['goodman_A'] = self._safe_float(self.goodman_a, 0.000019)
|
||||||
config['goodman_B'] = self._safe_float(self.goodman_b, 0.1)
|
config['goodman_B'] = self._safe_float(self.goodman_b, 0.1)
|
||||||
|
|
||||||
elif method == 'kutser':
|
elif method == 'kutser':
|
||||||
config['oxy_band'] = self.oxy_band.value()
|
config['oxy_wavelength'] = self.oxy_wavelength.value()
|
||||||
config['lower_oxy'] = self.lower_oxy.value()
|
config['lower_wavelength'] = self.lower_wavelength.value()
|
||||||
config['upper_oxy'] = self.upper_oxy.value()
|
config['upper_wavelength'] = self.upper_wavelength.value()
|
||||||
config['nir_band'] = self.nir_band.value()
|
config['nir_wavelength'] = self.nir_wavelength.value()
|
||||||
|
|
||||||
elif method == 'hedley':
|
elif method == 'hedley':
|
||||||
config['hedley_nir_band'] = self.hedley_nir_band.value()
|
config['hedley_nir_wavelength'] = self.hedley_nir_wavelength.value()
|
||||||
|
|
||||||
elif method == 'sugar':
|
elif method == 'sugar':
|
||||||
config['sugar_iter'] = self.sugar_iter.value() if self.sugar_iter.value() > 0 else None
|
config['sugar_iter'] = self.sugar_iter.value() if self.sugar_iter.value() > 0 else None
|
||||||
@ -447,26 +460,26 @@ class Step3Panel(QWidget):
|
|||||||
if idx >= 0:
|
if idx >= 0:
|
||||||
self.interp_method.setCurrentIndex(idx)
|
self.interp_method.setCurrentIndex(idx)
|
||||||
|
|
||||||
if 'nir_lower' in config:
|
if 'nir_lower_wavelength' in config:
|
||||||
self.nir_lower.setValue(config['nir_lower'])
|
self.nir_lower_wavelength.setValue(config['nir_lower_wavelength'])
|
||||||
if 'nir_upper' in config:
|
if 'nir_upper_wavelength' in config:
|
||||||
self.nir_upper.setValue(config['nir_upper'])
|
self.nir_upper_wavelength.setValue(config['nir_upper_wavelength'])
|
||||||
if 'goodman_A' in config:
|
if 'goodman_A' in config:
|
||||||
self.goodman_a.setText(f"{config['goodman_A']:.6f}")
|
self.goodman_a.setText(f"{config['goodman_A']:.6f}")
|
||||||
if 'goodman_B' in config:
|
if 'goodman_B' in config:
|
||||||
self.goodman_b.setText(f"{config['goodman_B']:.2f}")
|
self.goodman_b.setText(f"{config['goodman_B']:.2f}")
|
||||||
|
|
||||||
if 'oxy_band' in config:
|
if 'oxy_wavelength' in config:
|
||||||
self.oxy_band.setValue(config['oxy_band'])
|
self.oxy_wavelength.setValue(config['oxy_wavelength'])
|
||||||
if 'lower_oxy' in config:
|
if 'lower_wavelength' in config:
|
||||||
self.lower_oxy.setValue(config['lower_oxy'])
|
self.lower_wavelength.setValue(config['lower_wavelength'])
|
||||||
if 'upper_oxy' in config:
|
if 'upper_wavelength' in config:
|
||||||
self.upper_oxy.setValue(config['upper_oxy'])
|
self.upper_wavelength.setValue(config['upper_wavelength'])
|
||||||
if 'nir_band' in config:
|
if 'nir_wavelength' in config:
|
||||||
self.nir_band.setValue(config['nir_band'])
|
self.nir_wavelength.setValue(config['nir_wavelength'])
|
||||||
|
|
||||||
if 'hedley_nir_band' in config:
|
if 'hedley_nir_wavelength' in config:
|
||||||
self.hedley_nir_band.setValue(config['hedley_nir_band'])
|
self.hedley_nir_wavelength.setValue(config['hedley_nir_wavelength'])
|
||||||
|
|
||||||
if 'sugar_iter' in config:
|
if 'sugar_iter' in config:
|
||||||
self.sugar_iter.setValue(config['sugar_iter'] if config['sugar_iter'] is not None else 0)
|
self.sugar_iter.setValue(config['sugar_iter'] if config['sugar_iter'] is not None else 0)
|
||||||
|
|||||||
@ -24,9 +24,9 @@ Step11 后端计算服务(专题图生成 / 克里金插值)
|
|||||||
"geotiff_dir": "D:/10_WaterIndex_Images", # 批量 GeoTIFF
|
"geotiff_dir": "D:/10_WaterIndex_Images", # 批量 GeoTIFF
|
||||||
"boundary_shp_path": "D:/boundary.shp", # 边界 shp(可选)
|
"boundary_shp_path": "D:/boundary.shp", # 边界 shp(可选)
|
||||||
"resolution": 30.0, # 空间分辨率(米)
|
"resolution": 30.0, # 空间分辨率(米)
|
||||||
"input_crs": "EPSG:32651",
|
# CRS 默认留空,由 ContentMapper 从边界/栅格自动探测
|
||||||
# ★★★ 强制默认 output_crs = input_crs,禁止从 service 配置误改为 EPSG:4326 ★★★
|
"input_crs": None,
|
||||||
"output_crs": "EPSG:32651",
|
"output_crs": None,
|
||||||
"output_dir": "D:/11_Thematic_Map", # 输出目录
|
"output_dir": "D:/11_Thematic_Map", # 输出目录
|
||||||
"enabled": True,
|
"enabled": True,
|
||||||
"work_dir": "D:/workspace", # 工作目录
|
"work_dir": "D:/workspace", # 工作目录
|
||||||
@ -188,6 +188,13 @@ def _run_geotiff_mode(geotiff_paths: List[Path],
|
|||||||
from src.postprocessing.map import ContentMapper
|
from src.postprocessing.map import ContentMapper
|
||||||
|
|
||||||
mapper = ContentMapper()
|
mapper = ContentMapper()
|
||||||
|
# ★ CRS 动态探测:从第一个 GeoTIFF 或边界文件自动获取
|
||||||
|
if geotiff_paths:
|
||||||
|
mapper._ensure_crs(reference_file=str(geotiff_paths[0]))
|
||||||
|
elif boundary_shp_path:
|
||||||
|
mapper._ensure_crs(reference_file=boundary_shp_path)
|
||||||
|
else:
|
||||||
|
mapper._ensure_crs()
|
||||||
output_dir.mkdir(parents=True, exist_ok=True)
|
output_dir.mkdir(parents=True, exist_ok=True)
|
||||||
n_ok = 0
|
n_ok = 0
|
||||||
for tif_path in geotiff_paths:
|
for tif_path in geotiff_paths:
|
||||||
|
|||||||
@ -230,6 +230,8 @@ def _try_distribution_maps(work_dir: str, output_dir: Path) -> Dict[str, Any]:
|
|||||||
from src.postprocessing.map import ContentMapper
|
from src.postprocessing.map import ContentMapper
|
||||||
|
|
||||||
mapper = ContentMapper()
|
mapper = ContentMapper()
|
||||||
|
# ★ CRS 动态探测:从第一个 GeoTIFF 自动获取
|
||||||
|
mapper._ensure_crs(reference_file=str(tif_paths[0]))
|
||||||
for tif_path in tif_paths:
|
for tif_path in tif_paths:
|
||||||
stem = tif_path.stem
|
stem = tif_path.stem
|
||||||
chinese_title = mapper._get_chinese_title(stem)
|
chinese_title = mapper._get_chinese_title(stem)
|
||||||
|
|||||||
@ -19,10 +19,12 @@ Step3 后端计算服务(耀斑去除)
|
|||||||
"interpolation_method": "bilinear",
|
"interpolation_method": "bilinear",
|
||||||
"water_mask_path": "D:/mask.dat", # 水域掩膜(可选)
|
"water_mask_path": "D:/mask.dat", # 水域掩膜(可选)
|
||||||
"output_path": "D:/deglint_image.bsq",
|
"output_path": "D:/deglint_image.bsq",
|
||||||
# 方法专属参数(按 method 任选一组)
|
# 方法专属参数(波长驱动,nm → 自动解析波段号)
|
||||||
"nir_lower": 65, "nir_upper": 91, "goodman_A": 1.9e-5, "goodman_B": 0.1, # goodman
|
"nir_lower_wavelength": 641.93, "nir_upper_wavelength": 751.49,
|
||||||
"oxy_band": 38, "lower_oxy": 36, "upper_oxy": 49, "nir_band": 47, # kutser
|
"goodman_A": 1.9e-5, "goodman_B": 0.1, # goodman
|
||||||
"hedley_nir_band": 47, # hedley
|
"oxy_wavelength": 760.6, "lower_wavelength": 742.39, # kutser
|
||||||
|
"upper_wavelength": 860.48, "nir_wavelength": 842.36, # kutser
|
||||||
|
"hedley_nir_wavelength": 842.36, # hedley
|
||||||
"sugar_iter": 3, "sugar_sigma": 1.0, "sugar_estimate_background": True,
|
"sugar_iter": 3, "sugar_sigma": 1.0, "sugar_estimate_background": True,
|
||||||
"sugar_glint_mask_method": "cdf", "sugar_termination_thresh": 20.0,
|
"sugar_glint_mask_method": "cdf", "sugar_termination_thresh": 20.0,
|
||||||
"sugar_bounds": [(1, 2)], # sugar
|
"sugar_bounds": [(1, 2)], # sugar
|
||||||
@ -79,24 +81,24 @@ def _normalize_method(method: str) -> str:
|
|||||||
|
|
||||||
|
|
||||||
def _build_method_kwargs(method: str, config: Dict[str, Any]) -> Dict[str, Any]:
|
def _build_method_kwargs(method: str, config: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
"""按 method 从 config 中抽取对应的方法专属参数"""
|
"""按 method 从 config 中抽取对应的方法专属参数(波长驱动)"""
|
||||||
if method == "goodman":
|
if method == "goodman":
|
||||||
return {
|
return {
|
||||||
"nir_lower": int(config.get("nir_lower", 65)),
|
"nir_lower_wavelength": float(config.get("nir_lower_wavelength", 641.93)),
|
||||||
"nir_upper": int(config.get("nir_upper", 91)),
|
"nir_upper_wavelength": float(config.get("nir_upper_wavelength", 751.49)),
|
||||||
"goodman_A": float(config.get("goodman_A", 0.000019)),
|
"goodman_A": float(config.get("goodman_A", 0.000019)),
|
||||||
"goodman_B": float(config.get("goodman_B", 0.1)),
|
"goodman_B": float(config.get("goodman_B", 0.1)),
|
||||||
}
|
}
|
||||||
if method == "kutser":
|
if method == "kutser":
|
||||||
return {
|
return {
|
||||||
"oxy_band": int(config.get("oxy_band", 38)),
|
"oxy_wavelength": float(config.get("oxy_wavelength", 760.6)),
|
||||||
"lower_oxy": int(config.get("lower_oxy", 36)),
|
"lower_wavelength": float(config.get("lower_wavelength", 742.39)),
|
||||||
"upper_oxy": int(config.get("upper_oxy", 49)),
|
"upper_wavelength": float(config.get("upper_wavelength", 860.48)),
|
||||||
"nir_band": int(config.get("nir_band", 47)),
|
"nir_wavelength": float(config.get("nir_wavelength", 842.36)),
|
||||||
}
|
}
|
||||||
if method == "hedley":
|
if method == "hedley":
|
||||||
return {
|
return {
|
||||||
"hedley_nir_band": int(config.get("hedley_nir_band", 47)),
|
"hedley_nir_wavelength": float(config.get("hedley_nir_wavelength", 842.36)),
|
||||||
}
|
}
|
||||||
if method == "sugar":
|
if method == "sugar":
|
||||||
bounds = config.get("sugar_bounds")
|
bounds = config.get("sugar_bounds")
|
||||||
|
|||||||
@ -177,14 +177,11 @@ class Step11View(BaseView):
|
|||||||
params_layout.addRow("分辨率(米):", self.resolution)
|
params_layout.addRow("分辨率(米):", self.resolution)
|
||||||
|
|
||||||
self.input_crs = QLineEdit()
|
self.input_crs = QLineEdit()
|
||||||
self.input_crs.setText("EPSG:32651")
|
self.input_crs.setPlaceholderText("留空=从掩膜/栅格自动探测")
|
||||||
params_layout.addRow("输入坐标系:", self.input_crs)
|
params_layout.addRow("输入坐标系:", self.input_crs)
|
||||||
|
|
||||||
self.output_crs = QLineEdit()
|
self.output_crs = QLineEdit()
|
||||||
# ★★★ 强制默认输出坐标系与输入一致,禁止从 GUI 误改为 EPSG:4326 ★★★
|
self.output_crs.setPlaceholderText("留空=与输入坐标系一致")
|
||||||
# 历史默认值 'EPSG:4326' 会让 ContentMapper 把栅格重投影到经纬度,
|
|
||||||
# 与基于 EPSG:32651 的水域掩膜叠加时发生仿射变换撕裂(栅格错位、坐标轴扭曲)。
|
|
||||||
self.output_crs.setText("EPSG:32651")
|
|
||||||
params_layout.addRow("输出坐标系:", self.output_crs)
|
params_layout.addRow("输出坐标系:", self.output_crs)
|
||||||
|
|
||||||
self.show_points = QCheckBox("显示采样点")
|
self.show_points = QCheckBox("显示采样点")
|
||||||
@ -346,9 +343,8 @@ class Step11View(BaseView):
|
|||||||
self.resolution.setValue(config["resolution"])
|
self.resolution.setValue(config["resolution"])
|
||||||
if "input_crs" in config:
|
if "input_crs" in config:
|
||||||
self.input_crs.setText(config["input_crs"])
|
self.input_crs.setText(config["input_crs"])
|
||||||
# ★★★ 反灌入时强制 output_crs = input_crs,避免旧 config 中的 EPSG:4326 回填 ★★★
|
|
||||||
if "output_crs" in config or "input_crs" in config:
|
if "output_crs" in config or "input_crs" in config:
|
||||||
self.output_crs.setText(config.get("input_crs") or config.get("output_crs") or "EPSG:32651")
|
self.output_crs.setText(config.get("input_crs") or config.get("output_crs") or "")
|
||||||
if "show_sample_points" in config:
|
if "show_sample_points" in config:
|
||||||
self.show_points.setChecked(config["show_sample_points"])
|
self.show_points.setChecked(config["show_sample_points"])
|
||||||
if "use_distance_diffusion" in config:
|
if "use_distance_diffusion" in config:
|
||||||
|
|||||||
@ -64,14 +64,18 @@ class Step3View(BaseView):
|
|||||||
# Goodman 参数组
|
# Goodman 参数组
|
||||||
self.goodman_group = QGroupBox("Goodman方法参数")
|
self.goodman_group = QGroupBox("Goodman方法参数")
|
||||||
goodman_layout = QFormLayout()
|
goodman_layout = QFormLayout()
|
||||||
self.nir_lower = QSpinBox()
|
self.nir_lower_wavelength = QDoubleSpinBox()
|
||||||
self.nir_lower.setRange(0, 200)
|
self.nir_lower_wavelength.setDecimals(2)
|
||||||
self.nir_lower.setValue(65)
|
self.nir_lower_wavelength.setRange(300.0, 3000.0)
|
||||||
goodman_layout.addRow("NIR下波段索引:", self.nir_lower)
|
self.nir_lower_wavelength.setValue(641.93)
|
||||||
self.nir_upper = QSpinBox()
|
self.nir_lower_wavelength.setSuffix(" nm")
|
||||||
self.nir_upper.setRange(0, 200)
|
goodman_layout.addRow("NIR下界波长:", self.nir_lower_wavelength)
|
||||||
self.nir_upper.setValue(91)
|
self.nir_upper_wavelength = QDoubleSpinBox()
|
||||||
goodman_layout.addRow("NIR上波段索引:", self.nir_upper)
|
self.nir_upper_wavelength.setDecimals(2)
|
||||||
|
self.nir_upper_wavelength.setRange(300.0, 3000.0)
|
||||||
|
self.nir_upper_wavelength.setValue(751.49)
|
||||||
|
self.nir_upper_wavelength.setSuffix(" nm")
|
||||||
|
goodman_layout.addRow("NIR上界波长:", self.nir_upper_wavelength)
|
||||||
self.goodman_a = QDoubleSpinBox()
|
self.goodman_a = QDoubleSpinBox()
|
||||||
self.goodman_a.setDecimals(6)
|
self.goodman_a.setDecimals(6)
|
||||||
self.goodman_a.setRange(0, 1)
|
self.goodman_a.setRange(0, 1)
|
||||||
@ -88,22 +92,30 @@ class Step3View(BaseView):
|
|||||||
# Kutser 参数组
|
# Kutser 参数组
|
||||||
self.kutser_group = QGroupBox("Kutser方法参数")
|
self.kutser_group = QGroupBox("Kutser方法参数")
|
||||||
kutser_layout = QFormLayout()
|
kutser_layout = QFormLayout()
|
||||||
self.oxy_band = QSpinBox()
|
self.oxy_wavelength = QDoubleSpinBox()
|
||||||
self.oxy_band.setRange(0, 200)
|
self.oxy_wavelength.setDecimals(2)
|
||||||
self.oxy_band.setValue(38)
|
self.oxy_wavelength.setRange(300.0, 3000.0)
|
||||||
kutser_layout.addRow("氧吸收波段索引:", self.oxy_band)
|
self.oxy_wavelength.setValue(760.60)
|
||||||
self.lower_oxy = QSpinBox()
|
self.oxy_wavelength.setSuffix(" nm")
|
||||||
self.lower_oxy.setRange(0, 200)
|
kutser_layout.addRow("氧吸收波长:", self.oxy_wavelength)
|
||||||
self.lower_oxy.setValue(36)
|
self.lower_wavelength = QDoubleSpinBox()
|
||||||
kutser_layout.addRow("下氧吸收波段索引:", self.lower_oxy)
|
self.lower_wavelength.setDecimals(2)
|
||||||
self.upper_oxy = QSpinBox()
|
self.lower_wavelength.setRange(300.0, 3000.0)
|
||||||
self.upper_oxy.setRange(0, 200)
|
self.lower_wavelength.setValue(742.39)
|
||||||
self.upper_oxy.setValue(49)
|
self.lower_wavelength.setSuffix(" nm")
|
||||||
kutser_layout.addRow("上氧吸收波段索引:", self.upper_oxy)
|
kutser_layout.addRow("左肩波长:", self.lower_wavelength)
|
||||||
self.nir_band = QSpinBox()
|
self.upper_wavelength = QDoubleSpinBox()
|
||||||
self.nir_band.setRange(0, 200)
|
self.upper_wavelength.setDecimals(2)
|
||||||
self.nir_band.setValue(47)
|
self.upper_wavelength.setRange(300.0, 3000.0)
|
||||||
kutser_layout.addRow("NIR波段索引:", self.nir_band)
|
self.upper_wavelength.setValue(860.48)
|
||||||
|
self.upper_wavelength.setSuffix(" nm")
|
||||||
|
kutser_layout.addRow("右肩波长:", self.upper_wavelength)
|
||||||
|
self.nir_wavelength = QDoubleSpinBox()
|
||||||
|
self.nir_wavelength.setDecimals(2)
|
||||||
|
self.nir_wavelength.setRange(300.0, 3000.0)
|
||||||
|
self.nir_wavelength.setValue(842.36)
|
||||||
|
self.nir_wavelength.setSuffix(" nm")
|
||||||
|
kutser_layout.addRow("NIR参考波长:", self.nir_wavelength)
|
||||||
self.kutser_group.setLayout(kutser_layout)
|
self.kutser_group.setLayout(kutser_layout)
|
||||||
self.kutser_group.setVisible(False)
|
self.kutser_group.setVisible(False)
|
||||||
layout.addWidget(self.kutser_group)
|
layout.addWidget(self.kutser_group)
|
||||||
@ -111,10 +123,12 @@ class Step3View(BaseView):
|
|||||||
# Hedley 参数组
|
# Hedley 参数组
|
||||||
self.hedley_group = QGroupBox("Hedley方法参数")
|
self.hedley_group = QGroupBox("Hedley方法参数")
|
||||||
hedley_layout = QFormLayout()
|
hedley_layout = QFormLayout()
|
||||||
self.hedley_nir_band = QSpinBox()
|
self.hedley_nir_wavelength = QDoubleSpinBox()
|
||||||
self.hedley_nir_band.setRange(0, 200)
|
self.hedley_nir_wavelength.setDecimals(2)
|
||||||
self.hedley_nir_band.setValue(47)
|
self.hedley_nir_wavelength.setRange(300.0, 3000.0)
|
||||||
hedley_layout.addRow("NIR波段索引:", self.hedley_nir_band)
|
self.hedley_nir_wavelength.setValue(842.36)
|
||||||
|
self.hedley_nir_wavelength.setSuffix(" nm")
|
||||||
|
hedley_layout.addRow("NIR参考波长:", self.hedley_nir_wavelength)
|
||||||
self.hedley_group.setLayout(hedley_layout)
|
self.hedley_group.setLayout(hedley_layout)
|
||||||
self.hedley_group.setVisible(False)
|
self.hedley_group.setVisible(False)
|
||||||
layout.addWidget(self.hedley_group)
|
layout.addWidget(self.hedley_group)
|
||||||
@ -210,17 +224,17 @@ class Step3View(BaseView):
|
|||||||
|
|
||||||
method = self.method.currentData()
|
method = self.method.currentData()
|
||||||
if method == "goodman":
|
if method == "goodman":
|
||||||
config["nir_lower"] = self.nir_lower.value()
|
config["nir_lower_wavelength"] = self.nir_lower_wavelength.value()
|
||||||
config["nir_upper"] = self.nir_upper.value()
|
config["nir_upper_wavelength"] = self.nir_upper_wavelength.value()
|
||||||
config["goodman_A"] = self.goodman_a.value()
|
config["goodman_A"] = self.goodman_a.value()
|
||||||
config["goodman_B"] = self.goodman_b.value()
|
config["goodman_B"] = self.goodman_b.value()
|
||||||
elif method == "kutser":
|
elif method == "kutser":
|
||||||
config["oxy_band"] = self.oxy_band.value()
|
config["oxy_wavelength"] = self.oxy_wavelength.value()
|
||||||
config["lower_oxy"] = self.lower_oxy.value()
|
config["lower_wavelength"] = self.lower_wavelength.value()
|
||||||
config["upper_oxy"] = self.upper_oxy.value()
|
config["upper_wavelength"] = self.upper_wavelength.value()
|
||||||
config["nir_band"] = self.nir_band.value()
|
config["nir_wavelength"] = self.nir_wavelength.value()
|
||||||
elif method == "hedley":
|
elif method == "hedley":
|
||||||
config["hedley_nir_band"] = self.hedley_nir_band.value()
|
config["hedley_nir_wavelength"] = self.hedley_nir_wavelength.value()
|
||||||
elif method == "sugar":
|
elif method == "sugar":
|
||||||
config["sugar_iter"] = self.sugar_iter.value()
|
config["sugar_iter"] = self.sugar_iter.value()
|
||||||
config["sugar_sigma"] = self.sugar_sigma.value()
|
config["sugar_sigma"] = self.sugar_sigma.value()
|
||||||
|
|||||||
Reference in New Issue
Block a user