kml 是 Google Earth 標示圖標的副檔名,其格式其實就是 xml。
但 Google Earth 自創一些標籤,如 Placemark, StyleMap 等標籤,所以在Python中,就有 pykml 這套件將所有Google Earth的所有標籤包裝起來,而且包含了 html 的所有標籤,方便我們撰寫 kml 檔案。
使用pykml 前,請先 pip install pykml。
kml標籤
.kml 檔案的最頂層標籤(tag) 為 <kml>,然後是 <Document> 標籤。<Document>標籤的一開始會有 <StyleMap> 及 <Style> 等標籤記錄<Icon>的來源。正確格式如下
<kml ......>
<Document>
<name>111年台北市346分隊坑洞案件</name>
<StyleMap id="pothole">
</StyleMap>
<Style id="pothole">
pass
</Style>
<Folder>
<Placemark>
<Point>
<coordinates>經度, 緯度, 0</coordinates>
</Point>
</Placemark>
</Folder>
</Document>
</kml>
原始碼為 makestyle.py檔,如下
from pykml.factory import KML_ElementMaker as KML def makestyle(): kml = KML.kml() doc = KML.Document( KML.name("111年台北市346分隊坑洞案件"), KML.StyleMap( KML.Pair( KML.key("normal"), KML.styleUrl("#pothole_h0") ), KML.Pair( KML.key("highlight"), KML.styleUrl("#pothole_h1") ), id="pothole", ), KML.StyleMap( KML.Pair( KML.key("normal"), KML.styleUrl("#hot_h0") ), KML.Pair( KML.key("highlight"), KML.styleUrl("#hot_h1") ), id="hot", ), KML.StyleMap( KML.Pair( KML.key("normal"), KML.styleUrl("#finish_h0") ), KML.Pair( KML.key("highlight"), KML.styleUrl("#finish_h1") ), id="finish", ), KML.Style( KML.IconStyle( KML.scale(1.3), KML.Icon( KML.href("http://ip/mapicons/ticon4.png"), ), ), KML.hotSpot(x="20", y="2", xunits="pixels", yunits="pixels", xuints="pixels"), id="pothole_h0", ), KML.Style( KML.IconStyle( KML.scale(1.3), KML.Icon( KML.href("http://ip/mapicons/ticon3.png"), ), ), KML.hotSpot(x="20", y="2", xunits="pixels", yunits="pixels", xuints="pixels"), id="hot_h0", ), KML.Style( KML.IconStyle( KML.scale(1.3), KML.Icon( KML.href("http://ip/mapicons/ticon2.png"), ), ), KML.hotSpot(x="20", y="2", xunits="pixels", yunits="pixels", xuints="pixels"), id="finish_h0", ), KML.Style( KML.IconStyle( KML.scale(1.3), KML.Icon( KML.href("http://ip/mapicons/ticon4.png"), ), ), KML.hotSpot(x="20", y="2", xunits="pixels", yunits="pixels", xuints="pixels"), id="pothole_h1", ), KML.Style( KML.IconStyle( KML.scale(1.3), KML.Icon( KML.href("http://ip/mapicons/ticon3.png"), ), ), KML.hotSpot(x="20", y="2", xunits="pixels", yunits="pixels", xuints="pixels"), id="hot_h1", ), KML.Style( KML.IconStyle( KML.scale(1.3), KML.Icon( KML.href("http://ip/mapicons/ticon2.png"), ), ), KML.hotSpot(x="20", y="2", xunits="pixels", yunits="pixels", xuints="pixels"), id="finish_h1", ) ) kml.append(doc) return kml, doc
todo
主檔
主檔的工作是查詢資料庫,取得資料後轉成 DataFrame格式,方便後面的存取。然後將資料傳入 makenode函數,製作 Placemaker標籤。最後將整個 kml 樹儲存到硬碟檔案中。
from pykml.factory import KML_ElementMaker as KML from lxml import etree from makenode import makenode from makestyle import makestyle import datetime import mysql.connector as mysql import pandas as pd kml, doc=makestyle() conn = mysql.connect(host="ip", user="帳號", password="密碼", database="資料庫") cmd = f"SELECT * FROM table where 條件" cursor = conn.cursor() cursor.execute(cmd) rs = cursor.fetchall() descriptions = cursor.description columns = [] for d in descriptions: columns.append(d[0]) data = [] for r in rs: data.append(list(r)) df = pd.DataFrame(data=data, columns=columns) rs = df[["經度", "緯度", "巡查案號", "損壞類別", "地址","日期","地區", "完工日期", "道管編號"]] areas=['中正區','萬華區','內湖區','南港區','大安區','文山區'] for area in areas: f1=KML.Folder(KML.name(area)) doc.append(f1) f_ok = KML.Folder(KML.name("已完成")) f1.append(f_ok) rs_areas=rs[rs['地區']==f'{area}'] currentDay = datetime.datetime.now().strftime('%Y-%m-%d') dates = pd.date_range(start="2022-01-01", end=currentDay) for d in dates.values: d=str(d).split("T")[0] yyyy=int(d.split("-")[0]) mm=int(d.split("-")[1]) dd=int(d.split("-")[2]) rs_day=rs_areas[rs_areas['日期']==datetime.date(yyyy,mm,dd)] if len(rs_day)>0: f_no=KML.Folder(KML.name(d)) f1.append(f_no) for r in rs_day.values: lng = r[0] lat = r[1] address = r[4] patrol_no=r[2] finishDay=r[7] manager_no=r[8] makenode(f_no, f_ok, area, d, address, lng, lat, patrol_no, finishDay, manager_no) file="111年台北市346分隊坑洞案件" with open(f'{file}.kml', 'wb') as output: output.write(etree.tostring(kml, pretty_print=True, encoding='utf-8'))
上述藍色部份 etree.tostring(kml, pretty_print=True)是為了完美列印字串用的,可以讓xml有換行,縮排的效果,讓人更容易閱讀。
makenode.py
makenode函數主要是製作 Placemark標籤
from pykml.factory import KML_ElementMaker as KML import requests def makenode(f_no, f_ok, area, caseday, address, lng, lat, patrol_no, finishDay, manager_no): ip="自已指定"
placemark = KML.Placemark(KML.name(patrol_no)) r1 = requests.get(f"http://{ip}/photo/finish/{manager_no}_1.png") r2 = requests.get(f"http://{ip}/photo/finish/{manager_no}_2.png") if "404" in str(r1): finish = False folder=f_no else: finish = True folder=f_ok if "熱燙" in address: type = "#hot" elif finish: type = "#finish" else: type = "#pothole" styleurl=KML.styleUrl(type) folder.append(styleurl) point=KML.Point( KML.coordinates(f"{lng}, {lat}, 0") ) if finish and finishDay is None: fd = "無完工資料" elif finishDay is not None: fd = f"{finishDay}" else: fd = f"尚未完工" yyy = int(caseday.split("-")[0]) - 1911 mm = caseday.split("-")[1] dd = caseday.split("-")[2] description=KML.description() table=KML.table( KML.tr( KML.td(f"巡查案號 : {patrol_no}", width="50%"), KML.td(f"道管編號 : {manager_no}", width="50%"), ), KML.tr( KML.td(f"查報時間 : {caseday}", width="50%"), KML.td(f"完工日期 : {fd}", width="50%"), ), KML.tr( KML.td("報案種類 : 坑洞", width="50%"), KML.td(f"案件地點 : {address.split('_')[0]}", width="50%"), ), KML.tr( KML.td("施工前照片", colspan="2", bgcolor="#ffff00", align="center", height="30"), ), KML.tr( KML.td( KML.a( KML.img( src=f"http://{ip}/photo/photos/{area}/{yyy}{mm}{dd}/{address}_far.png", width="640", height="480" ), href=f"http://{ip}/photo/photos/{area}/{yyy}{mm}{dd}/{address}_far.png", ), colspan="2", align="center" ), ), border="1", cellspacing="0", cellpadding="0", width="650" ) if "404" not in str(r1): tr=KML.tr( KML.td("施工後照片", colspan="2", bgcolor="#00ff00", align="center", height="30"), ) table.append(tr) tr= KML.tr( KML.td( KML.a( KML.img( src=f"http://{ip}/photo/finish/{manager_no}_1.png", width="640", height="480" ), href=f"http://{ip}/photo/finish/{manager_no}_1.png", ), colspan="2", align="center" ), ) table.append(tr) if "404" not in str(r2): tr= KML.tr( KML.td( KML.a( KML.img( src=f"http://{ip}/photo/finish/{manager_no}_2.png", width="640", height="480" ), href=f"http://{ip}/photo/finish/{manager_no}_2.png", ), colspan="2", align="center" ), ) table.append(tr) description.append(table) placemark.append(styleurl) placemark.append(point) placemark.append(description) folder.append(placemark)
todo