iT邦幫忙

2023 iThome 鐵人賽

DAY 21
0
Mobile Development

Google Maps SDK for Android 與 GIS App 開發筆記系列 第 21

Day 21: Maps SDK for Android Utility–KML 套疊

  • 分享至 

  • xImage
  •  

KML 全稱為 Keyhole Markup Language,是一種基於 XML 的空間資料格式。它除了可以呈現點、線、面等幾何圖像外,還可以連結網頁、圖片等媒體資訊。

由於最初是由 Google 旗下的 Keyhole 公司所提出,所以廣泛地使用在 Google Earth 與 Google Maps 等產品。而 Google 在 2008年將此檔案標準移轉給 OGC (開放地理資訊系統協會),所以目前也已經是被廣泛接納與使用的空間資料格式之一。

資料格式

下方是擷取自 Google KML Reference 網站的圖片,主要說明一個 KML 檔案所包含的結構。

https://ithelp.ithome.com.tw/upload/images/20231005/20160271T4gZZx7LkX.png

相較於昨天介紹的 GeoJSON,KML 似乎明顯複雜許多,但好在有 Maps Utility,讓這個格式在 Maps SDK for Android 上不至於太困難。

練習檔案

練習使用的檔案是來自環境部環境地理資訊服務平台機車定檢站位置圖

如果您想下載這個平台上的其他圖資來練習也可以,不過要注意一下該圖資使用的座標系統,以及它可提供的檔案格式是否有支援 KML。

https://ithelp.ithome.com.tw/upload/images/20231005/201602714rqDdS8Wdh.png

KML 下載下來好像不太一樣?

檔案下載下來預設會是一個 zip 檔,解壓縮出來後會是 kmz 檔。

KMZ 檔其實也是一個透過 zip 壓縮的檔案,裏面包含 KML 檔與其他 KML 中參照的資料,像是 Marker Icon 的圖檔。

https://ithelp.ithome.com.tw/upload/images/20231005/20160271pszcZ0pXRZ.png
圖片來源:Google Doc: KMZ Files

實作

建立 KML 圖層 KmlLayer

在 Utility Library 中有提供 KmlLayer 類別用來代表 KML 資料圖層,使用的方法跟昨天的 GeoJSON 很類似。

方法一:從 raw 資料夾中引用

這裡使用的是 KML Tutorial 中提供的範例--KML_Samples.xml
https://ithelp.ithome.com.tw/upload/images/20231005/20160271MB0yVvHOov.png

  1. 將要匯入的 KML 檔案放在專案的 raw 資料夾中。
  2. 使用 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 (非必要)

因為這次練習使用的另一個檔案(機車定檢站位置圖),預設是 KMZ 的格式,所以讀入之前要先解壓縮,以取得裡面的 KML 檔案。

  1. 為了讓練習更單純,我把 KMZ 檔案放在assets 資料夾。但實際情況有可能是 App 內下載後,儲存至專案內部空間。又或是從手機上的外部儲存空間匯入。
  2. 將讀取檔案的 InputStream 透過 BufferedInputStream 傳給 ZipInputStream
  3. 透過 ZipInputStream.nextEntry 讀取每個節點,如果不是資料夾就查找 .kml 副檔名的檔案。
  4. 將讀取的 KML 檔案轉成 KmlLayer 後加到 Set 中傳出。
    • 通常單一 KMZ 裡面只會有一個 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
}

加入 KML 圖層到 GoogleMap

GeoJsonLayer 一樣,是呼叫圖層物件的方法,將圖層實際繪製在地圖上。

kmlLayer.addLayerToMap()

前面機車定檢圖層加入到地圖上的效果

https://ithelp.ithome.com.tw/upload/images/20231005/20160271rcgEbtzaVL.png

移除 KML 圖層

使用以下方法,就可以將已繪製在地圖上的 KML 圖層移除。

kmlLayer.removeLayerFromMap()

存取 KML 圖層下的 PlaceMarkerGroundOverlay

// 存取 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 並不是所有屬性都有支援

根據官方文件,KML 中的屬性欄位在 Google Maps SDK 上並不是全部都有支援,有些是屬於部分支援或是完全不支援,這點在資料套疊上要特別注意。

參考資料

小結

今天介紹的 KML ,以我自己的開發經驗來說,在手機端其實不常碰到。不過,實際試玩了一下基本的套疊,發現沒有想像中的那麼複雜。如果大家在練習的過程中有碰到問題,歡迎留言討論~

明天見啦~/images/emoticon/emoticon12.gif


上一篇
Day 20: Maps SDK for Android Utility–GeoJSON 套疊
下一篇
Day 22: Maps SDK for Android Utility–Heat map 熱視圖
系列文
Google Maps SDK for Android 與 GIS App 開發筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言