feat: 雷达历史数据
This commit is contained in:
parent
12b2ad5ace
commit
2085fd9a31
@ -35,7 +35,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card mb-4">
|
<div class="card mb-4">
|
||||||
<div class="text-lg font-semibold mb-2">最新 7/40/104 瓦片信息</div>
|
<div class="text-lg font-semibold mb-2">7/40/104 瓦片信息</div>
|
||||||
<div class="text-sm space-y-1">
|
<div class="text-sm space-y-1">
|
||||||
<div>时间:<span id="dt" class="font-mono"></span></div>
|
<div>时间:<span id="dt" class="font-mono"></span></div>
|
||||||
<div>索引:z=<span id="z"></span> / y=<span id="y"></span> / x=<span id="x"></span></div>
|
<div>索引:z=<span id="z"></span> / y=<span id="y"></span> / x=<span id="x"></span></div>
|
||||||
@ -46,7 +46,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card mb-4">
|
<div class="card mb-4">
|
||||||
<div class="text-lg font-semibold mb-2">最新雷达站气象(广州雷达站,金山楼顶坐标)</div>
|
<div class="text-lg font-semibold mb-2">雷达站气象(广州雷达站,金山楼顶坐标)</div>
|
||||||
<div id="rtInfo" class="text-sm grid grid-cols-2 gap-y-1 gap-x-6">
|
<div id="rtInfo" class="text-sm grid grid-cols-2 gap-y-1 gap-x-6">
|
||||||
<div>站点:<span id="rt_alias"></span></div>
|
<div>站点:<span id="rt_alias"></span></div>
|
||||||
<div>位置:<span id="rt_lat"></span>,<span id="rt_lon"></span></div>
|
<div>位置:<span id="rt_lat"></span>,<span id="rt_lon"></span></div>
|
||||||
@ -83,6 +83,9 @@
|
|||||||
<button id="btnNext" class="px-2 py-1 border rounded">下一时次</button>
|
<button id="btnNext" class="px-2 py-1 border rounded">下一时次</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="w-full flex justify-center mb-2">
|
||||||
|
<input id="timeSlider" type="range" min="0" max="0" value="0" step="1" class="slider slider-horizontal w-64" />
|
||||||
|
</div>
|
||||||
<div id="radarPlot" class="plot-box"></div>
|
<div id="radarPlot" class="plot-box"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -148,7 +151,11 @@
|
|||||||
gTimes = j.times || [];
|
gTimes = j.times || [];
|
||||||
gTimes.forEach(dt=>{ const opt=document.createElement('option'); opt.value=dt; opt.textContent=dt; sel.appendChild(opt); });
|
gTimes.forEach(dt=>{ const opt=document.createElement('option'); opt.value=dt; opt.textContent=dt; sel.appendChild(opt); });
|
||||||
if (gTimes.length>0 && gCurrentIdx<0){ sel.value=gTimes[0]; gCurrentIdx=0; }
|
if (gTimes.length>0 && gCurrentIdx<0){ sel.value=gTimes[0]; gCurrentIdx=0; }
|
||||||
updateCountAndButtons();
|
updateCountAndButtons(); updateSlider();
|
||||||
|
const shown = document.getElementById('dt').textContent;
|
||||||
|
if (gTimes.length>0 && gTimes[gCurrentIdx] && gTimes[gCurrentIdx] !== shown) {
|
||||||
|
await loadTileAt(gTimes[gCurrentIdx]);
|
||||||
|
}
|
||||||
}catch{}
|
}catch{}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,7 +177,7 @@
|
|||||||
const selBox = document.getElementById('timeSelect');
|
const selBox = document.getElementById('timeSelect');
|
||||||
for(let i=0;i<selBox.options.length;i++){ if(selBox.options[i].value===t.dt){ selBox.selectedIndex=i; break; } }
|
for(let i=0;i<selBox.options.length;i++){ if(selBox.options[i].value===t.dt){ selBox.selectedIndex=i; break; } }
|
||||||
gCurrentIdx = gTimes.indexOf(t.dt);
|
gCurrentIdx = gTimes.indexOf(t.dt);
|
||||||
updateCountAndButtons();
|
updateCountAndButtons(); updateSlider();
|
||||||
|
|
||||||
// x/y 等角坐标
|
// x/y 等角坐标
|
||||||
const w=t.width,h=t.height; gTileValues=t.values;
|
const w=t.width,h=t.height; gTileValues=t.values;
|
||||||
@ -329,7 +336,7 @@
|
|||||||
const v=e.target.value;
|
const v=e.target.value;
|
||||||
if(!v){ if(gTimes.length>0){ gCurrentIdx=0; await loadTileAt(gTimes[0]); } else { gCurrentIdx=-1; await loadLatestTile(); } }
|
if(!v){ if(gTimes.length>0){ gCurrentIdx=0; await loadTileAt(gTimes[0]); } else { gCurrentIdx=-1; await loadLatestTile(); } }
|
||||||
else { gCurrentIdx=gTimes.indexOf(v); await loadTileAt(v); }
|
else { gCurrentIdx=gTimes.indexOf(v); await loadTileAt(v); }
|
||||||
updateCountAndButtons();
|
updateCountAndButtons(); updateSlider();
|
||||||
});
|
});
|
||||||
document.getElementById('tsQuery').addEventListener('click', async ()=>{ const s=fromDTLocalInput(document.getElementById('tsStart').value); const e=fromDTLocalInput(document.getElementById('tsEnd').value); await populateTimes(s,e); });
|
document.getElementById('tsQuery').addEventListener('click', async ()=>{ const s=fromDTLocalInput(document.getElementById('tsStart').value); const e=fromDTLocalInput(document.getElementById('tsEnd').value); await populateTimes(s,e); });
|
||||||
function updateCountAndButtons(){
|
function updateCountAndButtons(){
|
||||||
@ -338,11 +345,33 @@
|
|||||||
prev.disabled = !(N>0 && gCurrentIdx>=0 && gCurrentIdx<N-1);
|
prev.disabled = !(N>0 && gCurrentIdx>=0 && gCurrentIdx<N-1);
|
||||||
next.disabled = !(N>0 && gCurrentIdx>0);
|
next.disabled = !(N>0 && gCurrentIdx>0);
|
||||||
}
|
}
|
||||||
|
function updateSlider(){
|
||||||
|
const slider = document.getElementById('timeSlider');
|
||||||
|
const N = gTimes.length;
|
||||||
|
slider.max = N > 0 ? String(N-1) : '0';
|
||||||
|
if (N > 0 && gCurrentIdx >= 0) {
|
||||||
|
const sliderVal = (N - 1) - gCurrentIdx; // 值越大越新
|
||||||
|
slider.value = String(sliderVal);
|
||||||
|
}
|
||||||
|
slider.disabled = N === 0;
|
||||||
|
}
|
||||||
document.getElementById('btnPrev').addEventListener('click', async ()=>{
|
document.getElementById('btnPrev').addEventListener('click', async ()=>{
|
||||||
if(gTimes.length===0) return; if(gCurrentIdx<0) gCurrentIdx=0; if(gCurrentIdx<gTimes.length-1){ gCurrentIdx++; const dt=gTimes[gCurrentIdx]; document.getElementById('timeSelect').value=dt; await loadTileAt(dt);} updateCountAndButtons();
|
if(gTimes.length===0) return; if(gCurrentIdx<0) gCurrentIdx=0; if(gCurrentIdx<gTimes.length-1){ gCurrentIdx++; const dt=gTimes[gCurrentIdx]; document.getElementById('timeSelect').value=dt; await loadTileAt(dt);} updateCountAndButtons(); updateSlider();
|
||||||
});
|
});
|
||||||
document.getElementById('btnNext').addEventListener('click', async ()=>{
|
document.getElementById('btnNext').addEventListener('click', async ()=>{
|
||||||
if(gTimes.length===0) return; if(gCurrentIdx>0){ gCurrentIdx--; const dt=gTimes[gCurrentIdx]; document.getElementById('timeSelect').value=dt; await loadTileAt(dt);} updateCountAndButtons();
|
if(gTimes.length===0) return; if(gCurrentIdx>0){ gCurrentIdx--; const dt=gTimes[gCurrentIdx]; document.getElementById('timeSelect').value=dt; await loadTileAt(dt);} updateCountAndButtons(); updateSlider();
|
||||||
|
});
|
||||||
|
document.getElementById('timeSlider').addEventListener('input', async (e)=>{
|
||||||
|
const N = gTimes.length; if (N === 0) return;
|
||||||
|
const raw = parseInt(e.target.value, 10);
|
||||||
|
const sliderVal = Math.max(0, Math.min(N-1, isNaN(raw)?0:raw));
|
||||||
|
const idx = (N - 1) - sliderVal; // 反向映射到数组索引(0=最新)
|
||||||
|
if (idx === gCurrentIdx) return;
|
||||||
|
gCurrentIdx = idx;
|
||||||
|
const dt = gTimes[gCurrentIdx];
|
||||||
|
document.getElementById('timeSelect').value = dt;
|
||||||
|
await loadTileAt(dt);
|
||||||
|
updateCountAndButtons();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -41,7 +41,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card mb-4">
|
<div class="card mb-4">
|
||||||
<div class="text-lg font-semibold mb-2">最新 7/40/102 瓦片信息</div>
|
<div class="text-lg font-semibold mb-2">7/40/102 瓦片信息</div>
|
||||||
<div id="tileInfo" class="text-sm space-y-1">
|
<div id="tileInfo" class="text-sm space-y-1">
|
||||||
<div>时间:<span id="dt" class="font-mono"></span></div>
|
<div>时间:<span id="dt" class="font-mono"></span></div>
|
||||||
<div>索引:z=<span id="z"></span> / y=<span id="y"></span> / x=<span id="x"></span></div>
|
<div>索引:z=<span id="z"></span> / y=<span id="y"></span> / x=<span id="x"></span></div>
|
||||||
@ -51,7 +51,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card mb-4">
|
<div class="card mb-4">
|
||||||
<div class="text-lg font-semibold mb-2">最新雷达站气象(南宁雷达站,第八台气象站)</div>
|
<div class="text-lg font-semibold mb-2">雷达站气象(南宁雷达站,第八台气象站)</div>
|
||||||
<div id="rtInfo" class="text-sm grid grid-cols-2 gap-y-1 gap-x-6">
|
<div id="rtInfo" class="text-sm grid grid-cols-2 gap-y-1 gap-x-6">
|
||||||
<div>站点:<span id="rt_alias"></span></div>
|
<div>站点:<span id="rt_alias"></span></div>
|
||||||
<div>位置:<span id="rt_lat"></span>,<span id="rt_lon"></span></div>
|
<div>位置:<span id="rt_lat"></span>,<span id="rt_lon"></span></div>
|
||||||
@ -87,6 +87,10 @@
|
|||||||
<button id="btnNext" class="px-2 py-1 border rounded">下一时次</button>
|
<button id="btnNext" class="px-2 py-1 border rounded">下一时次</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 居中滑动条,联动历史时次(0=最新,值越大越旧) -->
|
||||||
|
<div class="w-full flex justify-center mb-2">
|
||||||
|
<input id="timeSlider" type="range" min="0" max="0" value="0" step="1" class="slider slider-horizontal w-64" />
|
||||||
|
</div>
|
||||||
<div id="radarPlot" class="plot-box"></div>
|
<div id="radarPlot" class="plot-box"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card mt-4" style="width: 100%;">
|
<div class="card mt-4" style="width: 100%;">
|
||||||
@ -151,6 +155,7 @@
|
|||||||
gCurrentIdx = 0;
|
gCurrentIdx = 0;
|
||||||
}
|
}
|
||||||
updateCountAndButtons();
|
updateCountAndButtons();
|
||||||
|
updateSlider();
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -595,6 +600,7 @@
|
|||||||
await loadTileAt(v);
|
await loadTileAt(v);
|
||||||
}
|
}
|
||||||
updateCountAndButtons();
|
updateCountAndButtons();
|
||||||
|
updateSlider();
|
||||||
});
|
});
|
||||||
document.getElementById('tsQuery').addEventListener('click', async ()=>{
|
document.getElementById('tsQuery').addEventListener('click', async ()=>{
|
||||||
const s = fromDTLocalInput(document.getElementById('tsStart').value);
|
const s = fromDTLocalInput(document.getElementById('tsStart').value);
|
||||||
@ -621,6 +627,7 @@
|
|||||||
await loadTileAt(dt);
|
await loadTileAt(dt);
|
||||||
}
|
}
|
||||||
updateCountAndButtons();
|
updateCountAndButtons();
|
||||||
|
updateSlider();
|
||||||
});
|
});
|
||||||
document.getElementById('btnNext').addEventListener('click', async ()=>{
|
document.getElementById('btnNext').addEventListener('click', async ()=>{
|
||||||
if (gTimes.length === 0) return;
|
if (gTimes.length === 0) return;
|
||||||
@ -631,6 +638,31 @@
|
|||||||
await loadTileAt(dt);
|
await loadTileAt(dt);
|
||||||
}
|
}
|
||||||
updateCountAndButtons();
|
updateCountAndButtons();
|
||||||
|
updateSlider();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 滑动条联动
|
||||||
|
function updateSlider(){
|
||||||
|
const slider = document.getElementById('timeSlider');
|
||||||
|
const N = gTimes.length;
|
||||||
|
slider.max = N > 0 ? String(N-1) : '0';
|
||||||
|
if (N > 0 && gCurrentIdx >= 0) {
|
||||||
|
const sliderVal = (N - 1) - gCurrentIdx; // 值越大越新
|
||||||
|
slider.value = String(sliderVal);
|
||||||
|
}
|
||||||
|
slider.disabled = N === 0;
|
||||||
|
}
|
||||||
|
document.getElementById('timeSlider').addEventListener('input', async (e)=>{
|
||||||
|
const N = gTimes.length; if (N === 0) return;
|
||||||
|
const raw = parseInt(e.target.value, 10);
|
||||||
|
const sliderVal = Math.max(0, Math.min(N-1, isNaN(raw)?0:raw));
|
||||||
|
const idx = (N - 1) - sliderVal; // 反向映射到数组索引(0=最新)
|
||||||
|
if (idx === gCurrentIdx) return;
|
||||||
|
gCurrentIdx = idx;
|
||||||
|
const dt = gTimes[gCurrentIdx];
|
||||||
|
document.getElementById('timeSelect').value = dt;
|
||||||
|
await loadTileAt(dt);
|
||||||
|
updateCountAndButtons();
|
||||||
});
|
});
|
||||||
// 兜底加载最新
|
// 兜底加载最新
|
||||||
loadRealtimeLatest().catch(err => {
|
loadRealtimeLatest().catch(err => {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user