feat: improve D-InSAR pairing and distribution
This commit is contained in:
@@ -13,9 +13,11 @@ const BATCH_API_MAX_PAGES = 200;
|
||||
|
||||
const DataCopierPanel = ({ apiEndpoint, readOnly = false, onJobQueued }) => {
|
||||
const { t } = useI18n();
|
||||
const [activeTab, setActiveTab] = useState('ps');
|
||||
const [activeTab, setActiveTab] = useState('dinsar');
|
||||
const [destDir, setDestDir] = useState('');
|
||||
const [copyStatuses, setCopyStatuses] = useState(['COMPLETED']);
|
||||
const [includeDinsarOrbitFiles, setIncludeDinsarOrbitFiles] = useState(false);
|
||||
const [dinsarExportZip, setDinsarExportZip] = useState(false);
|
||||
const [batches, setBatches] = useState([]);
|
||||
const [selectedBatchId, setSelectedBatchId] = useState('');
|
||||
const [isUploading, setIsUploading] = useState(false);
|
||||
@@ -117,11 +119,16 @@ const DataCopierPanel = ({ apiEndpoint, readOnly = false, onJobQueued }) => {
|
||||
: `${apiEndpoint}/tools/copy-dinsar-pairs`;
|
||||
|
||||
try {
|
||||
const response = await axios.post(endpoint, {
|
||||
const payload = {
|
||||
batch_id: selectedBatchId,
|
||||
dest_dir: destDir,
|
||||
copy_statuses: copyStatuses,
|
||||
}, { withCredentials: true });
|
||||
};
|
||||
if (activeTab === 'dinsar') {
|
||||
payload.include_orbit_files = includeDinsarOrbitFiles;
|
||||
payload.export_zip = dinsarExportZip;
|
||||
}
|
||||
const response = await axios.post(endpoint, payload, { withCredentials: true });
|
||||
const taskId = response.data.task_id;
|
||||
setTaskId(taskId);
|
||||
|
||||
@@ -178,6 +185,42 @@ const DataCopierPanel = ({ apiEndpoint, readOnly = false, onJobQueued }) => {
|
||||
当前账号为只读模式,无法发起复制任务。
|
||||
</div>
|
||||
)}
|
||||
{activeTab === 'dinsar' && (
|
||||
<div
|
||||
className="input-group"
|
||||
style={{
|
||||
border: '1px solid #c7d2fe',
|
||||
background: '#eef2ff',
|
||||
borderRadius: '8px',
|
||||
padding: '10px 12px',
|
||||
}}
|
||||
>
|
||||
<label>D-InSAR 分发设置:</label>
|
||||
<div style={{ display: 'flex', gap: '16px', flexWrap: 'wrap', marginTop: '8px' }}>
|
||||
<label style={{ display: 'inline-flex', alignItems: 'center', gap: '6px' }}>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={includeDinsarOrbitFiles}
|
||||
onChange={(event) => setIncludeDinsarOrbitFiles(event.target.checked)}
|
||||
disabled={status === 'RUNNING' || readOnly}
|
||||
/>
|
||||
<span>复制精密轨道到 Task/orbit</span>
|
||||
</label>
|
||||
<label style={{ display: 'inline-flex', alignItems: 'center', gap: '6px' }}>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={dinsarExportZip}
|
||||
onChange={(event) => setDinsarExportZip(event.target.checked)}
|
||||
disabled={status === 'RUNNING' || readOnly}
|
||||
/>
|
||||
<span>导出为 ZIP 压缩包</span>
|
||||
</label>
|
||||
</div>
|
||||
<div style={{ fontSize: '12px', color: '#475569', marginTop: '6px' }}>
|
||||
未勾选 ZIP 时直接导出 Task 文件夹;勾选后每个 Task 输出一个 .zip。
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="input-group">
|
||||
<label>1. 选择批次:</label>
|
||||
<div style={{ display: 'flex', gap: '10px', alignItems: 'center' }}>
|
||||
|
||||
@@ -476,7 +476,7 @@ export default function DinsarCatalogPanel({
|
||||
<MetaField label="主影像日期" value={selectedProduct.profile?.master_imaging_date} />
|
||||
<MetaField label="从影像日期" value={selectedProduct.profile?.slave_imaging_date} />
|
||||
<MetaField label="时间基线" value={selectedProduct.profile?.time_baseline_days} />
|
||||
<MetaField label="空间基线" value={selectedProduct.profile?.spatial_baseline_meters} />
|
||||
<MetaField label="footprint 中心距" value={selectedProduct.profile?.scene_center_distance_meters ?? selectedProduct.profile?.spatial_baseline_meters} />
|
||||
</div>
|
||||
<div className="dinsar-catalog-section-card">
|
||||
<div className="dinsar-catalog-section-title">空间范围</div>
|
||||
@@ -521,7 +521,7 @@ export default function DinsarCatalogPanel({
|
||||
<MetaField label="主从模式" value={`${selectedPairingMetric?.master_imaging_mode || '-'} / ${selectedPairingMetric?.slave_imaging_mode || '-'}`} />
|
||||
<MetaField label="主从极化" value={`${selectedPairingMetric?.master_polarization || '-'} / ${selectedPairingMetric?.slave_polarization || '-'}`} />
|
||||
<MetaField label="时间基线" value={selectedPairingMetric?.time_baseline_days} />
|
||||
<MetaField label="空间基线" value={selectedPairingMetric?.spatial_baseline_meters} />
|
||||
<MetaField label="footprint 中心距" value={selectedPairingMetric?.scene_center_distance_meters ?? selectedPairingMetric?.spatial_baseline_meters} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -11,23 +11,23 @@ const STRATEGY_DESCRIPTIONS = {
|
||||
title: '全部配对(默认)',
|
||||
description: '列出所有满足约束条件的候选干涉对,由用户自行筛选。',
|
||||
details: [
|
||||
'• 系统遍历所有影像组合,保留满足时间基线、空间基线和重叠率阈值的配对',
|
||||
'• 系统遍历所有影像组合,保留满足时间基线、footprint 中心距和两景 footprint 最小重叠率的配对',
|
||||
'• 结果按时间排序,用户可在配对列表中逐一勾选或取消',
|
||||
'• 适用于研究型场景,需要精确控制每一对干涉组合',
|
||||
'• 配对数量可能较多,建议配合 AOI 和日期范围缩小结果'
|
||||
],
|
||||
params: '参数:时间基线范围、空间基线上限、最小重叠率'
|
||||
params: '参数:时间基线范围、中心距上限、两景 footprint 最小重叠率'
|
||||
},
|
||||
sbas: {
|
||||
title: 'SBAS (短基线子集)',
|
||||
description: '基于短基线原则的配对策略,通过覆盖优化算法自动筛选配对。',
|
||||
details: [
|
||||
'• 优先选择时间和空间基线都较短的配对',
|
||||
'• 优先选择时间间隔和 footprint 中心距都较短的配对',
|
||||
'• 通过覆盖优化算法,去除冗余配对,确保时间序列连续性',
|
||||
'• 适用于大范围、长时间序列的形变监测',
|
||||
'• 配对数量会比"全部配对"少,但覆盖更均匀'
|
||||
],
|
||||
params: '参数:时间基线、空间基线、重叠率阈值、覆盖多样性惩罚'
|
||||
params: '参数:时间基线、中心距、两景 footprint 最小重叠率、覆盖多样性惩罚'
|
||||
},
|
||||
sequential: {
|
||||
title: 'Sequential (顺序配对)',
|
||||
@@ -321,24 +321,24 @@ function PairingModal({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 基线和重叠率约束 */}
|
||||
{/* 时间、中心距和重叠率约束 */}
|
||||
<div className="form-group">
|
||||
<label>时间基线最小值 (天):</label>
|
||||
<input type="number" min="0" value={pairingParams.time_baseline_min}
|
||||
onChange={e => setPairingParams({...pairingParams, time_baseline_min: parseInt(e.target.value) || 0})} />
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>时间基线最大值 (天):</label>
|
||||
<label>最大时间间隔 (天):</label>
|
||||
<input type="number" min="1" value={pairingParams.time_baseline_max}
|
||||
onChange={e => setPairingParams({...pairingParams, time_baseline_max: parseInt(e.target.value) || 90})} />
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>最小重叠率 (0-1):</label>
|
||||
<label>两景 footprint 最小重叠率 (0-1):</label>
|
||||
<input type="number" step="0.1" min="0" max="1" value={pairingParams.overlap_threshold}
|
||||
onChange={e => setPairingParams({...pairingParams, overlap_threshold: parseFloat(e.target.value) || 0})} />
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>空间基线上限 (米):</label>
|
||||
<label>footprint 中心距上限 (米):</label>
|
||||
<input type="number" min="0" value={pairingParams.spatial_baseline_max_meters}
|
||||
onChange={e => setPairingParams({...pairingParams, spatial_baseline_max_meters: parseInt(e.target.value) || 3000})} />
|
||||
</div>
|
||||
|
||||
@@ -7,6 +7,7 @@ function PairListRow({
|
||||
onVisualizePair,
|
||||
onTogglePairVisibility,
|
||||
}) {
|
||||
const centerDistance = pair.scene_center_distance_meters ?? pair.spatial_baseline_meters;
|
||||
return (
|
||||
<li className="pair-item">
|
||||
<input
|
||||
@@ -20,7 +21,7 @@ function PairListRow({
|
||||
<strong>{pair.task_name}</strong>
|
||||
<div className="pair-details">
|
||||
<span>时基: {pair.time_baseline_days}d</span>
|
||||
<span>空基: {pair.spatial_baseline_meters.toFixed(2)}m</span>
|
||||
<span>中心距: {Number(centerDistance || 0).toFixed(2)}m</span>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
|
||||
@@ -138,7 +138,7 @@
|
||||
{ zh: '请先训练模型。', en: 'Please train the model first.' },
|
||||
|
||||
// PairingPanel
|
||||
{ zh: '基于时间基线、空间基线与重叠率筛选干涉对,可选 AOI 限定范围。', en: 'Filter interferometric pairs by temporal baseline, spatial baseline, and overlap ratio. Optional AOI constraint.' },
|
||||
{ zh: '基于时间基线、footprint 中心距与两景 footprint 最小重叠率筛选干涉对,可选 AOI 限定范围。', en: 'Filter interferometric pairs by temporal baseline, footprint center distance, and pair footprint overlap ratio. Optional AOI constraint.' },
|
||||
{ zh: '配对', en: 'Pair' },
|
||||
{ zh: '时序准备', en: 'Timeseries Prep' },
|
||||
{ zh: '结果与刷新', en: 'Results & Refresh' },
|
||||
|
||||
@@ -107,8 +107,8 @@ export default function PairPlanningPanel({
|
||||
<div className="panel-card-title">{en ? 'Pair Planning' : '配对规划'}</div>
|
||||
<p className="panel-card-desc">
|
||||
{en
|
||||
? 'Filter interferometric pairs by temporal baseline, spatial baseline, and overlap ratio. Optional AOI constraint.'
|
||||
: '基于时间基线、空间基线和重叠率筛选干涉对,可选 AOI 约束范围。'}
|
||||
? 'Filter interferometric pairs by temporal baseline, footprint center distance, and pair footprint overlap ratio. Optional AOI constraint.'
|
||||
: '基于时间基线、footprint 中心距和两景 footprint 最小重叠率筛选干涉对,可选 AOI 约束范围。'}
|
||||
</p>
|
||||
<div className="header-buttons" style={{ marginTop: '10px' }}>
|
||||
<button onClick={onOpenPairingModal} disabled={isLoading || !hasEnoughRadarScenesForPlanning || isReadOnlyUser} style={{ flex: 1 }}>
|
||||
|
||||
@@ -19,8 +19,8 @@ export default function PairingPanel({
|
||||
<div className="panel-card-title">{en ? 'Pair Planning' : '配对规划'}</div>
|
||||
<p className="panel-card-desc">
|
||||
{en
|
||||
? 'Filter interferometric pairs by temporal baseline, spatial baseline, and overlap ratio. Optional AOI constraint.'
|
||||
: '基于时间基线、空间基线与重叠率筛选干涉对,可选 AOI 限定范围。'
|
||||
? 'Filter interferometric pairs by temporal baseline, footprint center distance, and pair footprint overlap ratio. Optional AOI constraint.'
|
||||
: '基于时间基线、footprint 中心距与两景 footprint 最小重叠率筛选干涉对,可选 AOI 限定范围。'
|
||||
}
|
||||
</p>
|
||||
<div className="header-buttons" style={{ marginTop: '10px' }}>
|
||||
|
||||
Reference in New Issue
Block a user