NCDR網站示範習作
Table of contents
背景
- 如前所述,預報結果的網頁顯示方式不僅選項繁雜,其中也不少的技術瓶頸。
- 此處即以國家災害防救科技中心天氣與氣候監測網/災害模式/懸浮微粒模式 (NCDR/watch_cmaq)的顯示界面為範例,填充以自家的預報結果圖面,檢討使用到哪些重要程式與設定原則,以了解未來發展之重點方向。
- 以NCDR為習作對象的理由
- 該網頁為一單純的靜態網頁,模擬結果按照日期擺放,日期則由一外部檔案控制,並沒有動態之元件
- 互動方式單純,只有domain、pollutant、日期等3個選項、一個播放即暫停鍵、滑鼠點擊選擇時間等按鍵方式
- 所有程式碼皆可提供下載,並沒有特殊保護
- 實作結果
- 外部網站:http://sinotec24.com/time-bar/、
- 內部網站:http://200.200.31.47/time-bar/
整體作業流程方式
- 預報主機工作站:supermicron工作站2台
- 顯示平台工作站:dec工作站與iMac個人電腦
- 檔案傳輸
- 壓縮檔由預報主機結束作業最後,將檔案傳到顯示平台下載專區備用。
- 以scp傳送。每次傳送量共約147M。
- 傳送時間未固定。3個domain的結果分別約在起始工作後的第3、5.5與7.5小時提交。
網站軟硬體布置
- 顯示平台:
- 內部:dec工作站,使用apache httpd (Apache/2.4.6 (CentOS))
- 外部:iMac Monterey使用 Apache/2.4.55 (Unix)
- 硬碟與儲存需求
- 因減省污染項目,只提供PM2.5之畫面,一天的檔案壓縮約147M。
- 圖檔展開後一天的容量約需57M
- 網路速度
自動執行腳本
- 以crontab控制,每天早晨5/8/11時各上載一次,將下載專區的壓縮檔解開,傳送到網站指定之目錄,以備使用者播放。
- 腳本內容(centos版)
- 基本目錄與當天日期之設定。日期寫進
../php/list_realtime_date_csv
以供js讀取 - 分3層迴圈進行解壓縮
- domain,依序執行d01、d02、d03之解壓縮。如果當天結果還沒有出來,則跳出迴圈。
- 0~7日之迴圈
- 第0天:將模擬0~2天共3天結果搬到指定日期目錄下
- 其他天:前2天結果連結於昨天目錄下的檔案,第3天則放壓縮檔內容
- 各天目錄下之迴圈(0~71),分段方式如上述
#root@node03 /var/www/html/time-bar/pngs
# cat ./pngs2jfy.sh
today=$(date +%Y%m%d)
BEGD=$(date -d "$today -1days" +%Y-%m-%d)
base=/var/www/html/time-bar
root=${base}/00_Wxmap/8F4_CMAQ
cd $base/pngs
today=$(date +%Y%m%d)
echo "WRF2WEEKS_RAIN_date,${today}0800" > ../php/list_realtime_date_csv
for r in 1 2 3;do
gz=/home/kuang/png${r}_${BEGD}.tar.gz
if ! [[ -e $gz ]];then continue;fi
tar xvfz $gz
for d in {0..7};do
YM=$(date -d "${today} +${d}days" +%Y%m)
TD=$(date -d "${today} +${d}days" +%Y%m%d)08
p1=$root/${YM}/${TD}
mkdir -p $p1
if [[ $d > 0 ]];then
# previous day
n=$(( $d - 1 ))
YP=$(date -d "${today} -${n}days" +%Y%m)
TP=$(date -d "${today} +${n}days" +%Y%m%d)08
p0=$root/${YP}/${TP}
fi
ib1=$(( $d * 24 ))
ie1=$(( $ib1 + 48 ))
ib2=$(( $ie1 ))
ie2=$(( $ib2 + 24 ))
if [[ $d == 0 ]];then
for ((i=$ib1;i<$ie2; i+=1));do
iii=$(printf "%03d" $i)
j=$(( $i + 8 ));ymdh=$(date -d "${today} +${j}hours" +%Y%m%d%H)
mv -f ${base}/pngs/PM25_TOT_${iii}.png $p1/PM25_d0${r}_${ymdh}.png
done
else
for ((i=$ib1;i<$ie1; i+=1));do
iii=$(printf "%03d" $i)
j=$(( $i + 8 ));ymdh=$(date -d "${today} +${j}hours" +%Y%m%d%H)
ln -sf ${p0}/PM25_d0${r}_${ymdh}.png $p1/PM25_d0${r}_${ymdh}.png
done
for ((i=$ib2;i<$ie2; i+=1));do
iii=$(printf "%03d" $i)
j=$(( $i + 8 ));ymdh=$(date -d "${today} +${j}hours" +%Y%m%d%H)
mv -f ${base}/pngs/PM25_TOT_${iii}.png $p1/PM25_d0${r}_${ymdh}.png
done
fi
done
done
mac版本腳本
# diff pngs2jfy.sh pngs2jfy.sh_mac
0a1
> BEGD=$(date -v-1d +%Y-%m-%d)
2,5c3
< today=$(date +%Y%m%d)
< BEGD=$(date -d "$today -1days" +%Y-%m-%d)
<
< base=/var/www/html/time-bar
---
> base=/Library/WebServer/Documents/time-bar
9,10c7,8
< today=$(date +%Y%m%d)
< echo "WRF2WEEKS_RAIN_date,${today}0800" > ../php/list_realtime_date_csv
---
> today=$(date -j +%Y%m%d)08
> echo "WRF2WEEKS_RAIN_date,${today}00" > ../php/list_realtime_date_csv
13c11
< gz=/home/kuang/png${r}_${BEGD}.tar.gz
---
> gz=/Users/kuang/png${r}_${BEGD}.tar.gz
16,18c14,16
< for d in {0..7};do
< YM=$(date -d "${today} +${d}days" +%Y%m)
< TD=$(date -d "${today} +${d}days" +%Y%m%d)08
---
> for d in {0..7};do
> YM=$(date -v+${d}d -j -f "%Y%m%d%H" "${today}" +%Y%m)
> TD=$(date -v+${d}d -j -f "%Y%m%d%H" "${today}" +%Y%m%d%H)
24,25c22,23
< YP=$(date -d "${today} -${n}days" +%Y%m)
< TP=$(date -d "${today} +${n}days" +%Y%m%d)08
---
> YP=$(date -v+${n}d -j -f "%Y%m%d%H" "${today}" +%Y%m)
> TP=$(date -v+${n}d -j -f "%Y%m%d%H" "${today}" +%Y%m%d%H)
36,37c34,35
< j=$(( $i + 8 ));ymdh=$(date -d "${today} +${j}hours" +%Y%m%d%H)
---
> ymdh=`date -v+${i}H -j -f "%Y%m%d%H" "${today}" +%Y%m%d%H`
42c40
< j=$(( $i + 8 ));ymdh=$(date -d "${today} +${j}hours" +%Y%m%d%H)
---
> ymdh=`date -v+${i}H -j -f "%Y%m%d%H" "${today}" +%Y%m%d%H`
45,49c43,49
< j=$(( $i + 8 ));ymdh=$(date -d "${today} +${j}hours" +%Y%m%d%H)
---
ymdh=`date -v+${i}H -j -f "%Y%m%d%H" "${today}" +%Y%m%d%H`
html之修改
外觀之修改
- 將banner改成自己的標題與圖面。
- NCDR下拉選單有很多,多數跟空品預報是無關的,予以刪除
- footer有版權範圍說明。這也不需要。
- png容器之修改
- 原網頁在右方有個空白物件,應是排版需求設置的,會妨礙d01全圖之展現,予以刪除。
- 原寬高1000x773,改成1120x650
- 原設定為靠右
float:right;
,改成置中float:center;
時間的控制
- NCDR接收中大的模擬結果一天有2次,而每次實際時間與表列時間有2小時的時間差。
- 修正後以當天8時開始顯示,並不需要時間差
383c239
< document.getElementById('init_id').value=caldatemn(120,listdate);
---
> document.getElementById('init_id').value=caldatemn(0,listdate);
404c260
< document.getElementById('init_id').value=caldatemn(120,initday+'00');
---
> document.getElementById('init_id').value=caldatemn(0,initday+'00');
選單及檔名
- NCDR網頁提供了PM2.5、PM10、O3、SO2、NOx、CO等的選項,此處為求簡化,只保留PM2.5
- 原網頁圖檔的檔名規則中有個’ncdr’檔頭,應為因應未來擴增之需求設計的。此處予以刪除。
444c300
< url='/00_Wxmap/8F4_CMAQ/'+dirlst.substr(0,6)+'/'+dirlst.substr(0,10)+'/ncdr-'+avar+'_'+thislst.substr(0,10)+'.png';
---
> url='00_Wxmap/8F4_CMAQ/'+dirlst.substr(0,6)+'/'+dirlst.substr(0,10)+'/'+avar+'_'+thislst.substr(0,10)+'.png';
結果圖面與檢討
結果比較
- d03 wrf-python加上log-scale讓圖面似乎有較高的內容與解析度
| |
---|
NCDR原網頁 | 修改實作網頁 |
- d02:圖面footer似乎讓整體圖面縮小不少,但提供了模擬日期與當天最大值等重要訊息,只好犧牲圖面顯示的尺寸。
| |
---|
NCDR原網頁 | 修改實作網頁 |
- d01是ncdr沒有展示的圖面資訊。因比例不同,需要較寬範圍才能將色階完全納入。
檢討
$( "#play" ).button({ text: false, icons: { primary: "ui-icon-play" } })
.click(function() { var options; if ( $( this ).text() === "play" ) { options = { label: "pause", icons: { primary: "ui-icon-pause" } }; } else { options = { label: "play", icons: { primary: "ui-icon-play" } }; } $( this ).button( "option", options ); });
播放鍵之重整
- 需要引進jquery、並將播放與停止連到既有的js函數
- 播放與停止2個功能合併在同一個按鍵。
<!-- buttons -->
<script src="//apps.bdimg.com/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="//apps.bdimg.com/libs/jqueryui/1.10.4/jquery-ui.min.js"></script>
<style> #toolbar { padding: 4px; display: inline-block; }
/* support: IE7 */
*+html #toolbar { display: inline; } </style>
<script>
$(function() {
$( "#beginning" ).button({ text: false, icons: { primary: "ui-icon-seek-start" } });
$( "#rewind" ).button({ text: false, icons: { primary: "ui-icon-seek-prev" } });
$( "#play" ).button({ text: false, icons: { primary: "ui-icon-play" } })
.click(function() {
var options;
if ( $( this ).text() === "播放") {
options = { label: "停止", icons: { primary: "ui-icon-pause" } };
if(timelineID) { clearTimeout(timelineID); }
loop_pic();
} else {
options = { label: "播放", icons: { primary: "ui-icon-play" } };
if(timelineID) { clearTimeout(timelineID); }
if(timeID) { clearTimeout(timeID); }
}
$( this ).button( "option", options ); });
$( "#stop" ).button({ text: false, icons: { primary: "ui-icon-stop" } })
.click(function() { $( "#play" ).button( "option", { label: "play", icons: { primary: "ui-icon-play" } }); });
$( "#forward" ).button({ text: false, icons: { primary: "ui-icon-seek-next" } });
$( "#end" ).button({ text: false, icons: { primary: "ui-icon-seek-end" } });
$( "#shuffle" ).button();
$( "#repeat" ).buttonset();
});
</script>
<!-- buttons -->
- 大多數程式碼沿用w3big的提示,只有下述是連到既有的函數
- 重新開始timeout讀秒,時間為500毫秒:
timelineID = setTimeout('loop_pic()',500)
- 呼叫
loop_pic()
循環播放
...
if(timelineID) { clearTimeout(timelineID); }
loop_pic();
...
往前及往後1小時按鍵
- 此處還是沿用NCDR的方向鍵,比較有一致性,大小也比較適中。
- 呼叫新程式
move1_pic()
<div class="product_opt_button_two" id="" style="float:left;margin-left:10px;margin-right:10px;" onclick="move1_pic(-1)"><img src="icon/icon_vleft.png" style="height:100%;"></div>
<div class="product_opt_button_two" id="play" style="float:left;margin-left:5px;margin-right:5px;">播放</div>
<div class="product_opt_button_two" id="" style="float:left;margin-left:10px;margin-right:10px;" onclick="move1_pic(1);"><img src="icon/icon_vright.png" style="height:100%;"></div>
- 需新增js函數來播放前/後小時圖形
- 仿照
loop_pic()
程式的寫法,主要只差在- 是否直接展示圖形(
clickshow_pic
) - 向前播放會遭遇時間小於初始值,此時設為最大值(翻到最後)
- 以及重設timeout。
function move1_pic(i) {
clickshow_pic(curr_ntimeline,60*i);
nt=curr_ntimeline+intval_timetip*i;
nt=nt>start_timetip+(total_timetip-1)*intval_timetip ? start_timetip : nt;
nt=nt<start_timetip?(total_timetip-1)*intval_timetip : nt;
var async_func=async function() {
a=await show_pic(parseInt(nt),0);
}
async_func();
curr_ntimeline=nt;
}
function loop_pic() {
nt=curr_ntimeline+intval_timetip;
nt=nt>start_timetip+(total_timetip-1)*intval_timetip ? start_timetip : nt;
var async_func=async function() {
a=await show_pic(parseInt(nt),0);
}
async_func();
curr_ntimeline=nt;
timelineID = setTimeout('loop_pic()',500)
}
往前及往後1個批次
- 這裡設定一個批次是3天,就是time-bar能夠展示的天數
- 同樣也引用NCDR的前後按鍵,放在時間輸入欄的左右
<div class="product_opt_button_two" id="" style="float:right;margin-left:10px;margin-right:10px;" onclick="move1_init(1);"><img src="icon/icon_vright.png" style="height:100%;"></div>
...
<div class="product_opt_button_two" id="" style="float:right;margin-left:10px;margin-right:10px;" onclick="move1_init(-1)"><img src="icon/icon_vleft.png" style="height:100%;"></div>
- 新增函數
move1_init()
,來增減起始日、按一下增減3天 - 物件
initid
的值是10碼的年月日時,這在change_init()
中沒有用到,但因為與圖檔的目錄名稱有關,也需要一併更新。
function move1_init(a) {
initday=document.getElementById('initid').value;
if (initday.length==10) {
if(timelineID) { clearTimeout(timelineID); }
document.getElementById('init_id').value=caldatemn(a*72*60,initday+'00');
document.getElementById('initid').value=caldatehr(a*72,initday);
timelineUsercontral_min('timeitem_'+curr_ntimeline,'init_id',1,intval_minutes);
}
}
function change_init() {
initday=document.getElementById('initid').value;
if (initday.length==10) {
if(timelineID) { clearTimeout(timelineID); }
document.getElementById('init_id').value=caldatemn(0,initday+'00');
timelineUsercontral_min('timeitem_'+curr_ntimeline,'init_id',1,intval_minutes);
}
}
shift 批次之修正
- 前述一個批次72小時的切換,在連續播放的動態情況下會使程式馬上停止,而且日期也不能連續。並不符合操作的直覺。
- 此處修正成timeframe只切換24小時值,讓timeline游標時間保持不動、游標位置則視所在timeframe而移動,這樣的好處是
- 圖面及游標的日期不會因為按下左、右鍵就跳動,不會讓使用者有措手不及的突兀感。
- 連續按向右鍵三次,就有連續播放10天的功能。
- 播放不會因為timeframe更換就停頓、不需要重啟
- 需要解決的問題:
- current time在切換檔案目錄時已被修改過,需要扣回來。
- 在每個目錄下,timeframe的起訖時間重新定義過,因此有可能current time會超出範圍,須予以限定最大、最小可能值。否則不會出現圖面(檔案或日期不存在)。
- 如果正在播放中,須保持繼續播放。可以由
document.getElementById('play')
的console.log
內容,找到對應的標籤存放函數(.title
)。經由按鍵顯示的是”停止”,來確認使用者正目前在播放中。
function move1_init(a) {
initday=document.getElementById('initid').value;
if (initday.length==10) {
if(timelineID) { clearTimeout(timelineID); }
document.getElementById('init_id').value=caldatemn(a*24*60,initday+'00');
document.getElementById('initid').value=caldatehr(a*24,initday);
curr_ntimeline += - a*24
nt=curr_ntimeline
end_timetip=start_timetip+(total_timetip-1)*intval_timetip
nt=nt>end_timetip ? end_timetip : nt;
nt=nt<start_timetip ? start_timetip : nt;
curr_ntimeline = nt
timelineUsercontral_min('timeitem_'+curr_ntimeline,'init_id',1,intval_minutes);
// console.log(document.getElementById('play').title);
if( document.getElementById('play').title === "停止") { loop_pic(); }
}
}
- 似乎不再呼叫
clearTimeout(timelineID)
也可以達到一樣的功能。
其他調整
- 因日期跳來跳去,回不到當天的日期。而日期欄左邊的鉛筆小圖似乎沒有作用,將其用來連結到
load_initial(listdate);
函數,將畫面予以重設到'php/list_realtime_date_csv?v=CHART_NCDR_CMAQ'
的內容。 - 因增加了好幾個功能鍵,同時又有污染項目及domain的下拉選單,畫面太擠了放不進,將其移到右上方。
- 新增各個按鍵物件的中文說明hover description(title)。
完成圖面