Heat Map (熱視圖)又可稱為熱圖,是一種在地圖上以顏色呈現資料的分布位置與強度的一種圖層,讓讀者可以一目了然地看見資料在空間上的分佈與變化。Heat Map 在資料視覺化的領域裡,算是十分常見的一種呈現方式。
舉例來說,下圖是 台灣動物路死觀察網的路殺紀錄熱點圖。
從圖面來看,我們大概可以知道,哺乳類動物的路殺事件,主要集中在大台北地區與雲嘉高一帶。
在 Google Maps 上建立 Heat Map,所需的資料很簡單。最基本的只要有經緯度即可,頂多再多一個數值作為權重值。
找了好久終於讓我找到一個比較合適拿來玩的經緯度資料,那就是 ctiml/convenience-store-data 的全台超商資料。雖然資料的最後更新時間已經是八年前了,但作為測試用的資料綽綽有餘啦~
文章中的練習,將用 7-11 台中市的資料。
在 Google Maps 上加入 Heat Map,整體的邏輯大致上是:
HeatmapTileProvider
並傳遞資料的 LatLng
。TileOverlayOptions
並將前面建立的物件設為 tileProvider
TileOverlayOptions
加入到 GoogleMap
。private fun readStoreDataFromAssets(): List<StoreData> {
val resultList = mutableListOf<StoreData>()
try {
val inputStream = resources.assets.open("taichung_7_11.json")
val rawJson = JSONObject(BufferedReader(InputStreamReader(inputStream)).readText())
val storesJsonArray = rawJson.getJSONArray("stores")
for (i in 0 until storesJsonArray.length()) {
val storeJson = storesJsonArray.getJSONObject(i)
val lng = storeJson.getDouble("X")
val lat = storeJson.getDouble("Y")
val isIceCream = (storeJson.getString("isIceCream") == "Y")
resultList.add(StoreData(lng, lat, isIceCream))
}
} catch (e: Exception) {
e.printStackTrace()
}
return resultList
}
HeatmapTileProvider
private fun createHeatmapTileProvider(storeDataList: List<StoreData>): HeatmapTileProvider {
// 原始資料轉經緯度資料
val latLngList = mutableListOf<LatLng>()
storeDataList.forEach {
latLngList.add(LatLng(it.lat, it.lng))
}
// 建立 HeatmapTileProvider
return HeatmapTileProvider.Builder().data(latLngList).build()
}
權重值代表的是該位置的重要性,數值越大,圖層的漸層色彩強度就越高,若以預設的顏色來說就是紅色。
如果要替每個點位加上不同的權重值,要改用 HeatmapTileProvider.Builder().weightedData(dataList)
,並傳入 WeightedLatLng 資料集。
// 有冰淇淋的店加重權重
private fun createWeightedHeatmapTileProvider(storeDataList: List<StoreData>): HeatmapTileProvider {
val weightedLatLngs = mutableListOf<WeightedLatLng>()
storeDataList.forEach {
val intensity: Double = if (it.isIceCream) {
2
} else {
0.5
}
weightedLatLngs.add(WeightedLatLng(LatLng(it.lat, it.lng), intensity))
}
return HeatmapTileProvider.Builder().weightedData(weightedLatLngs).build()
}
map.addTileOverlay(TileOverlayOptions().tileProvider(heatmapTileProvider))
要變更 Heat Map 的外觀,可以在 Builder
階段呼叫方法設定,也可以在建立好 HeatmapTileProvider
後呼叫相關的 setter 方法來變更樣式,並接著呼叫 clearTileCache()
清除快取強制地圖重新繪製。
預設值為 20,單位為 px,值必須介於 10 ~ 50 之間。
HeatmapTileProvider.Builder().radius()
// 或者
HeatmapTileProvider.setRadius()
Gradient 用來設定 Heat Map 使用的色彩範圍,從低強度到最高強度。
設定色彩會使用兩個陣列,一個用來指定色彩(rgb),一個用來指出個顏色起始點的浮點陣列,代表占最高強度顏色的百分比 (數值範圍 0 ~ 1)。
設定方法如下:
Gradient
物件。HeatmapTileProvider.Builder()
階段呼叫 HeatmapTileProvider.Builder().gradient()
方法傳入參數。或是在 HeatmapTileProvider
建立好後,呼叫 HeatmapTileProvider.setGradient()
更新色彩設定。// 漸層的顏色
val colors = intArrayOf(
Color.rgb(102, 225, 0), // 綠色
Color.rgb(255, 0, 0) // 紅色
)
// 各漸層顏色開始的百分比
val startPoints = floatArrayOf(0.2f, 1f)
// 建立 Gradient
val gradient = Gradient(colors, startPoints)
// 在建立 TileProvider 時傳入色彩設定
val provider = HeatmapTileProvider.Builder()
.data(latLngs)
.gradient(gradient)
.build()
呼叫 HeatmapTileProvider.Builder().opacity()
或是 HeatmapTileProvider.setOpacity()
更新整個圖層的透明度,其值預設為 0.7,可設定的範圍為 0 ~ 1。
// 設定不透明度
provider.setOpacity(0.3)
// 呼叫圖層清除快取重繪
tileOverlay?.clearTileCache()
private fun updateHeatmapStyle(heatmapTileProvider: HeatmapTileProvider) {
// 設定半徑
heatmapTileProvider.setRadius(50)
// 設定透明度
heatmapTileProvider.setOpacity(0.5)
// 設定漸層
val colorIntArray = intArrayOf(
// lightskyblue
Color.rgb(135, 206, 250),
// mediumblue
Color.rgb(0, 0, 205)
)
val percentFloatArray = floatArrayOf(
0.2f, 1f
)
heatmapTileProvider.setGradient(Gradient(colorIntArray, percentFloatArray))
}
要新增、更新、刪除已建立的 Heat map 資料點,可以使用以下方法。
// 更新帶有權重值的版本
val weightedData: List<WeightedLatLng> = ArrayList()
provider.setWeightedData(weightedData)
// 更新一般版本
val data: List<LatLng> = ArrayList()
provider.setData(data)
// 呼叫圖層清除快取重繪
tileOverlay?.clearTileCache()
因為實際上是以 TileOverlay
的形式加入到地圖上,所以移除方法跟 TileOverlay
一樣。
tileOverlay?.remove()
以上就是今天的 Heat map 介紹,大家如果還想試試其他的資料,可以參考上方的Google Maps API 學習筆記 – 3:用熱圖 / Heat map 製作全台 12 小時雨量分佈圖這篇文章,串中央氣象署的雨量分布資料來玩玩看。
如果串接上遇到問題歡迎留言討論喔~
那就明天見啦~