index.js 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856
  1. (function (root, factory) {
  2. if (typeof exports === 'object') {
  3. module.exports = factory();
  4. } else if (typeof define === 'function' && define.amd) {
  5. define(factory);
  6. } else {
  7. root.BMapLib = root.BMapLib || {};
  8. root.BMapLib.Heatmap = root.BMapLib.Heatmap || factory();
  9. }
  10. })(this, function() {
  11. function inherits (subClass, superClass, className) {
  12. var key, proto,
  13. selfProps = subClass.prototype,
  14. clazz = new Function();
  15. clazz.prototype = superClass.prototype;
  16. proto = subClass.prototype = new clazz();
  17. for (key in selfProps) {
  18. proto[key] = selfProps[key];
  19. }
  20. subClass.prototype.constructor = subClass;
  21. subClass.superClass = superClass.prototype;
  22. // 类名标识,兼容Class的toString,基本没用
  23. if ("string" == typeof className) {
  24. proto._className = className;
  25. }
  26. };
  27. var heatmapFactory = (function(){
  28. // store object constructor
  29. // a heatmap contains a store
  30. // the store has to know about the heatmap in order to trigger heatmap updates when datapoints get added
  31. var store = function store(hmap){
  32. var _ = {
  33. // data is a two dimensional array
  34. // a datapoint gets saved as data[point-x-value][point-y-value]
  35. // the value at [point-x-value][point-y-value] is the occurrence of the datapoint
  36. data: [],
  37. // tight coupling of the heatmap object
  38. heatmap: hmap
  39. };
  40. // the max occurrence - the heatmaps radial gradient alpha transition is based on it
  41. this.max = 1;
  42. this.get = function(key){
  43. return _[key];
  44. };
  45. this.set = function(key, value){
  46. _[key] = value;
  47. };
  48. }
  49. store.prototype = {
  50. // function for adding datapoints to the store
  51. // datapoints are usually defined by x and y but could also contain a third parameter which represents the occurrence
  52. addDataPoint: function(x, y){
  53. if(x < 0 || y < 0)
  54. return;
  55. var me = this,
  56. heatmap = me.get("heatmap"),
  57. data = me.get("data");
  58. if(!data[x])
  59. data[x] = [];
  60. if(!data[x][y])
  61. data[x][y] = 0;
  62. // if count parameter is set increment by count otherwise by 1
  63. data[x][y]+=(arguments.length<3)?1:arguments[2];
  64. me.set("data", data);
  65. // do we have a new maximum?
  66. if(me.max < data[x][y]){
  67. // max changed, we need to redraw all existing(lower) datapoints
  68. heatmap.get("actx").clearRect(0,0,heatmap.get("width"),heatmap.get("height"));
  69. me.setDataSet({ max: data[x][y], data: data }, true);
  70. return;
  71. }
  72. heatmap.drawAlpha(x, y, data[x][y], true);
  73. },
  74. setDataSet: function(obj, internal){
  75. var me = this,
  76. heatmap = me.get("heatmap"),
  77. data = [],
  78. d = obj.data,
  79. dlen = d.length;
  80. // clear the heatmap before the data set gets drawn
  81. heatmap.clear();
  82. this.max = obj.max;
  83. // if a legend is set, update it
  84. heatmap.get("legend") && heatmap.get("legend").update(obj.max);
  85. if(internal != null && internal){
  86. for(var one in d){
  87. // jump over undefined indexes
  88. if(one === undefined)
  89. continue;
  90. for(var two in d[one]){
  91. if(two === undefined)
  92. continue;
  93. // if both indexes are defined, push the values into the array
  94. heatmap.drawAlpha(one, two, d[one][two], false);
  95. }
  96. }
  97. }else{
  98. while(dlen--){
  99. var point = d[dlen];
  100. heatmap.drawAlpha(point.x, point.y, point.count, false);
  101. if(!data[point.x])
  102. data[point.x] = [];
  103. if(!data[point.x][point.y])
  104. data[point.x][point.y] = 0;
  105. data[point.x][point.y] = point.count;
  106. }
  107. }
  108. heatmap.colorize();
  109. this.set("data", d);
  110. },
  111. exportDataSet: function(){
  112. var me = this,
  113. data = me.get("data"),
  114. exportData = [];
  115. for(var one in data){
  116. // jump over undefined indexes
  117. if(one === undefined)
  118. continue;
  119. for(var two in data[one]){
  120. if(two === undefined)
  121. continue;
  122. // if both indexes are defined, push the values into the array
  123. exportData.push({x: parseInt(one, 10), y: parseInt(two, 10), count: data[one][two]});
  124. }
  125. }
  126. return { max: me.max, data: exportData };
  127. },
  128. generateRandomDataSet: function(points){
  129. var heatmap = this.get("heatmap"),
  130. w = heatmap.get("width"),
  131. h = heatmap.get("height");
  132. var randomset = {},
  133. max = Math.floor(Math.random()*1000+1);
  134. randomset.max = max;
  135. var data = [];
  136. while(points--){
  137. data.push({x: Math.floor(Math.random()*w+1), y: Math.floor(Math.random()*h+1), count: Math.floor(Math.random()*max+1)});
  138. }
  139. randomset.data = data;
  140. this.setDataSet(randomset);
  141. }
  142. };
  143. var legend = function legend(config){
  144. this.config = config;
  145. var _ = {
  146. element: null,
  147. labelsEl: null,
  148. gradientCfg: null,
  149. ctx: null
  150. };
  151. this.get = function(key){
  152. return _[key];
  153. };
  154. this.set = function(key, value){
  155. _[key] = value;
  156. };
  157. this.init();
  158. };
  159. legend.prototype = {
  160. init: function(){
  161. var me = this,
  162. config = me.config,
  163. title = config.title || "Legend",
  164. position = config.position,
  165. offset = config.offset || 10,
  166. gconfig = config.gradient,
  167. labelsEl = document.createElement("ul"),
  168. labelsHtml = "",
  169. grad, element, gradient, positionCss = "";
  170. me.processGradientObject();
  171. // Positioning
  172. // top or bottom
  173. if(position.indexOf('t') > -1){
  174. positionCss += 'top:'+offset+'px;';
  175. }else{
  176. positionCss += 'bottom:'+offset+'px;';
  177. }
  178. // left or right
  179. if(position.indexOf('l') > -1){
  180. positionCss += 'left:'+offset+'px;';
  181. }else{
  182. positionCss += 'right:'+offset+'px;';
  183. }
  184. element = document.createElement("div");
  185. element.style.cssText = "border-radius:5px;position:absolute;"+positionCss+"font-family:Helvetica; width:256px;z-index:10000000000; background:rgba(255,255,255,1);padding:10px;border:1px solid black;margin:0;";
  186. element.innerHTML = "<h3 style='padding:0;margin:0;text-align:center;font-size:16px;'>"+title+"</h3>";
  187. // create gradient in canvas
  188. labelsEl.style.cssText = "position:relative;font-size:12px;display:block;list-style:none;list-style-type:none;margin:0;height:15px;";
  189. // create gradient element
  190. gradient = document.createElement("div");
  191. gradient.style.cssText = ["position:relative;display:block;width:256px;height:15px;border-bottom:1px solid black; background-image:url(",me.createGradientImage(),");"].join("");
  192. element.appendChild(labelsEl);
  193. element.appendChild(gradient);
  194. me.set("element", element);
  195. me.set("labelsEl", labelsEl);
  196. me.update(1);
  197. },
  198. processGradientObject: function(){
  199. // create array and sort it
  200. var me = this,
  201. gradientConfig = this.config.gradient,
  202. gradientArr = [];
  203. for(var key in gradientConfig){
  204. if(gradientConfig.hasOwnProperty(key)){
  205. gradientArr.push({ stop: key, value: gradientConfig[key] });
  206. }
  207. }
  208. gradientArr.sort(function(a, b){
  209. return (a.stop - b.stop);
  210. });
  211. gradientArr.unshift({ stop: 0, value: 'rgba(0,0,0,0)' });
  212. me.set("gradientArr", gradientArr);
  213. },
  214. createGradientImage: function(){
  215. var me = this,
  216. gradArr = me.get("gradientArr"),
  217. length = gradArr.length,
  218. canvas = document.createElement("canvas"),
  219. ctx = canvas.getContext("2d"),
  220. grad;
  221. // the gradient in the legend including the ticks will be 256x15px
  222. canvas.width = "256";
  223. canvas.height = "15";
  224. grad = ctx.createLinearGradient(0,5,256,10);
  225. for(var i = 0; i < length; i++){
  226. grad.addColorStop(1/(length-1) * i, gradArr[i].value);
  227. }
  228. ctx.fillStyle = grad;
  229. ctx.fillRect(0,5,256,10);
  230. ctx.strokeStyle = "black";
  231. ctx.beginPath();
  232. for(var i = 0; i < length; i++){
  233. ctx.moveTo(((1/(length-1)*i*256) >> 0)+.5, 0);
  234. ctx.lineTo(((1/(length-1)*i*256) >> 0)+.5, (i==0)?15:5);
  235. }
  236. ctx.moveTo(255.5, 0);
  237. ctx.lineTo(255.5, 15);
  238. ctx.moveTo(255.5, 4.5);
  239. ctx.lineTo(0, 4.5);
  240. ctx.stroke();
  241. // we re-use the context for measuring the legends label widths
  242. me.set("ctx", ctx);
  243. return canvas.toDataURL();
  244. },
  245. getElement: function(){
  246. return this.get("element");
  247. },
  248. update: function(max){
  249. var me = this,
  250. gradient = me.get("gradientArr"),
  251. ctx = me.get("ctx"),
  252. labels = me.get("labelsEl"),
  253. labelText, labelsHtml = "", offset;
  254. for(var i = 0; i < gradient.length; i++){
  255. labelText = max*gradient[i].stop >> 0;
  256. offset = (ctx.measureText(labelText).width/2) >> 0;
  257. if(i == 0){
  258. offset = 0;
  259. }
  260. if(i == gradient.length-1){
  261. offset *= 2;
  262. }
  263. labelsHtml += '<li style="position:absolute;left:'+(((((1/(gradient.length-1)*i*256) || 0)) >> 0)-offset+.5)+'px">'+labelText+'</li>';
  264. }
  265. labels.innerHTML = labelsHtml;
  266. }
  267. };
  268. // heatmap object constructor
  269. var heatmap = function heatmap(config){
  270. // private variables
  271. var _ = {
  272. radius : 40,
  273. element : {},
  274. canvas : {},
  275. acanvas: {},
  276. ctx : {},
  277. actx : {},
  278. legend: null,
  279. visible : true,
  280. width : 0,
  281. height : 0,
  282. max : false,
  283. gradient : false,
  284. opacity: 180,
  285. premultiplyAlpha: false,
  286. bounds: {
  287. l: 1000,
  288. r: 0,
  289. t: 1000,
  290. b: 0
  291. },
  292. debug: false
  293. };
  294. // heatmap store containing the datapoints and information about the maximum
  295. // accessible via instance.store
  296. this.store = new store(this);
  297. this.get = function(key){
  298. return _[key];
  299. };
  300. this.set = function(key, value){
  301. _[key] = value;
  302. };
  303. // configure the heatmap when an instance gets created
  304. this.configure(config);
  305. // and initialize it
  306. this.init();
  307. };
  308. // public functions
  309. heatmap.prototype = {
  310. configure: function(config){
  311. var me = this,
  312. rout, rin;
  313. me.set("radius", config["radius"] || 40);
  314. me.set("element", (config.element instanceof Object)?config.element:document.getElementById(config.element));
  315. me.set("visible", (config.visible != null)?config.visible:true);
  316. me.set("max", config.max || false);
  317. me.set("gradient", config.gradient || { 0.45: "rgb(0,0,255)", 0.55: "rgb(0,255,255)", 0.65: "rgb(0,255,0)", 0.95: "yellow", 1.0: "rgb(255,0,0)"}); // default is the common blue to red gradient
  318. me.set("opacity", parseInt(255/(100/config.opacity), 10) || 180);
  319. me.set("width", config.width || 0);
  320. me.set("height", config.height || 0);
  321. me.set("debug", config.debug);
  322. if(config.legend){
  323. var legendCfg = config.legend;
  324. legendCfg.gradient = me.get("gradient");
  325. me.set("legend", new legend(legendCfg));
  326. }
  327. },
  328. resize: function () {
  329. var me = this,
  330. element = me.get("element"),
  331. canvas = me.get("canvas"),
  332. acanvas = me.get("acanvas");
  333. canvas.width = acanvas.width = me.get("width") || element.style.width.replace(/px/, "") || me.getWidth(element);
  334. this.set("width", canvas.width);
  335. canvas.height = acanvas.height = me.get("height") || element.style.height.replace(/px/, "") || me.getHeight(element);
  336. this.set("height", canvas.height);
  337. },
  338. init: function(){
  339. var me = this,
  340. canvas = document.createElement("canvas"),
  341. acanvas = document.createElement("canvas"),
  342. ctx = canvas.getContext("2d"),
  343. actx = acanvas.getContext("2d"),
  344. element = me.get("element");
  345. me.initColorPalette();
  346. me.set("canvas", canvas);
  347. me.set("ctx", ctx);
  348. me.set("acanvas", acanvas);
  349. me.set("actx", actx);
  350. me.resize();
  351. canvas.style.cssText = acanvas.style.cssText = "position:absolute;top:0;left:0;z-index:10000000;";
  352. if(!me.get("visible"))
  353. canvas.style.display = "none";
  354. element.appendChild(canvas);
  355. if(me.get("legend")){
  356. element.appendChild(me.get("legend").getElement());
  357. }
  358. // debugging purposes only
  359. if(me.get("debug"))
  360. document.body.appendChild(acanvas);
  361. actx.shadowOffsetX = 15000;
  362. actx.shadowOffsetY = 15000;
  363. actx.shadowBlur = 15;
  364. },
  365. initColorPalette: function(){
  366. var me = this,
  367. canvas = document.createElement("canvas"),
  368. gradient = me.get("gradient"),
  369. ctx, grad, testData;
  370. canvas.width = "1";
  371. canvas.height = "256";
  372. ctx = canvas.getContext("2d");
  373. grad = ctx.createLinearGradient(0,0,1,256);
  374. // Test how the browser renders alpha by setting a partially transparent pixel
  375. // and reading the result. A good browser will return a value reasonably close
  376. // to what was set. Some browsers (e.g. on Android) will return a ridiculously wrong value.
  377. testData = ctx.getImageData(0,0,1,1);
  378. testData.data[0] = testData.data[3] = 64; // 25% red & alpha
  379. testData.data[1] = testData.data[2] = 0; // 0% blue & green
  380. ctx.putImageData(testData, 0, 0);
  381. testData = ctx.getImageData(0,0,1,1);
  382. me.set("premultiplyAlpha", (testData.data[0] < 60 || testData.data[0] > 70));
  383. for(var x in gradient){
  384. grad.addColorStop(x, gradient[x]);
  385. }
  386. ctx.fillStyle = grad;
  387. ctx.fillRect(0,0,1,256);
  388. me.set("gradient", ctx.getImageData(0,0,1,256).data);
  389. },
  390. getWidth: function(element){
  391. var width = element.offsetWidth;
  392. if(element.style.paddingLeft){
  393. width+=element.style.paddingLeft;
  394. }
  395. if(element.style.paddingRight){
  396. width+=element.style.paddingRight;
  397. }
  398. return width;
  399. },
  400. getHeight: function(element){
  401. var height = element.offsetHeight;
  402. if(element.style.paddingTop){
  403. height+=element.style.paddingTop;
  404. }
  405. if(element.style.paddingBottom){
  406. height+=element.style.paddingBottom;
  407. }
  408. return height;
  409. },
  410. colorize: function(x, y){
  411. // get the private variables
  412. var me = this,
  413. width = me.get("width"),
  414. radius = me.get("radius"),
  415. height = me.get("height"),
  416. actx = me.get("actx"),
  417. ctx = me.get("ctx"),
  418. x2 = radius * 3,
  419. premultiplyAlpha = me.get("premultiplyAlpha"),
  420. palette = me.get("gradient"),
  421. opacity = me.get("opacity"),
  422. bounds = me.get("bounds"),
  423. left, top, bottom, right,
  424. image, length, alpha, offset, finalAlpha;
  425. if(x != null && y != null){
  426. if(x+x2>width){
  427. x=width-x2;
  428. }
  429. if(x<0){
  430. x=0;
  431. }
  432. if(y<0){
  433. y=0;
  434. }
  435. if(y+x2>height){
  436. y=height-x2;
  437. }
  438. left = x;
  439. top = y;
  440. right = x + x2;
  441. bottom = y + x2;
  442. }else{
  443. if(bounds['l'] < 0){
  444. left = 0;
  445. }else{
  446. left = bounds['l'];
  447. }
  448. if(bounds['r'] > width){
  449. right = width;
  450. }else{
  451. right = bounds['r'];
  452. }
  453. if(bounds['t'] < 0){
  454. top = 0;
  455. }else{
  456. top = bounds['t'];
  457. }
  458. if(bounds['b'] > height){
  459. bottom = height;
  460. }else{
  461. bottom = bounds['b'];
  462. }
  463. }
  464. image = actx.getImageData(left, top, right-left, bottom-top);
  465. length = image.data.length;
  466. // loop thru the area
  467. for(var i=3; i < length; i+=4){
  468. // [0] -> r, [1] -> g, [2] -> b, [3] -> alpha
  469. alpha = image.data[i],
  470. offset = alpha*4;
  471. if(!offset)
  472. continue;
  473. // we ve started with i=3
  474. // set the new r, g and b values
  475. finalAlpha = (alpha < opacity)?alpha:opacity;
  476. image.data[i-3]=palette[offset];
  477. image.data[i-2]=palette[offset+1];
  478. image.data[i-1]=palette[offset+2];
  479. if (premultiplyAlpha) {
  480. // To fix browsers that premultiply incorrectly, we'll pass in a value scaled
  481. // appropriately so when the multiplication happens the correct value will result.
  482. image.data[i-3] /= 255/finalAlpha;
  483. image.data[i-2] /= 255/finalAlpha;
  484. image.data[i-1] /= 255/finalAlpha;
  485. }
  486. // we want the heatmap to have a gradient from transparent to the colors
  487. // as long as alpha is lower than the defined opacity (maximum), we'll use the alpha value
  488. image.data[i] = finalAlpha;
  489. }
  490. // the rgb data manipulation didn't affect the ImageData object(defined on the top)
  491. // after the manipulation process we have to set the manipulated data to the ImageData object
  492. // image.data = imageData;
  493. ctx.putImageData(image, left, top);
  494. },
  495. drawAlpha: function(x, y, count, colorize){
  496. // storing the variables because they will be often used
  497. var me = this,
  498. radius = me.get("radius"),
  499. ctx = me.get("actx"),
  500. max = me.get("max"),
  501. bounds = me.get("bounds"),
  502. xb = x - (1.5 * radius) >> 0, yb = y - (1.5 * radius) >> 0,
  503. xc = x + (1.5 * radius) >> 0, yc = y + (1.5 * radius) >> 0;
  504. ctx.shadowColor = ('rgba(0,0,0,'+((count)?(count/me.store.max):'0.1')+')');
  505. ctx.shadowOffsetX = 15000;
  506. ctx.shadowOffsetY = 15000;
  507. ctx.shadowBlur = 15;
  508. ctx.beginPath();
  509. ctx.arc(x - 15000, y - 15000, radius, 0, Math.PI * 2, true);
  510. ctx.closePath();
  511. ctx.fill();
  512. if(colorize){
  513. // finally colorize the area
  514. me.colorize(xb,yb);
  515. }else{
  516. // or update the boundaries for the area that then should be colorized
  517. if(xb < bounds["l"]){
  518. bounds["l"] = xb;
  519. }
  520. if(yb < bounds["t"]){
  521. bounds["t"] = yb;
  522. }
  523. if(xc > bounds['r']){
  524. bounds['r'] = xc;
  525. }
  526. if(yc > bounds['b']){
  527. bounds['b'] = yc;
  528. }
  529. }
  530. },
  531. toggleDisplay: function(){
  532. var me = this,
  533. visible = me.get("visible"),
  534. canvas = me.get("canvas");
  535. if(!visible)
  536. canvas.style.display = "block";
  537. else
  538. canvas.style.display = "none";
  539. me.set("visible", !visible);
  540. },
  541. // dataURL export
  542. getImageData: function(){
  543. return this.get("canvas").toDataURL();
  544. },
  545. clear: function(){
  546. var me = this,
  547. w = me.get("width"),
  548. h = me.get("height");
  549. me.store.set("data",[]);
  550. // @TODO: reset stores max to 1
  551. //me.store.max = 1;
  552. me.get("ctx").clearRect(0,0,w,h);
  553. me.get("actx").clearRect(0,0,w,h);
  554. },
  555. cleanup: function(){
  556. var me = this;
  557. me.get("element").removeChild(me.get("canvas"));
  558. }
  559. };
  560. return {
  561. create: function(config){
  562. return new heatmap(config);
  563. },
  564. util: {
  565. mousePosition: function(ev){
  566. // this doesn't work right
  567. // rather use
  568. /*
  569. // this = element to observe
  570. var x = ev.pageX - this.offsetLeft;
  571. var y = ev.pageY - this.offsetTop;
  572. */
  573. var x, y;
  574. if (ev.layerX) { // Firefox
  575. x = ev.layerX;
  576. y = ev.layerY;
  577. } else if (ev.offsetX) { // Opera
  578. x = ev.offsetX;
  579. y = ev.offsetY;
  580. }
  581. if(typeof(x)=='undefined')
  582. return;
  583. return [x,y];
  584. }
  585. }
  586. };
  587. })();
  588. var HeatmapOverlay = function(opts) {
  589. try {
  590. BMap;
  591. } catch (e) {
  592. throw Error('Baidu Map JS API is not ready yet!');
  593. }
  594. if (!HeatmapOverlay._isExtended) {
  595. HeatmapOverlay._isExtended = true;
  596. inherits(HeatmapOverlay, BMap.Overlay, "HeatmapOverlay");
  597. var newHeatmap = new HeatmapOverlay(opts);
  598. this.__proto__ = newHeatmap.__proto__;
  599. }
  600. // HeatmapOverlay.prototype = new BMap.Overlay();
  601. this.conf = opts;
  602. this.heatmap = null;
  603. this.latlngs = [];
  604. this.bounds = null;
  605. this._moveendHandler = this._moveendHandler.bind(this);
  606. }
  607. HeatmapOverlay.prototype.initialize = function(map) {
  608. this._map = map;
  609. var el = document.createElement("div");
  610. el.style.position = "absolute";
  611. el.style.top = 0;
  612. el.style.left = 0;
  613. el.style.border = 0;
  614. el.style.width = this._map.getSize().width + "px";
  615. el.style.height = this._map.getSize().height + "px";
  616. this.conf.element = el;
  617. map.getPanes().mapPane.appendChild(el);
  618. this.heatmap = heatmapFactory.create(this.conf);
  619. this._div = el;
  620. return el;
  621. }
  622. HeatmapOverlay.prototype.draw = function() {
  623. var currentBounds = this._map.getBounds();
  624. if (currentBounds.equals(this.bounds)) {
  625. return;
  626. }
  627. this.bounds = currentBounds;
  628. var ne = this._map.pointToOverlayPixel(currentBounds.getNorthEast()),
  629. sw = this._map.pointToOverlayPixel(currentBounds.getSouthWest());
  630. if (!ne || !sw) {
  631. return
  632. }
  633. var topY = ne.y,
  634. leftX = sw.x,
  635. h = sw.y - ne.y,
  636. w = ne.x - sw.x;
  637. this.conf.element.style.left = leftX + 'px';
  638. this.conf.element.style.top = topY + 'px';
  639. this.conf.element.style.width = w + 'px';
  640. this.conf.element.style.height = h + 'px';
  641. this.heatmap.store.get("heatmap").resize();
  642. if (this.latlngs.length > 0) {
  643. this.heatmap.clear();
  644. var len = this.latlngs.length;
  645. var d = {
  646. max: this.heatmap.store.max,
  647. data: []
  648. };
  649. while (len--) {
  650. var latlng = this.latlngs[len].latlng;
  651. if (!currentBounds.containsPoint(latlng)) {
  652. continue;
  653. }
  654. var divPixel = this._map.pointToOverlayPixel(latlng),
  655. screenPixel = new BMap.Pixel(divPixel.x - leftX, divPixel.y - topY);
  656. var roundedPoint = this.pixelTransform(screenPixel);
  657. d.data.push({
  658. x: roundedPoint.x,
  659. y: roundedPoint.y,
  660. count: this.latlngs[len].c
  661. });
  662. }
  663. this.heatmap.store.setDataSet(d);
  664. }
  665. }
  666. HeatmapOverlay.prototype.pixelTransform = function(p) {
  667. var w = this.heatmap.get("width"),
  668. h = this.heatmap.get("height");
  669. while (p.x < 0) {
  670. p.x += w;
  671. }
  672. while (p.x > w) {
  673. p.x -= w;
  674. }
  675. while (p.y < 0) {
  676. p.y += h;
  677. }
  678. while (p.y > h) {
  679. p.y -= h;
  680. }
  681. p.x = (p.x >> 0);
  682. p.y = (p.y >> 0);
  683. return p;
  684. }
  685. HeatmapOverlay.prototype._moveendHandler = function (e) {
  686. this.setDataSet(this._data);
  687. delete this._data;
  688. this._map.removeEventListener('moveend', this._moveendHandler);
  689. }
  690. HeatmapOverlay.prototype.setDataSet = function(data) {
  691. if (!this._map) {
  692. return;
  693. }
  694. var currentBounds = this._map.getBounds();
  695. var ne = this._map.pointToOverlayPixel(currentBounds.getNorthEast()),
  696. sw = this._map.pointToOverlayPixel(currentBounds.getSouthWest());
  697. if (!ne || !sw) {
  698. this._data = data
  699. this._map.addEventListener('moveend', this._moveendHandler);
  700. }
  701. var mapdata = {
  702. max: data.max,
  703. data: []
  704. };
  705. var d = data.data,
  706. dlen = d.length;
  707. this.latlngs = [];
  708. while (dlen--) {
  709. var latlng = new BMap.Point(d[dlen].lng, d[dlen].lat);
  710. this.latlngs.push({
  711. latlng: latlng,
  712. c: d[dlen].count
  713. });
  714. if (!currentBounds.containsPoint(latlng)) {
  715. continue;
  716. }
  717. var divPixel = this._map.pointToOverlayPixel(latlng),
  718. leftX = this._map.pointToOverlayPixel(currentBounds.getSouthWest()).x,
  719. topY = this._map.pointToOverlayPixel(currentBounds.getNorthEast()).y,
  720. screenPixel = new BMap.Pixel(divPixel.x - leftX, divPixel.y - topY);
  721. var point = this.pixelTransform(screenPixel);
  722. mapdata.data.push({
  723. x: point.x,
  724. y: point.y,
  725. count: d[dlen].count
  726. });
  727. }
  728. this.heatmap.clear();
  729. this.heatmap.store.setDataSet(mapdata);
  730. }
  731. HeatmapOverlay.prototype.addDataPoint = function(lng, lat, count) {
  732. var latlng = new BMap.Point(lng, lat),
  733. point = this.pixelTransform(this._map.pointToOverlayPixel(latlng));
  734. this.heatmap.store.addDataPoint(point.x, point.y, count);
  735. this.latlngs.push({
  736. latlng: latlng,
  737. c: count
  738. });
  739. }
  740. HeatmapOverlay.prototype.toggle = function() {
  741. this.heatmap.toggleDisplay();
  742. }
  743. return HeatmapOverlay;
  744. });