feat: 前端新增雨量预测准确率
This commit is contained in:
parent
582270ce95
commit
0caa1da229
@ -45,7 +45,10 @@ 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) {
|
||||
bucket[h] = fp;
|
||||
// 保留同一 forecast_time+lead 的最新版本(查询结果已按 issued_at DESC 排序)
|
||||
if (bucket[h] == null) {
|
||||
bucket[h] = fp;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user