前段時間,項目要開發熱力圖插件,研究了heatmap.js,打算好好總結一下。javascript
本文主要有如下幾部份內容:java
關於heatmap.js介紹,請看這裏: http://www.oschina.net/p/heatmap-jsgit
目前,對於熱力圖的開發,百度、高德開發平臺上使用的都是這款JS開源庫。固然,如今還有咱們公司:Pgithub
百度示例:http://developer.baidu.com/map/jsdemo.htm#c1_15canvas
高德示例:http://lbs.amap.com/api/javascript-api/example/layers/heatmap/api
PS:本人對JS只能算是初級水平,剛開始寫熱力圖插件的時候,真是一頭霧水。充分利用搜索引擎後,在百度的開源庫發現了這個(示例源文件),總算有點眉目了。替換了地圖實例對象,剩下的難點是圖層處理和像素座標轉換,刪刪改改,也弄出了插件的1.0版。v2.0版本的內容與v1.0相比仍是有不少不一樣的,通過大半天廢寢忘食的努力,終於遷移到了2.0(準確的說是v2.0.5),也算對得起忘吃的午餐。數組
通過源碼閱讀和測試,數據集合中的max參數表示熱力點權值的最大值,用於熱力顏色漸變的計算。其本質是熱力區域內部填充顏色的透明度。瀏覽器
使用到max的算式:dom
(value-min)/(max-min) /* 其中, value是某個熱力點的權值; max是最大權值,默認是1; min是最小權值,默認是10; */
當熱力點權值大於等於max,熱力點顏色達到最大漸變程度。函數
當熱力點權值遠遠小於max,熱力點顏色較淺,甚至顯示不出來。
該方法的參數能夠是單個點的對象,也能夠是個數組。可是方法內的處理是,在原圖的基礎上每增長一個點就渲染一次。若是參數類型是數組,則遞歸調用本方法,逐個點的增長並渲染。
若是數據是少許動態增長的,則能夠實時的呈現出熱力情況。當大數據量頻繁更新的時候,推測對性能仍是會有一點影響吧,這個須要測試驗證。
——2016-11-28 補充驗證結果:使用此方法添加10條數據,幾乎是馬上呈現;添加100條,須要2秒左右;添加1000條,須要20秒左右。測試瀏覽器爲Chrome 版本53。
使用刪除方法(即用setData方法從新繪圖)刪除100條、1000條數據,基本都在1秒之內。
另一點須要注意的是,新增的熱力點的權值對max的值有影響。源碼中,會對同一座標點的權值進行累加而後設置到Store._max屬性,這會使顏色漸變的標準發生改變,影響熱圖的一致性。
github上也有人提出這個問題[How not to change 'max' when adding point with addData?]正問題中所描述的那樣,要想保證顏色漸變的標準在增長熱力點先後保持一致性,須要調用setDataMax方法從新設定max值,目前只能這樣。
我所使用的是heatmap v2.0.5版本,其中removeData方法仍是空的,具體處理尚未實現。因此我在插件中本身寫了一個方法。
採用二次循環,將現存的數據與要刪除的數據進行比對,只保留那些經緯度不相同的數據,以及經緯度相同可是權值差大於0的數據,而後清空畫布,調用setData從新賦值繪圖。
方法有點low,求指點~~
data = [460: [280:15, 255:6], 490: [251:8], 416: [289:9]]
數組的長度是可變的,最外層長度是全部x值中最大的那個數值加1,如 data.length = 491。
_organiseData方法會將外部傳入數據存儲到Store._data中,而且會累計權值。若是本方法是被setData調用,則按照傳入參數中max來渲染;若是是被addData方法調用,則會按累計權值渲染。
_unOrganizeData方法內部並沒有特殊處理,只是將內部以二維數組存儲的數據轉換爲外部數據格式對象,該方法被getData方法調用,返回熱力數據。
如下內容來自這裏:https://github.com/pa7/heatmap.js/blob/master/docs/how-to-migrate.md
1. element -> container
做爲熱圖canvas的容器,heatmap做者認爲「container」能更加形象的描述這個配置參數,因此原來的element屬性如今叫container。
var cfg = { "container": domElement };
2. opacity -> maxOpacity
var cfg = { "maxOpacity": .8 }
另外,opacity、maxOpacity、minOpacity這仨屬性如今都是[0,1]範圍內的數。
3. 裝填數據
heatmap.js的做者決定減小API,使其變爲更扁平的結構。爲了添加數據你沒必要訪問heatmap的數據存儲,而是向heatmap實例直接添加新數據。同時也擺脫了重複命名。(heatmap.store.addDataPoint -> heatmap.addData, heatmap.store.setDataSet -> heatmap.setData).var datapoint = { x: 100, y: 100, value: 10 }; heatmap.addData(datapoint); heatmap.setData({ max: 10, data: [datapoint] });
同時,你可能注意到:熱力點的權值屬性count如今默認叫value,爲了順利遷移你能夠把「count」設置爲valueField屬性的值。
var datapoint = { x: 100, y: 100, count: 10 }; var heatmap = h337.create({ container: domElement, // ... valueField: 'count' }); heatmap.addData(datapoint); heatmap.setData({ max: 10, data: [datapoint] });
4. 總結
舊的配置實例:
var cfg = { "element": domElement, "opacity": 80 } var heatmap = h337.create(cfg); heatmap.store.setDataSet(data);
變成了新的配置:
var cfg = { "container": domElement, "maxOpacity": .8 } var heatmap = h337.create(cfg); heatmap.setData(data);
本人補充幾條:
1. 支持自定義熱點數據的屬性名稱
若是以前你的熱力數據是這樣的:
{"lng":123.456789, "lat":987.654321, "count": 10}
爲了向heatmap內部設置數據,你須要將數據轉換成這樣的:
{"x":123.456789, "y":987.654321, "value": 10}
如今,配置參數中新增長了三個屬性xField、yField、valueField,分別對應x座標、y座標、權值的屬性名稱。設置了這三個屬性就不須要上面的數據格式轉換了。例如:xField="lng"; yField="lat"; valueField="count;
2. 清空畫布
我以前的作法是:
heatmap.clear();
如今是:
heatmap._renderer._clear();
3. 重置畫布大小
以前是:
heatmap.set("width", w); heatmap.set("height", h); heatmap.store.get("heatmap").resize();
如今改成:
heatmap._renderer.setDimensions(w, h);
下面是我翻譯的heatmap.js v2.0官方文檔。水平有限,若有疏漏或錯誤請指正。
API函數的優先級排序:
可配置的屬性:
它是熱力圖像canvas所依附的DOM節點(熱圖將適應節點的大小)。
背景色字符串能夠是16進制編碼、英文顏色名稱、rgb顏色。
表明顏色漸變的對象(語法:數值字符串[0,1]: 顏色字符串),請查看代碼示例。
每一個熱力點的半徑(若是節點自己未被指定)。
heatmap的全局透明度。將覆蓋以前設置的maxOpacity 和 minOpacity。
heatmap將擁有的最大透明度。(會被opacity的設置覆蓋)。
heatmap將擁有的最小透明度。(會被opacity的設置覆蓋)。
傳遞一個回調函數來接收極值變化更新。對於DOM legends頗有用。
濾鏡係數將應用於全部熱點數據。係數越高,顏色漸變越平滑。
熱點數據x座標的屬性名稱。
熱點數據y座標的屬性名稱。
熱點數據權值的屬性名稱。
// create configuration object var config = { container: document.getElementById('heatmapContainer'), radius: 10, maxOpacity: .5, minOpacity: 0, blur: .75}; // create heatmap with configuration var heatmapInstance = h337.create(config);
自定義顏色漸變
// create configuration object var config = { container: document.getElementById('heatmapContainer'), radius: 10, maxOpacity: .5, minOpacity: 0, blur: .75, gradient: { // enter n keys between 0 and 1 here // for gradient color customization '.5': 'blue', '.8': 'red', '.95': 'white' }};var heatmapInstance = h337.create(config);
// a single datapoint var dataPoint = { x: 5, // x coordinate of the datapoint, a number y: 5, // y coordinate of the datapoint, a number value: 100 // the value at datapoint(x, y) }; heatmapInstance.addData(dataPoint); // multiple datapoints (for data initialization use setData!!) var dataPoints = [dataPoint, dataPoint, dataPoint, dataPoint]; heatmapInstance.addData(dataPoints);
返回heatmapInstance
var data = { max: 100, min: 0, data: [ dataPoint, dataPoint, dataPoint, dataPoint ]}; heatmapInstance.setData(data);
heatmapInstance.setDataMax(200); // setting the maximum value triggers a complete rerendering of the heatmap heatmapInstance.setDataMax(100);
heatmapInstance.setDataMin(number)
heatmapInstance.setDataMin(10); // setting the minimum value triggers a complete rerendering of the heatmap heatmapInstance.setDataMin(0);
var nuConfig = { radius: 10, maxOpacity: .5, minOpacity: 0, blur: .75}; heatmapInstance.configure(nuConfig);
heatmapInstance.addData({ x: 10, y: 10, value: 100}); // get the value at x=10, y=10 heatmapInstance.getValueAt({ x: 10, y: 10 }); // returns 100
返回一個持久化而且可重裝的JSON對象。
var currentData = heatmapInstance.getData(); // now let's create a new instance and set the data var heatmap2 = h337.create(config); heatmap2.setData(currentData); // now both heatmap instances have the same content
返回值是base64編碼的dataURL字符串。
heatmapInstance.getDataURL(); // data:image/png;base64... // ready for saving locally or on the server
----------------------完 結----------------------------------------------------------------------------------------------------------------------