feat: 前端新增雨量预测准确率

This commit is contained in:
yarnom 2025-10-17 10:56:41 +08:00
parent 582270ce95
commit 0caa1da229
2 changed files with 95 additions and 3 deletions

View File

@ -45,8 +45,11 @@ const WeatherChart = {
const bucket = byTime.get(fp.date_time);
const h = typeof fp.lead_hours === 'number' ? fp.lead_hours : null;
if (h !== null && h >= 0 && h <= 3) {
// 保留同一 forecast_time+lead 的最新版本(查询结果已按 issued_at DESC 排序)
if (bucket[h] == null) {
bucket[h] = fp;
}
}
});
const getRainAtLead = (label, lead) => {
@ -81,6 +84,76 @@ const WeatherChart = {
if (this.chart) this.chart.destroy();
// 计算降水分类准确率(+1h/+2h/+3h
const updateAccuracyPanel = () => {
// 仅在有历史数据(实际)时计算
const usedIdx = historyRainfalls
.map((v, idx) => ({ v, idx }))
.filter(x => x.v !== null)
.map(x => x.idx);
const totalHours = usedIdx.length;
const bucketOf = (mm) => {
if (mm === null || mm === undefined || isNaN(Number(mm))) return null;
const v = Math.max(0, Number(mm));
if (v < 5) return 0;
if (v < 10) return 1;
return 2;
};
const calcFor = (arrFcst) => {
let correct = 0;
usedIdx.forEach(i => {
const a = historyRainfalls[i];
const f = arrFcst[i];
const ba = bucketOf(a);
const bf = bucketOf(f);
if (ba !== null && bf !== null && ba === bf) correct += 1;
});
return { correct, total: totalHours };
};
const fmt = (n) => `${n.toFixed(1)}%`;
const elPanel = document.getElementById('accuracyPanel');
const elH1 = document.getElementById('accH1');
const elH2 = document.getElementById('accH2');
const elH3 = document.getElementById('accH3');
if (!elPanel || !elH1 || !elH2 || !elH3) return;
if (forecastData.length === 0 || totalHours === 0) {
elPanel.style.display = 'none';
return;
}
// 详细计算过程日志
try {
console.groupCollapsed('[准确率] 降水分档 (+1h/+2h/+3h) 计算详情');
console.log('时间段总小时(有实测):', totalHours);
const nameOf = (b) => (b===0?'[0,5)':(b===1?'[5,10)':'[10,∞)'));
const fv = (v) => (v===null||v===undefined||isNaN(Number(v)) ? 'NULL' : Number(v).toFixed(2));
usedIdx.forEach(i => {
const label = allLabels[i];
const a = historyRainfalls[i];
const bA = bucketOf(a);
const f1 = forecastRainfallsH1[i]; const b1 = bucketOf(f1);
const f2 = forecastRainfallsH2[i]; const b2 = bucketOf(f2);
const f3 = forecastRainfallsH3[i]; const b3 = bucketOf(f3);
const m1 = (bA!==null && b1!==null && bA===b1) ? '√' : '×';
const m2 = (bA!==null && b2!==null && bA===b2) ? '√' : '×';
const m3 = (bA!==null && b3!==null && bA===b3) ? '√' : '×';
console.log(
`${label} | 实测 ${fv(a)}mm (${bA===null?'--':nameOf(bA)}) | +1h ${fv(f1)} (${b1===null?'--':nameOf(b1)}) ${m1} | +2h ${fv(f2)} (${b2===null?'--':nameOf(b2)}) ${m2} | +3h ${fv(f3)} (${b3===null?'--':nameOf(b3)}) ${m3}`
);
});
} catch (e) { console.warn('准确率计算日志输出失败', e); }
const r1 = calcFor(forecastRainfallsH1);
const r2 = calcFor(forecastRainfallsH2);
const r3 = calcFor(forecastRainfallsH3);
console.log(`+1h: ${r1.correct}/${r1.total}`);
console.log(`+2h: ${r2.correct}/${r2.total}`);
console.log(`+3h: ${r3.correct}/${r3.total}`);
console.groupEnd();
elH1.textContent = r1.total > 0 ? fmt((r1.correct / r1.total) * 100) : '--';
elH2.textContent = r2.total > 0 ? fmt((r2.correct / r2.total) * 100) : '--';
elH3.textContent = r3.total > 0 ? fmt((r3.correct / r3.total) * 100) : '--';
elPanel.style.display = 'block';
};
const datasets = [
{
label: '温度 (°C) - 实测',
@ -274,6 +347,9 @@ const WeatherChart = {
this.chart = new Chart(ctx, chartConfig);
// 更新准确率面板
updateAccuracyPanel();
const mode = document.getElementById('legendMode')?.value || 'combo_standard';
this.applyLegendMode(mode);
}

View File

@ -196,6 +196,18 @@
animation: slideDown 0.3s ease;
}
.accuracy-panel {
display: none;
font-size: 12px;
color: #374151; /* 灰色文字 */
white-space: nowrap;
text-align: right;
margin-top: -6px;
}
.accuracy-panel .item { margin-left: 8px; }
.accuracy-panel .label { color: #6b7280; margin-right: 4px; }
.accuracy-panel .value { font-weight: 600; color: #111827; }
.chart-wrapper {
height: 500px;
margin-bottom: 30px;
@ -520,7 +532,6 @@
</div>
</div>
<!-- 在时间范围下方增加瓦片联动控制 -->
<div class="control-row flex items-center gap-3 flex-wrap">
<div class="control-group">
<label class="text-sm text-gray-600">叠加显示:</label>
@ -552,6 +563,11 @@
<div class="chart-container" id="chartContainer">
<div id="stationInfoTitle" class="station-info-title"></div>
<div id="accuracyPanel" class="accuracy-panel">
<span class="item"><span class="label">+1h</span><span id="accH1" class="value">--</span></span>
<span class="item"><span class="label">+2h</span><span id="accH2" class="value">--</span></span>
<span class="item"><span class="label">+3h</span><span id="accH3" class="value">--</span></span>
</div>
<div class="chart-wrapper">
<canvas id="combinedChart"></canvas>
</div>