Link Search Menu Expand Document

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
  • 網路速度
    • 內部:1GB/s
    • 外部:10M/s

自動執行腳本

  • 以crontab控制,每天早晨5/8/11時各上載一次,將下載專區的壓縮檔解開,傳送到網站指定之目錄,以備使用者播放。
  • 腳本內容(centos版)
  • 基本目錄與當天日期之設定。日期寫進../php/list_realtime_date_csv以供js讀取
  • 分3層迴圈進行解壓縮
    1. domain,依序執行d01、d02、d03之解壓縮。如果當天結果還沒有出來,則跳出迴圈。
    2. 0~7日之迴圈
      • 第0天:將模擬0~2天共3天結果搬到指定日期目錄下
      • 其他天:前2天結果連結於昨天目錄下的檔案,第3天則放壓縮檔內容
    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版本腳本

  • mac上的date指令與linux有別
# 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沒有展示的圖面資訊。因比例不同,需要較寬範圍才能將色階完全納入。

檢討

  • 播放鍵
    • 播放跟暫停應該是同一個鍵,分成2個鍵不太直覺
    • 缺少往前播放的倒帶鍵,而必須以滑鼠點選特定時刻,如果只是需要往回1~2個小時,對滑鼠動作來說太辛苦(小時刻度太小)
    • 可以參考w3big.com jQuery UI 實例– 按鈕(Button)工具欄
  • 日期選擇
    $( "#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()程式的寫法,主要只差在
    1. 是否直接展示圖形(clickshow_pic)
    2. 向前播放會遭遇時間小於初始值,此時設為最大值(翻到最後)
    3. 以及重設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;
}
  • 原版的loop_pic()
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);
   }
}
  • 原版的change_init
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而移動,這樣的好處是
    1. 圖面及游標的日期不會因為按下左、右鍵就跳動,不會讓使用者有措手不及的突兀感。
    2. 連續按向右鍵三次,就有連續播放10天的功能。
    3. 播放不會因為timeframe更換就停頓、不需要重啟
  • 需要解決的問題:
    1. current time在切換檔案目錄時已被修改過,需要扣回來。
    2. 在每個目錄下,timeframe的起訖時間重新定義過,因此有可能current time會超出範圍,須予以限定最大、最小可能值。否則不會出現圖面(檔案或日期不存在)。
    3. 如果正在播放中,須保持繼續播放。可以由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)也可以達到一樣的功能。

其他調整

  1. 因日期跳來跳去,回不到當天的日期。而日期欄左邊的鉛筆小圖似乎沒有作用,將其用來連結到load_initial(listdate);函數,將畫面予以重設到'php/list_realtime_date_csv?v=CHART_NCDR_CMAQ'的內容。
  2. 因增加了好幾個功能鍵,同時又有污染項目及domain的下拉選單,畫面太擠了放不進,將其移到右上方。
  3. 新增各個按鍵物件的中文說明hover description(title)。

完成圖面