AERMAP之準備 Table of contents 背景 下載數值地形資料 內政部 內政部地政司定期公開其調查結果在政府資料開放平台 ,如”2020年版全臺灣及部分離島20公尺網格數值地形模型DTM資料 “。下載、解壓縮後可以得到一GeoTiff檔案,將其更名為taiwan2020.tiff備用。 不分幅檔案可能較大,但處理還算順暢。 內政部亦有5M解析度地形數據,需另行向主管單位申請,並未在政府資料開放平台 提供。 目前尚未有金門縣與連江縣數據,需另由其他來源取得。 EIO EIO(elevation) 為pypi上的公開程式,會連結到NASA及NGA所維護的地形數據庫(SRTM 30m Global 1 arc second V003 )以及CGIAR-CSI所維護的 SRTM 90m Digital Elevation Database v4.1。由於為全球性質,因此包括所有離島與境外其他國家範圍。索取範圍(--bounds
),為西南到東北角之經緯度,範例如下(向外擴張20倍間距) llmin = pnyc ( xmin - 2000. * dx / 100 - Xcent , ymin - 2000 * dx / 100. - Ycent , inverse = True ) #long/lati
llmax = pnyc ( xmax + 2000. * dx / 100 - Xcent , ymax + 2000 * dx / 100. - Ycent , inverse = True )
smax = str ( llmax [ 0 ]) + ' ' + str ( llmax [ 1 ]) #long/lati
smin = str ( llmin [ 0 ]) + ' ' + str ( llmin [ 1 ]) + ' ' + smax
雖然是動態連結下載,程式可以將原始數據(cache檔)儲存至指定目錄,如下次有下載需求時,就不會重複下載檔案。如下例將cache儲存在(--cache_dir
)/tmp/gdal目錄 須指定環境變數GDAL_DATA之位置 下載結果檔案為一GeoTiff檔案。由於已經指定範圍,下載後就不必另行切割 ran = tf . NamedTemporaryFile (). name . replace ( '/' , '' ). replace ( 'tmp' , '' )
tmp = '/tmp/gdal' #_'+ran
pth1 = '/opt/anaconda3/bin/'
pth2 = '/opt/anaconda3/envs/ncl_stable/bin/'
eio = '/opt/anaconda3/bin/eio'
gd_data = ';export PATH=' + pth1 + ':' + pth2 + ':$PATH;GDAL_DATA=/opt/anaconda3/envs/py37/share/gdal '
TIF , DEM , NUL = fname + '.tiff' , last + '.dem' , ' >>' + dir + 'geninp.out'
cmd = 'cd ' + dir + gd_data + eio + ' --cache_dir ' + tmp + ' clip -o ' + TIF + ' --bounds ' + smin + NUL
os . system ( 'echo "' + cmd + '"' + NUL )
os . system ( cmd )
gen_inp.py 引數 GDNAME Xinit Xnum Xdelta Yinit Ynum YdeltaGDNAME:網格系統名稱(自行命名,將用做產生檔案之filename ROOT) Xinit及Yinit為西南角落之TWD97座標(m) Xnum及Ynum為x/y方向的點數(整數),全部共Xnum*Ynum點 Xdelta及Ydelta為x/y方向的間距(m),以正值為宜,但不限定。 自由格式,不須逗號,不接受全形或中文碼 範例 gd3 277500. 50 100. 2776500. 50 100. 輸入檔案 輸出檔案 修改過後的輸入檔aermap.inp 檔名為gd3(範例)為首之6個檔案gd3.kml→模擬範圍的等高線,彙入google map-my map作圖 gd3_re.dat→文字檔。以DISCCART方式標示每一格點的地形高程,併入iscst3的控制檔(.INP)內,取代原有XYINC設定 gd3_TG.txt→文字檔。iscst3所需要的地形網格外部檔案,須在控制檔(.INP)內設定TG INPUTFIL gd3_TG.txt,以進行連結。第1行:Xnum Ynum Xinit Yinit Xend Yend Xdelta Ydelta (Xend/Yend為東北角座標) 第2行→第1個Y值,自西到東X方向所有點之高程, 第3行→第2個Y值,自西到東X方向所有點之高程, … gd3.tiff→作DEM檔案時使用。可以用QGIS檔GIS程式檢視。 或以tif2kml.py檢視tif2kml.py -f gd3.tiff 結果檔為gd3.tiff.kml gd3.dem→文字檔。aermap所需數值地形之外部檔案。aermap輸入檔案格式中較容易處理者(格式詳DEM )。 aermap執行結果aermap.out:回饋輸入數據及錯誤訊息 MAPDETAIL.OUT:地圖的細節 MAPPARAMS.OUT:確認DEM的正確性 DOMDETAIL.OUT:如四角未落在DEM檔內,檢查範圍的設定 gd3.REC:此檔將輸入AERMOD 模式中,設定地形及特徵山高 gen_inp.py程式下載 修改呼叫程式之路徑 gdal_translate 內設為/opt/anaconda3/envs/ncl_stable/bin/ 環境變數GDAL_DATA內設為GDAL_DATA=/opt/anaconda3/envs/py37/share/gdal AERMAP 執行檔 gen_inp.py程式分段說明 調用模組 因計算等濃度線,調用了cntr
模組。 AERMAP 使用的是UTM系統,因此需用到utm 模組,在台灣地區不適用,需另安裝。台灣地區使用twd97 。絕對座標轉換使用utm及twd97,相對座標批次轉換,還是使用pyproj的Proj比較方便快速。tiff的讀寫,使用rasterio ,基本指令及應用詳見筆記 說明。 import numpy as np
from pandas import *
import twd97 , utm
from scipy.interpolate import griddata
#python3 -m pip install --index-url https://github.com/matplotlib/legacycontour.git legacycontour
import legacycontour._cntr as cntr
import bisect
import sys , os
import tempfile as tf
from pyproj import Proj
import rasterio
from rasterio.transform import Affine
from cntr_kml import cntr_kml
內政部dtm檔案之讀取、切割、內插與轉存 讀取:使用rasterio ,因南北向y軸是由北向南、(x0,y0)
為左上方座標,需進行轉置 矩陣用np.flip
、序列用.sort()
#read the 20M DTM data TIFF from https://dtm.moi.gov.tw/2020dtm20m/台灣本島及4離島\(龜山島_綠島_蘭嶼_小琉球\).7z
fname = 'taiwan2020.tif'
img = rasterio . open ( fname )
data = np . flip ( img . read ()[ 0 ,:,:],[ 0 ])
x0 , y0 = img . xy ( 0 , 0 )
mn = img . shape
dxm , dym = ( img . bounds . right - img . bounds . left ) / img . width , - ( img . bounds . top - img . bounds . bottom ) / img . height
x1d = np . array ([ x0 + dxm * i for i in range ( mn [ 1 ])])
y1d = np . array ([ y0 + dym * i for i in range ( mn [ 0 ])])
y1d . sort ()
切割用bisect 定位所需座標範圍 向外再擴張2格40M 將高程矩陣、座標值x及y線性化,準備進行內插 I1 = bisect . bisect_left ( x1d , x_mesh [ 0 ]) - 2 ; I2 = bisect . bisect_left ( x1d , x_mesh [ - 1 ]) + 2
J1 = bisect . bisect_left ( y1d , y_mesh [ 0 ]) - 2 ; J2 = bisect . bisect_left ( y1d , y_mesh [ - 1 ]) + 2
c = data [ J1 : J2 , I1 : I2 ]. flatten ()
c = np . where ( c < 0 , 0 , c )
x = np . array ([ x1d [ I1 : I2 ] for j in range ( J2 - J1 )]). flatten ()
y = np . array ([[ j for i in range ( I2 - I1 )] for j in y1d [ J1 : J2 ]]). flatten ()
內插AERMAP 應有內插功能,此處內插是為ISC模式與結果展示所需。DEM與接受點的網格系統都一樣,在AERMAP 內也可提高計算速度。因一般20M的解析度較需求為高,採用線性內插即可 #interpolation from points to the receptor grid (x_g, y_g)
points = [( i , j ) for i , j in zip ( x , y )]
grid_z2 = griddata ( points , c , ( x_g , y_g ), method = 'linear' )
#save tiff file
TIF , DEM , NUL = fname + '.tiff' , last + '.dem' , ' >>' + dir + 'geninp.out'
os . system ( 'cp template.tiff ' + TIF )
resx , resy = ( np . max ( lon ) - np . min ( lon )) / ( nx + 2 * M - 1 ),( np . max ( lat ) - np . min ( lat )) / ( ny + 2 * M - 1 ),
transform = Affine . translation ( np . min ( lon ), np . max ( lat )) * Affine . scale ( resx , - resy )
new_dataset = rasterio . open ( TIF , 'w' , driver = 'GTiff' , height = grid_z2 . shape [ 0 ], width = grid_z2 . shape [ 1 ], count = 1 ,
dtype = grid_z2 . dtype , crs = '+proj=latlong' , transform = transform ,)
data = np . flip ( grid_z2 ,[ 0 ])
new_dataset . write ( data , 1 )
new_dataset . close ()
gdal_translate TIF , DEM , NUL = fname + '.tiff' , last + '.dem' , ' >>' + dir + 'geninp.out'
# convert the tiff to dem
llSE = str ( lon [ 1 , - 2 ]) + ' ' + str ( lat [ 1 , - 2 ])
llNW = str ( lon [ - 2 , 1 ]) + ' ' + str ( lat [ - 2 , 1 ]) + ' ' + llSE
pth1 = '/opt/anaconda3/bin/'
pth2 = '/opt/anaconda3/envs/ncl_stable/bin/'
gd_data = ';export PATH=' + pth1 + ':' + pth2 + ':$PATH;GDAL_DATA=/opt/anaconda3/envs/py37/share/gdal'
os . system ( 'echo "before gdal"' + NUL )
gd = 'gdal_translate'
cmd = 'cd ' + dir + gd_data + gd + ' -of USGSDEM -ot Float32 -projwin ' + llNW + ' ' + TIF + ' ' + DEM + NUL
os . system ( 'echo "' + cmd + '"' + NUL )
os . system ( cmd )
aermap.inp之改寫與執行 aermap.inp 為aermap控制檔案之模版,此檔案會加入指定範圍之參數與接受點座標DATAFILE:aermap的結果檔,輸出給aermod使用(.REC) DOMAINXY:接受點的UTM範圍,確認邊界角落都在DEM檔案範圍內 ANCHORXY:接受點自訂座標值(台灣地區建議直接使用twd97座標系統)與UTM間錨定點的對照關係。 xy:UTM之(x,y)
值,2維矩陣。因旋轉後可能有些歪斜,因此需取最大範圍 uxanc,uyanc:錨定點(此處取模擬範圍的中心點,以避免誤差累計)的UTM值 #generate aermap.inp,
xy = utm . from_latlon ( lat , lon )
uxmn , uxmx = int ( np . min ( xy [ 0 ][ M : - M , M ])), int ( np . max ( xy [ 0 ][ M : - M , - M ]))
uymn , uymx = int ( np . min ( xy [ 1 ][ M , M : - M ])), int ( np . max ( xy [ 1 ][ - M , M : - M ]))
co , an , z = ' DOMAINXY ' , ' ANCHORXY ' , ' 51 '
UTMrange = co + str ( uxmn ) + ' ' + str ( uymn ) + z + str ( uxmx ) + ' ' + str ( uymx ) + z
xmid , ymid = ( xmin + xmax ) / 2. , ( ymin + ymax ) / 2.
llanc = pnyc ( xmid - Xcent , ymid - Ycent , inverse = True )
uxanc , uyanc = utm . from_latlon ( llanc [ 1 ], llanc [ 0 ])[ 0 : 2 ]
UTMancha = an + str ( int ( xmid )) + ' ' + str ( int ( ymid )) + ' ' + str ( int ( uxanc )) + ' ' + str ( int ( uyanc )) + z + '0'
#change the contain of aermap
text_file = open ( "aermap.inp" , "r+" )
d = [ line for line in text_file ]
keywd = [ i [ 3 : 11 ] for i in d ]
ifile = keywd . index ( 'DATAFILE' )
idmxy = keywd . index ( 'DOMAINXY' )
ianxy = keywd . index ( 'ANCHORXY' )
ihead = keywd . index ( 'ELEVUNIT' )
iend = d . index ( 'RE FINISHED \n ' )
text_file = open ( "aermap.inp" , "w" )
x0 , y0 = xmin , ymin
sta = 'RE GRIDCART ' + last + ' STA \n '
args [ 0 ] = last
STR = 'RE GRIDCART {:s} XYINC {:s} {:s} {:s} {:s} {:s} {:s} \n ' . format ( * args )
s = [ sta , STR , sta . replace ( 'STA' , 'END' )]
for l in range ( ihead + 1 ):
# if l == ifile:
# text_file.write( "%s" % ' DATAFILE '+DEM+'\n')
if l == idmxy :
text_file . write ( "%s" % UTMrange + ' \n ' )
elif l == ianxy :
text_file . write ( "%s" % UTMancha + ' \n ' )
else :
text_file . write ( "%s" % d [ l ])
for l in range ( len ( s )):
text_file . write ( "%s" % s [ l ])
for l in range ( iend , len ( d )):
text_file . write ( "%s" % d [ l ])
text_file . close ()
# execute the aermap
aermap_path = './'
os . system ( aermap_path + 'aermap >& isc.out' )
AERMAP之編譯 USEPA 官網程式壓縮檔中提供了window版本的gfortran(32及64位元)與ifort編譯批次檔,可供參考。 linux/macOS者編譯並沒有特別需求,只需先將main1.mod
、tifftags.mod
等2個檔案產生出來,以便其他程式的引用。 gfortran 編譯選項設定 set COMPILE_FLAGS = -fbounds-check -Wuninitialized -static -O2
set LINK_FLAGS = -static -O2
ifort 編譯選項設定 kuang@125-229-149-182 /Users/1.PlumeModels/AERMOD/aermap/src
$ head intel-aermap.bat
ifort mod_main1.f /compile_only /O3 /Qipo /Qprec-div- /QaxPT /traceback
ifort mod_tifftags.f /compile_only /O3 /Qipo /Qprec-div- /QaxPT /traceback
ifort aermap.f /compile_only /O3 /Qipo /Qprec-div- /QaxPT /traceback
ifort sub_calchc.f /compile_only /O3 /Qipo /Qprec-div- /QaxPT /traceback /assume:byterecl
ifort sub_chkadj.f /compile_only /O3 /Qipo /Qprec-div- /QaxPT /traceback
...
其他處理 KML檔案之輸出 result = cntr_kml ( grid_z2 , lon , lat , fname )
複雜地形ISC模式所需輸入檔 因GeoTiff檔案提供了較大的範圍,實際輸出時回歸正確範圍[M:-M,M:-M]
使用簡單的with open及write指令,將高程寫進檔案中re.dat:接受點位置及高程 TG.txt:高程網格數據檔 # generate the ISC files
if M > 0 :
grid_z2 = grid_z2 [ M : - M , M : - M ]
x_g , y_g = x_g [ M : - M , M : - M ], y_g [ M : - M , M : - M ]
xy = np . array ([[( i , j ) for i , j in zip ( x_g [ k ], y_g [ k ])] for k in range ( ny )])
with open ( fname + '_re.dat' , 'w' ) as f :
f . write ( 'RE ELEVUNIT METERS \n ' )
for j in range ( ny ):
for i in range ( nx ):
f . write ( 'RE DISCCART ' + str ( xy [ j , i , 0 ]) + ' ' + str ( xy [ j , i , 1 ]) + ' ' + str ( grid_z2 [ j , i ]) + ' \n ' )
#terrain grid file
with open ( fname + '_TG.txt' , 'w' ) as f :
f . write ( str ( nx ) + ' ' + str ( ny ) + ' ' + str ( xmin ) + ' ' + str ( xmax ) + ' ' + str ( ymin ) + ' ' + str ( ymax ) + ' ' + str ( dx ) + ' ' + str ( dy ) + ' \n ' )
for j in range ( ny ):
ele = [ str ( int ( grid_z2 [ j , i ])) for i in range ( nx )]
st = ele [ 0 ]
for i in range ( 1 , nx ):
st += ' ' + ele [ i ]
f . write ( st + ' \n ' )
Reference