熱力圖作為一種數據可視化形式應用廣泛,它可以展示大量數據點,幫助用戶直觀獲取數據。這是一篇關於如何使用 Mapbox GL JS 製作熱力圖的教程。


準備工作

| Mapbox 賬號和一個訪問令牌

你可以在 mapbox.com/signup 註冊賬號,在用戶頁面可獲取訪問令牌。

| Mapbox GL JS

用來構建網頁地圖的 Mapbox JavaScript API.

| 數據

在本教程中,我們將以匹茲堡城市道路綠化數據為例,數據來源為西賓夕法尼亞州區域數據中心。

點擊下載 GeoJSON


熱力圖的作用

「熱力圖」作為地圖可視化的方式可以處理不同的數據主題,比如總統選舉結果地圖、人口密度地圖或者氣象地圖。

縱觀所有熱力圖的應用,我們可以將其大致分為兩類:一類為用戶查看密集數據點提供方便,另一類通過漸變坡度展現連續平面上的離散值。前者更為常見,後者則多用於科學研究或是針對一個在區域範圍內可預測變化的數據。比如,你所在的城市分佈著若干個氣象站,這些氣象站的數據會有所不同,那我們有理由假設兩個氣象站之間的天氣數據是漸變過度的。天氣 app 就會參考氣象站數據,構建覆蓋整個城市的漸變坡度來進行天氣預報。

本教程的目旨在通過一種不同類型的可視化方式,對於顯示區域上點的密度density更有用。 這種類型的熱力圖不會通過在一組邊界中按照一個等值線圖的方式聚合要素來顯示密度,而是在點之間顯示一個連續的梯度。

熱力圖不僅將數據密度可視化,它還展示了數據間的區別和聯繫。你可以根據特定屬性的值賦予不同數據點不同的權重。在這裡,我們使用「dbh」屬性來設置權重。「dbh」代表「diameter at breast height",是測量3.5英寸高樹木直徑的標準方法。一般來說,一棵有較高 DBH 值的樹會比較老比較大。當你根據這點來調整大樹所佔的權重後,你就可以更精確地估計該區域的樹蔭覆蓋率。


熱力圖的繪製

在添加熱力圖圖層之前,你需要設置幾個屬性。這些屬性對於你數據的展示尤為關鍵,要避免太空泛也不能過於詳細。DPH值的範圍是1-62。

數據權重 (heatmap-weight)

考量每一個數據點對於整張地圖的重要性,默認每一點的數據權重相同。你可以使用 stop 方法來指定屬性,來設置點的權重。

  • 數據強度 (heatmap-intensity):是一個基於"heatmap-weight"的倍增器,可以通過調整縮放比例 (zoom level) 來設置熱力圖的整體外觀。
  • 熱力圖顏色 (heatmap-color):為最小值和最大值分別設置顏色,系統會根據每一像素內數據密度 (heatmap-density) 的變化完成漸變(從0.0至1.0)。數值的設定是一個將數據密度 (heatmap-density)作為輸入值的表達式expression。更多配色方案可以參考 Color Brewer。
  • 數據點半徑 (heatmap-radius):以像素為單位為每個數據點設置半徑,半徑值越大熱力圖的漸變過渡越自然,但顯示的具體數據越少。
  • 熱力圖透明度 (heatmap-opacity):設置熱力圖圖層的整體的透明度。

使用 Mapbox GL JS 創建你的地圖

在瞭解熱力圖的應用和基本屬性之後,就可以創建你的地圖了。在本教程中,我們將使用 Mapbox暗色模板樣式。你可以在我們的 API 相關文檔中找到每一種模板樣式的 Style URL。

<html>
<head>
<metacharset=utf-8/>
<title></title>
<metaname=viewportcontent=initial-scale=1,maximum-scale=1,user-scalable=no/>
<scriptsrc=https://api.tiles.mapbox.com/mapbox-gl-js/v0.42.1/mapbox-gl.js>
</script>
<linkhref=https://api.tiles.mapbox.com/mapbox-gl-js/v0.42.1/mapbox-gl.cssrel=stylesheet/>
<style>
body{
margin: 0;
padding: 0;
}
#map {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
</style>
</head>
<body>
<divid=map></div>
<script>
mapboxgl.accessToken = <your-access-token>;
var map =new mapboxgl.Map({
container: map,
style: mapbox://styles/mapbox/dark-v9,
center: [-79.999732, 40.4374], zoom: 11
});
// we will add more code here in the next steps
</script>
</body>
</html>


添加數據

首先添加你之前下載的 GeoJSON 文件作為熱力圖的源文件,添加數據源可參考 addSource 方法。完成之後會加入熱力圖圖層和數據圈圖層 (circle layer)。在你初始化的地圖後加入以下代碼:

map.on(load, function() {
map.addSource(trees, {
type: geojson,
data: ./trees.geojson
});
// add heatmap layer here
// add circle layer here
});


添加熱力圖圖層

添加熱力圖圖層,參考 addLayer 方法,然後你就可以通過設置我們前文中提到的屬性來調整熱力圖外觀。

對於 heatmap-weight,指定反映數據的範圍(在geojson源中,dbh 屬性的值範圍在 1-62)。 由於較大的樹木具有較高的 dbh,因此通過創建一個 stop 方法,在 dbb 增加時增加 heatmap-weight,為熱力圖賦予更多的權重。

由於 heatmap-intensity 是基於 heatmap-weight 的乘數,heatmap-intensity 可以隨著地圖的放大而增加,以在整個變焦範圍內保持相似的外觀。 下面的圖片展示了熱圖強度對地圖外觀的影響。下圖展示了不同數據強度 (heatmap-intensity) 下的熱力圖外觀。左圖增加了縮放比例 (zoom level),右圖則是在為 1 的默認值。

設置熱力圖透明度 (heatmap-opacity) 時需要注意,在縮放比例 (zoom level) 在 14 到 15 之間時不透明度要從 1 減少到 0,來保證圈圖層 (circle layer) 和熱力圖圖層之間的過渡。設置完所有屬性之後,在事件處理器 (event handler) 中加入以下代碼:

map.addLayer({
id: trees-heat,
type: heatmap,
source: trees,
maxzoom: 15,
paint: {
// increase weight as diameter breast height increases
heatmap-weight: {
property: dbh,
type: exponential,
stops: [
[1, 0],
[62, 1]
]
},
// increase intensity as zoom level increases
heatmap-intensity: {
stops: [
[11, 1],
[15, 3]
]
},
// assign color values be applied to points depending on their density
heatmap-color: [
interpolate,
[linear],
[heatmap-density],
0, rgba(236,222,239,0),
0.2, rgb(208,209,230),
0.4, rgb(166,189,219),
0.6, rgb(103,169,207),
0.8, rgb(28,144,153)
],
// increase radius as zoom increases
heatmap-radius: {
stops: [
[11, 15],
[15, 20]
]
},
// decrease opacity to transition into the circle layer
heatmap-opacity: {
default: 1,
stops: [
[14, 1],
[15, 0]
]
},
}
}, waterway-label);


添加圈圖層

當你放大你的熱力圖時,原本集中的數據點會隨著放大分散,這時你需要保證用戶可以完成相關的交互,如瞭解數據點的具體信息等。還記得之前你是如何用停止功能 (stop function) 使熱力圖圖層在縮放比例在14到15時淡出的嗎?這裡使用的是相同的原理。在縮放比例在14到15之間時增加圈圖層透明度circle-opacity來實現熱力圖圖層淡出的同時圈圖層淡入。具體代碼可見下,添加在上一步中熱力圖圖層代碼之後:

map.addLayer({
id: trees-point,
type: circle,
source: trees,
minzoom: 14,
paint: {
// increase the radius of the circle as the zoom level and dbh value increases
circle-radius: {
property: dbh,
type: exponential,
stops: [
[{zoom: 15, value: 1 }, 5],
[{zoom: 15, value: 62 }, 10],
[{zoom: 22, value: 1 }, 20],
[{zoom: 22, value: 62 }, 50],
]
},
circle-color: {
property: dbh,
type: exponential,
stops: [
[0, rgba(236,222,239,0)],
[10, rgb(236,222,239)],
[20, rgb(208,209,230)],
[30, rgb(166,189,219)],
[40, rgb(103,169,207)],
[50, rgb(28,144,153)],
[60, rgb(1,108,89)]
]
},
circle-stroke-color: white,
circle-stroke-width: 1,
circle-opacity: {
stops: [
[14, 0],
[15, 1]
]
}
}
}, waterway-label);


其他交互

添加下面這段代碼之後,當用戶點擊圈圖層數據點就會彈出樹木的 DBH 值。

map.on(click, trees-point, function(e) {
new mapboxgl.Popup()
.setLngLat(e.features[0].geometry.coordinates)
.setHTML(<b>DBH:</b> + e.features[0].properties.dbh)
.addTo(map);
});

至此,你就成功完成你的熱力圖啦!戳這裡瀏覽熱力圖 Demo。

如果還有任何關於如何將熱力圖整合進 BI 平臺和控制面板的問題或任何疑問可微信搜索Mapbox-China關注mapbox微信公眾號,並在後臺留言,我們的工程師會為您耐心解答。


推薦閱讀:
相關文章