KML 全稱為 Keyhole Markup Language,是一種基於 XML 的空間資料格式。它除了可以呈現點、線、面等幾何圖像外,還可以連結網頁、圖片等媒體資訊。
由於最初是由 Google 旗下的 Keyhole 公司所提出,所以廣泛地使用在 Google Earth 與 Google Maps 等產品。而 Google 在 2008年將此檔案標準移轉給 OGC (開放地理資訊系統協會),所以目前也已經是被廣泛接納與使用的空間資料格式之一。
下方是擷取自 Google KML Reference 網站的圖片,主要說明一個 KML 檔案所包含的結構。
相較於昨天介紹的 GeoJSON,KML 似乎明顯複雜許多,但好在有 Maps Utility,讓這個格式在 Maps SDK for Android 上不至於太困難。
練習使用的檔案是來自環境部環境地理資訊服務平台的機車定檢站位置圖。
如果您想下載這個平台上的其他圖資來練習也可以,不過要注意一下該圖資使用的座標系統,以及它可提供的檔案格式是否有支援 KML。
檔案下載下來預設會是一個 zip 檔,解壓縮出來後會是 kmz 檔。
KMZ 檔其實也是一個透過 zip 壓縮的檔案,裏面包含 KML 檔與其他 KML 中參照的資料,像是 Marker Icon 的圖檔。
KmlLayer
在 Utility Library 中有提供 KmlLayer
類別用來代表 KML 資料圖層,使用的方法跟昨天的 GeoJSON 很類似。
raw
資料夾中引用這裡使用的是 KML Tutorial 中提供的範例--KML_Samples.xml。
raw
資料夾中。KmlLayer
的建構式建立物件。val layer = KmlLayer(map, R.raw.kml_samples, context)
InputStream
引入不論是本地或是遠端所取得的 KML 檔案,只要能夠轉成 InputStream
就可以建立。
val inputStream: InputStream? = // 包含 KML 資料的 InputStream
val layer = KmlLayer(map, inputStream, context)
因為這次練習使用的另一個檔案(機車定檢站位置圖),預設是 KMZ 的格式,所以讀入之前要先解壓縮,以取得裡面的 KML 檔案。
assets
資料夾。但實際情況有可能是 App 內下載後,儲存至專案內部空間。又或是從手機上的外部儲存空間匯入。InputStream
透過 BufferedInputStream
傳給 ZipInputStream
。ZipInputStream.nextEntry
讀取每個節點,如果不是資料夾就查找 .kml
副檔名的檔案。KML
檔案轉成 KmlLayer
後加到 Set
中傳出。
KML
檔案,但這裡保險起見還是用 Set
儲存。private fun unzipKmzFile(map: GoogleMap): Set<KmlLayer> {
val kmlLayerSet = mutableSetOf<KmlLayer>()
try {
val inputStream = resources.assets.open("taiwan_scooter_inspection_sites.kmz")
val zipInputStream = ZipInputStream(BufferedInputStream(inputStream))
lateinit var zipEntry: ZipEntry
while (zipInputStream.nextEntry.also { zipEntry = it } != null) {
if (!zipEntry.isDirectory) {
val fileName = zipEntry.name
if (fileName.endsWith(".kml")) {
val kmlLayer = KmlLayer(map, zipInputStream, this)
kmlLayerSet.add(kmlLayer)
}
}
zipInputStream.closeEntry()
}
zipInputStream.close()
} catch (e: Exception) {
e.printStackTrace()
}
return kmlLayerSet
}
GoogleMap
跟 GeoJsonLayer
一樣,是呼叫圖層物件的方法,將圖層實際繪製在地圖上。
kmlLayer.addLayerToMap()
使用以下方法,就可以將已繪製在地圖上的 KML 圖層移除。
kmlLayer.removeLayerFromMap()
PlaceMarker
與 GroundOverlay
// 存取 PlaceMarker
kmlLayer.placemarks.forEach { kmlPlacemark ->
}
// 存取 GroundOverlay
kmlLayer.groundOverlays.forEach { kmlGroundOverlay ->
}
使用 KmlLayer.OnFeatureClickListener()
可以監聽 KML 圖層中幾何圖形的點擊事件。
kmlLayer.setOnFeatureClickListener { feature ->
// 點擊到圖形後 顯示該圖形的 name property
Toast.makeText(this, feature.getProperty("name"), Toast.LENGTH_SHORT).show()
}
根據官方文件,KML 中的屬性欄位在 Google Maps SDK 上並不是全部都有支援,有些是屬於部分支援或是完全不支援,這點在資料套疊上要特別注意。
今天介紹的 KML ,以我自己的開發經驗來說,在手機端其實不常碰到。不過,實際試玩了一下基本的套疊,發現沒有想像中的那麼複雜。如果大家在練習的過程中有碰到問題,歡迎留言討論~
明天見啦~