在OpenTopoMap上貼上NCL等值圖

 

背景

  • 本項作業取代傳統SURFER、NCL、或dat2kmlcsv2kml等繪圖方式,以python程式讀取高斯煙流模式結果(plotfile、格式詳下),運作NCL並讀取結果檔,與Open TopoMap地圖進行疊加,並以CaaS形式對外提供遠端計算服務。
    1. 正確、迅速取得(切割)指定範圍、指定解析度之OSM/OTM底圖,並進行整併。
    2. 應用NCL程式製作報告品質之等值線圖
    3. 將此二者予以疊加、以網頁連結形式提供使用者下載。

重要元件

  • 開放地形圖(Open TopoMap)為開放街道圖專案項目之一
    • 其線上服務位置:https://opentopomap.org/#map=5/49.000/10.000,url命令列#後的三個數字分別是放大倍率(zoom)、緯度及經度。臺灣則為:https://opentopomap.org/#map=8/23.5338/120.9265
    • OpenTopoMap 可用於嵌入到個別的項目中,圖磚可從{a b c}.tile.opentopomap.org/{z}/{x}/{y}.png圖檔取得。
    • 此處之作業乃將其下載至本地目錄,貼上NCL圖檔,並於CGI服務中提供予使用者。
  • NCAR命令語言(NCL)
    • 為美國大氣研究中心提供之高精繪圖套件,除一般使用外,也常使用在學術論文報告中。
    • 此處應用其繪製高斯模式之濃度輸出等值線圖。
    • 為典型之等值線搭配png底圖,其他搭配構想詳等濃度圖類型與比較

其他方案之比較

  • 數位板專案中曾經用到Open Map系列的Open TopoMap,該專案是套用Leaflet套件,將底圖在網頁中置換成地形圖(如範例),並不是真正輸出高解析度之圖檔。
  • dat2kmlcsv2kml作業結果為google Map可以讀取的kml檔案,該方案也是以網頁呈現為主(如範例),因google Map的版權限制,並不適合輸出成圖檔製作成報告。

過去製圖方式之檢討

  • 底圖的截取:
    • 從網頁畫面、使用剪取工具直接就所要的範圍進行截取
      • 無法得到指定座標的精確範圍,須嘗試錯誤
      • 解析度有限(截圖只有72dpi、列印另存pdf檔可達300dpi),對高品質要求製圖(>1000dpi)則無法配合
    • 使用OSMOSIS
      • 可以就經緯度範圍(box or polygon)進行切割、數個檔案可以再進行整併(merge)
      • 如要較高解析度之地圖,要先全部下載再行切割。
      • 除了命令列方式之外,也有GUI版本OSMembrane
      • 如果是較大範圍的地圖,整併時會發生問題,需要較專業的merge方式,
      • 在小範圍、接近垂直座標系統的情況下,直接montage拼接較快速方便,也不會有顏色失真的問題(詳merged_GeoTIFF)。
    • 用使Pbftoosmosmconvert(都沒有macOS版本)
    • 直接動態下載:(詳merged_GeoTIFF)
      • 與Leaflet或其他網頁存取方式相同
      • 隨時更新圖資內容,與時俱進
      • 下載後另存快取,提升後續存取速度
  • SURFER:
    • 好處:
      • 互動式作業方式、也有模版可以使用、容易上手、品質符合水準
      • 也能貼上png或其他格式之地圖
    • 沒有linux/macOS版本
    • 自動化方面雖有VBA程式、然目前仍無法與其他unix系統相容
    • 底圖的georeferencing須自行(另行)輸入4個頂點的座標
    • 因為不是程式化、自動化的作業方式,修改範圍、解析度時,將難以因應。
  • NCL
    • 高品質、可貼在地形圖(另一等值圖)上、作業可程式化、自動化
    • 網友提到NCL貼中文字的作法。但也有網友提問無法正確顯示的問題,即使能順利顯示還是得一個個貼。
    • 最大缺點就是不能貼raster底圖,對於目前已有的google map、OSMOTM資源而言,為非常可惜之限制。
  • dat2kml.py
    • 具有regrid功能:不論非等間距網格、離散點、程式會依據數據範圍重新進行內插、並輸出等間距的網格結果(.grd)檔
    • 雖然在命令列、在CaaS方面,都能提供圖檔的計算,與OSM/OTM/google map搭配顯示,也有動態縮放平移的功能,在偵錯階段提供檢核的能力。
    • 然而無法提供報告品質之圖檔,解析度不足以列印。
    • google Map無法在等值線上標註數字

煙流模式結果的PLT格式(PLOTFILE

  • 高斯煙流模式(isc及aermod)可以輸出PLT檔供後續製圖使用,其表頭及順序如下aermod結果範例(官方使用說明):
    • 前8行為模式說明文字,特徵為:
      • 第一字元為星號*、
      • 第一個字為模式名稱,可能是AERMOD或ISCST。這2個模式PLT檔案主要差異在AERMOD多插入了1欄(ZHILL)。
      • SURFER可以略過這些文字
      • python讀取時要先以字元讀入,刪除前8行,再進行欄位解讀
      • NCL會失控。必須先刪除(如使用sed),才能進行NCL指令asciiread
    • 其後先變動X(東西方向)、再變動Y(南北)方向
      • 格點數不存在於文字檔內、(建議)須另行解讀
      • 方向由西南 → 東南 → …(北移),最後到東北角
* AERMOD ( 19191): A Simple Example Problem for the AERMOD Model with PRIME                03/10/21
* AERMET ( 15181):                                                                          05:36:19
* MODELING OPTIONS USED:  NonDFAULT  CONC  FLAT  RURAL  MMIF_Data
*        PLOT FILE OF  HIGH  1ST HIGH  1-HR VALUES FOR SOURCE GROUP: ALL   
*        FOR A TOTAL OF  5000 RECEPTORS.
*        FORMAT: (3(1X,F13.5),3(1X,F8.2),3X,A5,2X,A8,2X,A5,5X,A8,2X,I8)                                                                                                                                                 
*        X            Y      AVERAGE CONC    ZELEV    ZHILL    ZFLAG    AVE    GRP      RANK    NET ID  DATE(CONC)
* ____________  ____________  ____________  ______  ______  ______  ______  ________  ________  ________  ________
  132700.00000 2595400.00000      0.00008    34.00    34.00    0.00    1-HR  ALL        1ST    GD2      20012620
  133700.00000 2595400.00000      0.00013    34.00    34.00    0.00    1-HR  ALL        1ST    GD2      20012620
  134700.00000 2595400.00000      0.00020    34.00    34.00    0.00    1-HR  ALL        1ST    GD2      20012620
  135700.00000 2595400.00000      0.00026    34.00    34.00    0.00    1-HR  ALL        1ST    GD2      20012620
…
  181200.00000 2644900.00000      0.00001    34.00    34.00    0.00    1-HR  ALL        1ST    GD3      20031710
  181700.00000 2644900.00000      0.00001    34.00    34.00    0.00    1-HR  ALL        1ST    GD3      20031710
  182200.00000 2644900.00000      0.00001    34.00    34.00    0.00    1-HR  ALL        1ST    GD3      20031710

實例

NCLonOTM.png
遠端計算服務:在OpenTopoMap上貼上高斯煙流模式之NCL等值圖

CaaS成果及運作

輸入說明:

  • (一) 切割OpenTopoMap服務:輸入RE GRIDCART範圍設定
    • 在空格內至少要輸入6個數字,數字的內容、順序及意義如下表所示(ISC/AERMOD之受體點RE GRIDCART設定方式),其他考量檢討如下:
    • 直角座標系統的必要性
      • 由於煙流模式必須用直角座標系統來設定接收點及排放口位置,不接受經緯度系統
      • NCL雖然可以接受經緯度、但網格定義仍然是用等間距系統
      • 雖然各式地圖之引用以經緯度為主,但在此階段還是要用直角座標系統輸入較為方便
    • 在此使用TWD97(2度分帶)系統,符合內政部地圖系統的要求(與一般模式模擬作業之習慣)

RE GRIDCART範圍設定參數說明

項目 設定內容(範例) 說明
1. 290700 原點x座標,單位為m(TWD97系統)
2. 40 東西向格點數、整數
3. 1250 東西向格點間距(m)
4. 2746400 原點y座標,單位為m(TWD97系統)
5. 40 南北向格點數、整數
6. 1250 南北向格點間距(m)
  • (二)製作等值線圖服務(輸入高斯模式模擬結果.PLT檔案、或regrid後的.grd檔)
    • 檔案範例:.PLT檔案.grd檔
    • 必須是單一網格系統、此處的NCL程式並不會重新進行regrid
      • 目前NCL程式尚不接受加入離散點、
      • X、Y方向的間距「必須」相等(dx==dy)
      • 可接受不同方向的格點數不同(nx!=ny)
    • 因為煙流模式目前尚無法區分網格系統或離散點,也無法將其分別輸出成PLT檔,如果檔案含離散點可行因應的方式包括:
      1. 先執行dat2kml產生.grd(文字)檔,
      2. 刪除煙流模式中有關其他網格系統、離散點的設定、重新執行煙流模式。
      3. 另在python程式內做regard,輸出等間距網格的結果。
      4. 修改NCL程式、在NCL程式內進行regrid(未嘗試)
  • 按下執行鍵(Run and Revise Links)

輸出檔案

項目 服務 結果 範例
1. 切割OpenTopoMap 依使用者要求範圍、解析度之OTM地形圖 fitted.png
2. 製作等值線圖服務 同一範圍解析度之OTM地形圖 fitted.png
- - NCL等值線圖(不含底圖) 固定檔名tmp_cn.png
- - 疊圖結果 固定檔名NCLonOTM.png

OTM檔案下載、合併與切割( tiles_to_tiffFit.py

本項工作的目標是得到指定範圍、指定解析度之OTM檔案,可以給SURFER或其他繪圖軟體使用。詳細參閱詳merged_GeoTIFF及筆記之說明,此處說明搭配的原則與注意事項:

(一)引數個數與內容的應用

  • 在CaaS的cgi_python中,tiles_to_tiffFit.py有2種被呼叫的可能情況:網格設定字串、或直接給予一個PLT檔案。因此必須彈性設計引數的個數:
  • 按照引數個數是否小於6作為判斷
    • 6以下,讀取使用者提供的PLT檔案,從當中讀取西南、東北位置的座標
    • 至少6個數字,讀取使用者提供的字串,從當中計算出網格範圍西南、東北位置的座標
  • 再將這2個位置座標轉成經緯度(TWDLL)
    44    larg=len(sys.argv)
    45    if larg<6:
    46        fname=sys.argv[1]
    47        with open(fname,'r') as f:
    48            ll=[l for l in f]
    49        ll=ll[8:]
    50        X,Y=([float(l.split()[i]) for l in ll] for i in [0,1])
    51        mnX,mnY,mxX,mxY=(min(X),min(Y),max(X),max(Y))   
    52    else:
    53        mnX,nx,dx,mnY,ny,dy=(float(sys.argv[i]) for i in range(larg-6,larg))
    54        mxX,mxY=mnX+nx*dx,mnY+ny*dy
    55    lat_min,lon_min=TWD2LL(mnX,mnY)
    56    lat_max,lon_max=TWD2LL(mxX,mxY)

(二)縮放比例(zoom)的決定

  • OSM/OTM的拼塊儲存位置、名稱、與其位置的定義,都是按照縮放比例決定的,zoom必須先決定,才能正確下載檔案。
  • 縮放比例、下載檔案個數,也與裁切後的解析度(dpi)、像素矩陣大小有關、亦即與地圖的品質、檔案大小有關。
    • 太密:檔案太大、下載處理的速度變慢、等值圖須放大的比例太大,會出現斷線、無線條的狀況,降低圖的品質,只能重做。
    • 太疏:地圖無法辨識細節,也只能重做
  • 為維持等值圖與地圖有接近的像素大小,地圖至少須20幅以上、但也必須控制在150幅以下。
    33    zoomi = 22 #initial zoomming level
...
  137    for zoom in range(zoomi,5,-1):
  138      x_min, x_max, y_min, y_max = bbox_to_xyz(
  139        lon_min, lon_max, lat_min, lat_max, zoom)
  140      ntiles=(x_max - x_min + 1) * (y_max - y_min + 1)
  141      if 150 > ntiles > 20:break
  142    print(f"Downloading {(x_max - x_min + 1) * (y_max - y_min + 1)} tiles")

(三)圖檔的管理

  • Png
    • 雖然OSM/OTM的更新速度很快,沒有必要儲存在工作站,但如果在嘗試錯誤期間,同一地區地圖不斷重複下載,似乎也沒有必要。
    • 此處修改原程式的設計,先測試是否已經有過去下載過的檔案,如果沒有,才真的進行下載。依然暫時儲存在temp目錄下。
    • 執行裁切後,原程式內設是刪除temp目錄下所有檔案,在此修改成備份到工作站某處儲存備用。
  • tif
    • 原程式是將結果檔案另存在output目錄之下,如此就檔案性質分類自然是為了方便檔案管理。
  • cgi_python每次呼叫會開一個暫存目錄(cntr_????),output似乎沒有必要存在,為減少目錄的複雜性,在此取消output,將裁切結果直接存在cntr_????下即可。
    28    store_dir = os.path.join(os.path.dirname(__file__), '../pngs')
    29    temp_dir = temp'
...
  101    def download_tile(x, y, z, tile_server):
  102        url = tile_server.replace(
  103            "{x}", str(x)).replace(
  104            "{y}", str(y)).replace(
  105            "{z}", str(z))
  106        store = f'{store_dir}/{x}_{y}_{z}.png'
  107        path = f'{temp_dir}/{x}_{y}_{z}.png'
  108        if os.path.exists(store):
  109            os.system('ln -sf '+store+' '+path)   
  110            return(path)
  111        if "street" in tile_server:
  112            os.system(wget+url+' -O '+path)
  113        else:
  114            urllib.request.urlretrieve(url, path)
  115        os.system('cp '+path+' '+store)   
  116        return(path)

(四)合併(merge)或拼接(montage)

  • 原程式是使用gdal_merge.py這支程式進行下載圖檔的合併。
  • 衛星圖像變色的問題與解決
    • OSM/OTM每個圖檔的顏色種類個數不同,合併時會按照第一個圖片的色譜來解讀後續圖檔,因此合併後全變成黑白版(以達成最大公約數)。
    • 解決方案
      1. 使用OSMosis(未執行)
      2. 使用montage拼接
        • 唯一要修改檔案命名方式,原本x-y-z的檔名,轉變成z-y-x,以利程式按照各圖所在位置直接拼接
        • 拼接後還要進行座標之設定(gdalwarp)
        • -t_srs:Set target spatial reference.
        • -to:Set a transformer option suitable to pass to GDALCreateGenImgProjTransformer2() - 使用批次檔mtg.cs
$ cat mtg.cs
#$1=X1,$2=X2,$3=Y1,$4=Y2
X1=$1
X2=$2
Y1=$3
Y2=$4
Z=$5
for x in `seq $X1 $X2`; do for y in `seq $Y1 $Y2`; do ln -sf ${x}_${y}_${Z}.tif ${Z}_${y}_${x}.tif;done;done
/usr/local/bin/montage -mode concatenate -tile "$((X2-X1+1))x" "${Z}_*.tif" ../merged_montage.tif >&/dev/null
cd ..
/opt/anaconda3/envs/env_name/bin/gdalwarp -t_srs "+proj=longlat +ellps=WGS84" -to DST_METHOD=NO_GEOTRANSFORM merged.tif merged_montage.tif

(五)圖檔格式的選擇

  • 原程式是設計讓圖檔自帶座標資料,因此使用GeoTiff格式
  • 如果NCL的等值線是按照座標繪製、地圖也是按照座標裁切,二者疊圖是再沒有需要知道座標值或任何座標系統相關資訊,tiff似無必要
  • 經比較tiff的RGB色譜與png格格不入,縮放、疊圖時,都會發生變色的情形,
  • 結論就是不必再維護tiff檔、利用Imagmagic的convert指令,將裁切結果都轉成png檔

(六)切割Open TopoMap png檔服務之結果

  • 給定範圍解析度(RE GRIDCART)STR: 290700 40 1250 2746400 40 1250
  • 檔案大小:3.7MB
  • 像素矩陣: (1464, 1457)

NCL等值線製圖(PLT_cn.ncl)

本項工作的目標是得到像素較高的等值線圖,使用NCL的程式PLT_cn.ncl。使用NCL的理由:

  • 品質為科學期刊所認可、軟體持續精進更新、備援及範例豐富
  • Linux/macos相容、自動化作業
  • 出圖像素可控制

(一)矩陣大小的判定

  • NCLasciiread指令可以接受未知維度、長度的檔案,但是要在NCL程式內判斷檔案X軸及Y軸的格點數,因為沒有set()指令,困難度太高了、只能放棄
  • cgi_python內呼叫os.system(“echo”)指令,可以將RE GRIDCART字串設定參數寫在文字檔案param.txt內(只有用到網格數nx,ny)
  • NCL程式內讀入維度、宣告檔案及變數的陣列大小、可以順利將矩陣讀入
kuang@114-32-164-198 /Users/Data/GIS/OSM_20210318/merged_GeoTIFF
$ cat -n PLT_cn.ncl
    1    ;----------------------------------------------------------------------
    2    ; WRF_cn_1.ncl
    3    ;
…
    17      nxy=asciiread("param.txt",(/6/),"integer")
    18      nx=nxy(1)
    19      ny=nxy(4)
...
    22      f=asciiread("tmp.PLT",(/ny*nx,3/),"float”)
…
    28      p=new((/ny,nx/),float)
    29      do j=0,ny-1
    30      do i=0,nx-1
    31        ji=j*nx+i
    32        p(j,i)=f(ji,2)
    33      end do
    34      end do

(二)圖名的傳遞

  • 同樣運用檔案來傳遞圖名訊息
...
    20      t=asciiread("title.txt",1,"string")
    21      tit=t(0)
...
    49      res@tiMainString          = tit            ; main title
...

(三)等值圖的像素

  • 在疊圖過程中,需要保持底圖與等值線圖有接近的解析度,如此二者失真的情況可以降到最低。
  • 縮放地圖:因地圖是格柵圖、不宜縮放。
  • 縮放等值圖:雖然NCL不能輸出向量檔,但因為等值線相對單純、微小的縮放尚能接受。
  • 唯一需要控制像素
    • wks_type函數內控制,在此抓整個圖約2000像素、圖框內約控制在1500左右
    • 此處XY方向保持一樣(如有需要可以按照nx/ny比例修改)
    41    ;---Create simple contour plots 
    42      wks_type = "png"
    43      wks_type@wkWidth = 2000
    44      wks_type@wkHeight = 2000
    45      wks = gsn_open_wks(wks_type,"tmp_cn")

(四)等值線的粗細

  • 由於等值線要在複製的地圖上表現清楚,必須加粗。
  • nLineThicknessF變數控制,內設為1。
    50      res@cnLineThicknessF      =7

(五)輸出結果

  • 檔名內設為tmp_cn.png,詳範例

疊圖(NCLonOTM.py)

本項作業的重點是在尋找等值圖上找到圖框的位置、框外保持不變、框內空白處置換成底圖的像素即可。圖框的定義及作法:

  1. 在X或Y方向有最多的黑色像素、
  2. 運用np.where來協助尋找
  3. 解決2個圖像素不同的問題
  4. 解決NCL等值線不夠黑的問題
  5. 逐一判斷、置換
  6. 存檔離開

(一)圖框位置的判定

  • 圖框位置會因為nx,ny的大小、比例、圖名的長度字型等而改變,雖然其數字差異不大,但為精確,還是用程式將其定位較佳。
  • 先求出各方向非為白色的像素個數(nwidth,nheight)
  • 依序取最高、次高位置,如果二者一樣,表示軸線黑色像素完全一樣,np.where全部囊括,只要判斷序列的大小就知道起訖了。
  • 由於軸線的寬度至少有3點像素,用abs()來解決(approximate)。
#kuang@114-32-164-198 /Users/Data/GIS/OSM_20210318/merged_GeoTIFF
#$ cat -n NCLonOTM.py
    1    #!/opt/anaconda3/envs/env_name/bin/python
...
    11    image=cv2.imread('tmp_cn.png')
    12    ny,nx,nz=image.shape
    13   
    14    #locate the frame of contour plot
    15    nwidth=np.array([len(np.where(image[j,:,:]!=255)[0]) for j in range(ny)])
    16    nheight=np.array([len(np.where(image[:,i,:]!=255)[0]) for i in range(nx)])
    17    wh={'j':'nwidth','i':'nheight'}
    18    nxy={'j':'ny','i':'nx'}
    19    for ij in wh:
    20      n=nxy[ij]
    21      exec('id1=int(np.mean(np.where('+wh[ij]+'>=max('+wh[ij]+')-2)[0]))')
    22      exec('id2=int(np.mean(np.where('+wh[ij]+'==max(['+wh[ij]+'[i] for i in range('+n+') if abs(i-id1) > 2]))))')
    23      if id1==id2:
    24        exec('id1=np.min(np.where('+wh[ij]+'>=max('+wh[ij]+')-2)[0])') 
    25        exec('id2=np.max(np.where('+wh[ij]+'>=max('+wh[ij]+')-2)[0])') 
    26      exec(ij+'0=min([id1,id2])')
    27      exec(ij+'1=max([id1,id2])')
    28    size = (j1-j0,i1-i0)

(二)圖框內線條加黑

  • NCL為求品質,線條數字可能帶有陰影、漸層、灰階,在縮放、疊圖時會不清楚,此處全部將其塗黑。
  • 邏輯就是RGB 3者相同,卻不是白色RGB=(255,255,255))
  • 不是白色,即設成全黑
    30    #darken the lines inside of the frame
    31    image3=image[:]
    32    boo=(image[:,:,0]==image[:,:,1]) & (image[:,:,1]==image[:,:,2])& (image[:,:,2]!=255)
    33    idx=np.where(boo)
    34    idx0=np.array([idx[0][i] for i in range(len(idx[0])) if idx[0][i]>j0 and idx[0][i]<j1 and idx[1][i]>i0 and idx[1][i]<i1])
    35    idx1=np.array([idx[1][i] for i in range(len(idx[0])) if idx[0][i]>j0 and idx[0][i]<j1 and idx[1][i]>i0 and idx[1][i]<i1])
    36    if len(idx0)>0 and len(idx1)>0:image3[idx0,idx1,:]=0

(三)等值線圖縮放

  • 此處控制等值線圖可以放大、縮小,但縮小不能超過30%。
  • 使用cv2.resize函數進行縮放
  • 圖框原點也要縮放
    38    #resize (alignment the resolutions)
    39    image2=cv2.imread('fitted.png')
    40    ny2,nx2,nz=image2.shape
    41    if ny2<size[0] and size[0]/ny2>1.3 :sys.exit('make a more detail basemap')
    42    rate=(ny2/size[0],nx2/size[1])
    43    size2=(int(rate[0]*ny),int(rate[1]*nx))
    44    im_resized = cv2.resize(image3,size2, interpolation=cv2.INTER_CUBIC)
    45    image3=np.array(im_resized)
    46    j0,i0=int(j0*rate[0]),int(i0*rate[1])

(四)空白處置換成底圖

  • 由於前面已經將等值線加黑(RGB=(0,0,0))了,因此判斷式就是黑色處跳開、非黑處置換成底圖
  • 只有圖框內的像素需要置換、圖框外的軸線、文字、保持不變
  • 存檔離開
    48    #replace the nodata location with OSM plot
    49    for j in range(j0,j0+ny2):
    50      for i in range(i0,i0+nx2):
    51        if (image3[j,i,:]==0).all():continue 
    52    #    if sum(image3[j,i,:])>=200+255:
    53        image3[j,i,:]=image2[j-j0,i-i0,:]
    54    cv2.imwrite("NCLonOTM.png",image3)

NCLonOTM.html

  1. 設計成可以接受字串與檔案輸入。右側有成品範例、下側有設定數字意義表格,詳實例或前圖。
  2. IO部分設計可以接受RE GRIDCART字串、以及開啟本地檔案(filepicker)
  3. 呼叫cgi_python、傳遞字串或檔案

cgi_python(NCLonOTM-cgi.py)

主要工作就是接受html的呼叫,將RE GRIDCART座標設定內容、或檔案內容傳遞到前述3項作業。

(一)解析輸入檔案的副程式

    26  def PLT_parser(fname):
    27    with open(fname,'r') as f:
    28      l=[line.strip('\n') for line in f]
    29    if l[0][0]=='*':l=l[8:]
    30    X,Y=([float(l[i].split()[j]) for i in range(len(l))] for j in range(2))
    31    sX ,sY=list(set(X)),list(set(Y))
    32    sX.sort()
    33    sY.sort()
    34    nx,ny=len(sX),len(sY)
    35    dx,dy=[round(sX[i+1]-sX[i],3) for i in range(nx-1)],[round(sY[i+1]-sY[i],3) for i in range(ny-1)]
    36    if len(set(dx))!=1 or len(set(dy))!=1:
    37      fname=pth+fn
    38      print """not a regular RE GRIDCART system, sorry! your input is:
    39      <a data-auto-download href="%s">%s</a>
    40          """  % (fname.replace(WEB,'../../../'),fname.split('/')[-1])
    41      sys.exit('not a regular RE GRIDCART system')
    42    return ' %d %d %d %d %d %d ' % (int(min(X)),nx,int(dx[0]),int(min(Y)),ny,int(dy[0]))

(二)按照輸入字串呼叫FIT程式

  • 確認RE GRIDCART字串是否存在任何內容(長度4以上)
    15  FIT='/Users/Data/GIS/OSM_20210318/merged_GeoTIFF/tiles_to_tiffFit.py '
...
    48  form = cgi.FieldStorage()
    49  STR = str(form.getvalue("iscinp"))
    50  os.system('echo "'+STR+'"'+OUT)
    51  if len(STR)>=4: #in case of input a string
    52    cmd ='cd '+pth+';'
    53    cmd+= FIT+STR+OUT+';'
    54    os.system('echo "'+cmd+'"'+OUT)
    55    r=os.system(cmd+OUT)
    56    if r!=0:sys.exit('error in ncl')
    57    fnames=['fitted.png']

(三)將regridded的SURFER grd檔案寫成tmp.PLT (X,Y,C)格式

  • 檔案類型的判斷依據是在檔案的第一行、第一個字串必須是“DSAA”,才是.grd檔案
  • 使用網友提供的load_surfer模組
  • 寫出時要注意python2還沒有{:f}格式,必須符合工作站cgi-python的版本進行調修
    58  else:
    59    fileitem = form['filename']
    60    if fileitem.filename:
    61      fn = os.path.basename(fileitem.filename)
    62      open(pth+fn, 'wb').write(fileitem.file.read())
    63      with open(pth+fn, 'r') as f:
    64        ll=[l.strip('\n') for l in f]
    65      if ll[0]=='DSAA':
    66        x, y, c, (ny, nx) = load_surfer(pth+fn)
    67        with open(pth+'tmp.PLT','w') as f:
    68          for j in range(ny):
    69            for i in range(nx):
    70              f.write('%f %f %f\n' % (x[j,i],y[j,i],c[j,i]))

(四)如是PLOTFILE格式,還需要進行SED去行、CUT選欄位

  • tmp.PLT檔案整理好之後就可以進行FIT程式
    71      elif ll[0].split()[1] in ['AERMOD','ISCST3']:
    72        cmd ='cd '+pth+';'
    73        if pth+fn!=pth+'userinp.PLT': cmd+='cp '+pth+fn+' '+pth+'userinp.PLT;'
    74        cmd+= SED+'userinp.PLT'+CUT+';'
    75        os.system('echo "'+cmd+'"'+OUT)
    76        r=os.system(cmd+OUT)
    77      else:
    78        print 'wrong format! '+ll[0]
    79        sys.exit('wrong format')
    80      cmd ='cd '+pth+';'
    81      cmd+= FIT+' tmp.PLT '+OUT+';'
    82      os.system('echo "'+cmd+'"'+OUT)
    83      r=os.system(cmd+OUT)

(五)執行NCL程式與疊圖

  • 準備好座標設定字串內容
  • 依序執行NCL製圖、再與前述FIT準備好的底圖進行疊加(OVL即前述NCLonOTM.py)
    16  OVL='/Users/Data/GIS/OSM_20210318/merged_GeoTIFF/NCLonOTM.py'
...
    85      STR=PLT_parser(pth+'tmp.PLT')
    86      cmd ='cd '+pth+';'
    87      cmd+='echo "'+STR+'">param.txt;'
    88      cmd+='echo "'+fn+'">title.txt;'
    89      cmd+= NCL+OUT+';'
    90      cmd+= OVL
    91      os.system('echo "'+cmd+'"'+OUT)
    92      r=os.system(cmd+OUT)
    93      if r!=0:sys.exit('error in ncl')

(六)輸出結果檔案

    94      fnames=['fitted.png', 'tmp_cn.png', "NCLonOTM.png"]
    95      descip={ 'fitted.png':'OpenTopoMap: ',
    96          'tmp_cn.png':'CLN_contour: ',
    97          'NCLonOTM.png':'contour post on OTM: '}
    98
    99  for fn in fnames:
   100    fname=pth+fn
   101    print """\
   102    %s<a data-auto-download href="%s">%s</a></br>
   103    """  % (descip[fn],fname.replace(WEB,'../../../'),fname.split('/')[-1])
   104  print """\
   105    </body>
   106    </html>
   107    """

Reference

  • Jimmy Utterström, Generate merged GeoTIFF imagery from web maps (xyz tile servers) with Python, merged_GeoTIFF
  • wiki, “OpenTopoMap ( opentopomap.org ) 是一個旨在從 OSM 和SRTM數據中渲染地形圖的項目。”
  • wiki, 通用网关接口, “在電腦領域,通用閘道器介面 (英語:Common Gateway Interface,CGI) 是為提供網路服務而執行控制台應用 (或稱命令列介面)的程式,提供於伺服器上實現動態網頁的通用協定。通常情況下,一次請求對應一個CGI 指令碼的執行,生成一個 HTML。簡而言之,一個 HTTP POST 請求,從客戶端經由 標準輸入 傳送資料到一個CGI 程式。同時攜帶其他資料,例如 URL 路徑, HTTP頭欄位資料,被轉換為行程的環境變數。”
  • NCL, “NCAR Command Language Programs”