iT邦幫忙

2022 iThome 鐵人賽

DAY 6
0
AI & Data

氣象食材系列 第 6

[ Day 6] 基本觀測資料-氣象站資料4 (xml)

  • 分享至 

  • xImage
  •  

前面3篇都是說明讀取歷史資料,那如果想要即時觀測的資料呢?
那就可以利用氣象局opendata網站

建議大家可以去申請一組帳號,因為接下來介紹的資料大概都是從開方資料下載。
相關透過API下載的方式在網路上都有許多參考文章,我就不贅述了。

這一篇主要說明xml的讀取方式,主要使用基本的xml.etree.ElementTree來處理。
其實有xmltodict的套件可以使用,可能會簡單多XD。

登入氣象局opendata後,下載自動氣象站-氣象觀測資料(xml格式。)
https://ithelp.ithome.com.tw/upload/images/20220920/20150923VTEqFylCIQ.png

下載完成後,檔案名稱應該是O-A0001-001.xml。
用記事本或者網頁方式打開,如下圖所示(擷取部分)。
https://ithelp.ithome.com.tw/upload/images/20220920/20150923JRTs1SPsmP.jpg

我們可以看到是類似html的格式,所以就屬於標籤格式類的資料儲存方式。
稍微解釋一下,每一個向下的灰色三角形代表的是一個區塊的標籤標記
第一層是cwbopendata,再這一層之後,還有第二層location,其它以此類推。
那每一個有屬於該層的訊息及後一層的標籤標記。
以location層為例,location層的直接資訊有lat、lon等,而下一層的標籤標記有time、weatherElement、parameter。
https://ithelp.ithome.com.tw/upload/images/20220920/20150923WccG0JTbo4.png

所以,今天要將資料抓出來顯示,就類似一層一層剝洋蔥的方式將資料萃取。

接下來以程式碼實作
先import xml.etree.ElementTree,然後把最基本的根抓出來,

import xml.etree.ElementTree as ET
import pandas as pd
trees = ET.parse('O-A0001-001.xml')
roots = trees.getroot()

對根物件進行基本的處理,看最基本的節點標記

for child in roots:
    print(child.tag)

結果如圖所示(擷取部分),對照上面xml檔案內容,可以看到最基本的節點標記是第一層cwbopendata的直接資訊及下一層的location標記。由於已經先看到我們需要的資料都在location層,因此直接對location進行處理。
https://ithelp.ithome.com.tw/upload/images/20220920/20150923ia33IOJ0DJ.png

由上述的結果可以觀察到一件事,選擇某一層標記時,會僅回傳該層的直接資訊與下一層的標記,所以選擇location時,可以知道會回傳該層的直接資訊lat, lon, lat_wgs84, lon_wgs84, loctionName, stationId及下一層的標記time, weatherElement, parameter。來實證一下,我們使用根結點直接抓取location標記的方法(iter),然後把該層的直接資訊及下一層的標記資訊print出來顯示,就如下圖所示。

https://ithelp.ithome.com.tw/upload/images/20220920/20150923sHcoRtwJHF.jpg

那要如何取得資料呢?
如果是該層資料的話,只要用location層及該層資訊配合text方法即可。
如下程式,在每個location層中,取該層的lat標記,然後使用text方法

那如果是location的下一層呢?
假設取weatherElement層,這一層的直接資訊有elementName及下一層有elementValue標記
如果是該層的直接資訊,就取第0個位置後,然後使用text方法,這邊要加個0是因為相對location來說,weatherElement已經是下一層的標記位置,而weatherElement有一個直接資訊elementName,所以對location來說,elementName的位置是weatherElement的第0個位置。

所以簡單來說,該層的直接資訊不用加第0個位置資訊。

###第一段
for locationtags in roots.iter("{urn:cwb:gov:tw:cwbcommon:0.1}location"):
    for xx in locationtags.iter("{urn:cwb:gov:tw:cwbcommon:0.1}lat"):
        print(xx.text)

###第二段
for locationtags in roots.iter("{urn:cwb:gov:tw:cwbcommon:0.1}location"):
    for xx in locationtags.iter("{urn:cwb:gov:tw:cwbcommon:0.1}weatherElement"):
        print(xx[0].text)
        for xxin in xx.iter("{urn:cwb:gov:tw:cwbcommon:0.1}elementValue"):
            print(xxin[0].text)

由上述的取直說明,開始寫成函式,如果是以location為基準的狀況下,就是以一個測站為基準,

loc_element = ['{urn:cwb:gov:tw:cwbcommon:0.1}time',
               '{urn:cwb:gov:tw:cwbcommon:0.1}stationId',
               '{urn:cwb:gov:tw:cwbcommon:0.1}locationName',
               '{urn:cwb:gov:tw:cwbcommon:0.1}lon',
               '{urn:cwb:gov:tw:cwbcommon:0.1}lat',
               '{urn:cwb:gov:tw:cwbcommon:0.1}lon_wgs84',
               '{urn:cwb:gov:tw:cwbcommon:0.1}lat_wgs84',
               '{urn:cwb:gov:tw:cwbcommon:0.1}weatherElement',
               '{urn:cwb:gov:tw:cwbcommon:0.1}parameter',
              ] #將location 該層直接資訊及下一層標記寫成list
              
def onestn(lociter, elements):
    """
    lociter代表針對根的location的iter,即roots.iter("{urn:cwb:gov:tw:cwbcommon:0.1}location")
    以此份資料來說,一個location代表一個測站。
    elements代表location的該層資訊及下一層標記。
    """
    valu, header = [],[]  #儲存數值及檔頭用
    cnt = 1
    itercol = [ loctag.iter(eachtag) for eachtag in elements] #先對每個element進行iter
    
    for eachiter, eachelement in zip(itercol,elements):
        for elements in eachiter:
            if "weatherElement" not in eachelement and "parameter" not in eachelement and "time" not in eachelement:
                header.append(eachelement.split("}")[-1])
                valu.append(elements.text)
            else:
                for qqinin in elements:
                    if  "weatherElement" in eachelement:
                        for xxin in qqinin.iter("{urn:cwb:gov:tw:cwbcommon:0.1}elementName"):
                            if cnt == 1:                            
                                header.append(xxin.text)
                        for xxin in qqinin.iter("{urn:cwb:gov:tw:cwbcommon:0.1}elementValue"):
                            valu.append(xxin[0].text)
                    elif "parameter" in eachelement:
                        for xxin in qqinin.iter("{urn:cwb:gov:tw:cwbcommon:0.1}parameterName"):
                            if cnt ==1:
                                header.append(xxin.text)
                        for xxin in qqinin.iter("{urn:cwb:gov:tw:cwbcommon:0.1}parameterValue"):
                            valu.append(xxin.text)
                    else:
                        header.append(eachelement.split("}")[-1])
                        valu.append(qqinin.text)
    cnt = 2
    return header, valu

將上述的函式待配所有資料及pandas 應用,這樣就可以完成一個觀測資料表囉。

packvalue = []
for qq in roots.iter("{urn:cwb:gov:tw:cwbcommon:0.1}location"):
    ch, eachvalue = onestn(qq,loc_element)
    packvalue.append(eachvalue)

df = pd.DataFrame(packvalue,columns=ch)
print(df)

簡單畫個圖

import matplotlib.pyplot as plt
import os
os.environ["PROJ_LIB"] = "PATH to your shared proj directory" 
# For example D:\ithome\Miniconda3\envs\pltenv\Library\share\proj"
from mpl_toolkits.basemap import Basemap
import matplotlib.colors as mcolors

cwb_Tcolor=['#117388','#207E92','#2E899C','#3D93A6','#4C9EB0','#5BA9BA','#69B4C4','#78BFCE','#87CAD8',\
                '#96D4E2','#A4DFEC','#B3EAF6','#0C924B','#1D9A51','#2FA257','#40A95E','#51B164','#62B96A',\
                '#74C170','#85C876','#96D07C','#A7D883','#B9E089','#CAE78F','#DBEF95','#F4F4C3','#F7E78A',\
                '#F4D576','#F1C362','#EEB14E','#EA9E3A','#E78C26','#E07B03','#ED5138','#ED1759','#AD053A',\
                '#780101','#9C68AD','#845194','#8520A0']
clevs = list(range(-1,39))
clevs.insert(0,-10)
cmaps = mcolors.ListedColormap(cwb_Tcolor,'temp')
norms = mcolors.BoundaryNorm(clevs, cmaps.N)
hh = list(df)
print(hh)
lat = df["lat"].astype(float).values
lon = df["lon"].astype(float).values
tempature = df["TEMP"].astype(float).values
obstime = df["time"]

plt.figure(figsize=(16,12))
m = Basemap(projection='merc',urcrnrlat=26, llcrnrlat=21.5, llcrnrlon=117.5, urcrnrlon=122.5 ) 
m.readshapefile("D:\ithome\/tw_shp\COUNTY_MOI_1090820","tw_shp")
sct = m.scatter(lon, lat, c=tempature, cmap=cmaps,norm=norms, latlon=True)
plt.colorbar(sct)
plt.title(obstime[0],fontsize=18)
plt.show()

https://ithelp.ithome.com.tw/upload/images/20220920/201509230BYjVfEDrw.png


上一篇
[ Day 5]基本觀測資料-氣象站資料3 (簡易爬蟲)
下一篇
[ Day 7] 基本觀測資料-雷達資料 (xml)
系列文
氣象食材30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言