转自原文 Cesium热力图实现

生成热力图的算法我是用的一个热力图插件 heatmap.js。
 
heatmap中热力图生成原理:
heatmap中首先会根据输入的渐进色参数,在内部生成一个0-255色值的调色板。
  1. var _getColorPalette = function(config) {
  2. var gradientConfig = config.gradient || config.defaultGradient;
  3. var paletteCanvas = document.createElement('canvas');
  4. var paletteCtx = paletteCanvas.getContext('2d');
  5.  
  6. paletteCanvas.width = 256;
  7. paletteCanvas.height = 1;
  8.  
  9. var gradient = paletteCtx.createLinearGradient(0, 0, 256, 1);
  10. for (var key in gradientConfig) {
  11. gradient.addColorStop(key, gradientConfig[key]);
  12. }
  13.  
  14. paletteCtx.fillStyle = gradient;
  15. paletteCtx.fillRect(0, 0, 256, 1);
  16.  
  17. return paletteCtx.getImageData(0, 0, 256, 1).data;
  18. };
  19.  
  20. 对于输入的点数据,会根据点坐标生成一个黑色圆阴影效果。
  21.  
  22. //生成一个阴影模板
  23. var _getPointTemplate = function(radius, blurFactor) {
  24. var tplCanvas = document.createElement('canvas');
  25. var tplCtx = tplCanvas.getContext('2d');
  26. var x = radius;
  27. var y = radius;
  28. tplCanvas.width = tplCanvas.height = radius*2;
  29.  
  30. if (blurFactor == 1) {
  31. tplCtx.beginPath();
  32. tplCtx.arc(x, y, radius, 0, 2 * Math.PI, false);
  33. tplCtx.fillStyle = 'rgba(0,0,0,1)';
  34. tplCtx.fill();
  35. } else {
  36. var gradient = tplCtx.createRadialGradient(x, y, radius*blurFactor, x, y, radius);
  37. gradient.addColorStop(0, 'rgba(0,0,0,1)');
  38. gradient.addColorStop(1, 'rgba(0,0,0,0)');
  39. tplCtx.fillStyle = gradient;
  40. tplCtx.fillRect(0, 0, 2*radius, 2*radius);
  41. }
  42.  
  43. var tpl;
  44. if (!this._templates[radius]) {
  45. this._templates[radius] = tpl = _getPointTemplate(radius, blur);
  46. } else {
  47. tpl = this._templates[radius];
  48. }
  49. // value from minimum / value range
  50. // => [0, 1]
  51. //根据value值设置阴影的alpha通道,后面可以通过alpha值获取value值
  52. var templateAlpha = (value-min)/(max-min);
  53. // this fixes #176: small values are not visible because globalAlpha < .01 cannot be read from imageData
  54. shadowCtx.globalAlpha = templateAlpha < .01 ? .01 : templateAlpha;
  55.  
  56. shadowCtx.drawImage(tpl, rectX, rectY);
  57.  
  58. // update renderBoundaries
  59. if (rectX < this._renderBoundaries[0]) {
  60. this._renderBoundaries[0] = rectX;
  61. }
  62. if (rectY < this._renderBoundaries[1]) {
  63. this._renderBoundaries[1] = rectY;
  64. }
  65. if (rectX + 2*radius > this._renderBoundaries[2]) {
  66. this._renderBoundaries[2] = rectX + 2*radius;
  67. }
  68. if (rectY + 2*radius > this._renderBoundaries[3]) {
  69. this._renderBoundaries[3] = rectY + 2*radius;
  70. }
  71.  
  72. }

首先呢,阴影是黑色的,所以接下来heatmap会进行一个像素点重新着色的过程,根据每个点的alpha值*4(rgba步长)得出一个offset,然后从调色板上取颜色。因为上面设置了阴影透明度效果是递减的,所以在获取颜色的时候,就能获得一个平滑的渐变效果。这样就得到了热力图。

  1. _colorize: function() {
  2. var x = this._renderBoundaries[0];
  3. var y = this._renderBoundaries[1];
  4. var width = this._renderBoundaries[2] - x;
  5. var height = this._renderBoundaries[3] - y;
  6. var maxWidth = this._width;
  7. var maxHeight = this._height;
  8. var opacity = this._opacity;
  9. var maxOpacity = this._maxOpacity;
  10. var minOpacity = this._minOpacity;
  11. var useGradientOpacity = this._useGradientOpacity;
  12.  
  13. if (x < 0) {
  14. x = 0;
  15. }
  16. if (y < 0) {
  17. y = 0;
  18. }
  19. if (x + width > maxWidth) {
  20. width = maxWidth - x;
  21. }
  22. if (y + height > maxHeight) {
  23. height = maxHeight - y;
  24. }
  25.  
  26. var img = this.shadowCtx.getImageData(x, y, width, height);
  27. var imgData = img.data;
  28. var len = imgData.length;
  29. var palette = this._palette;
  30.  
  31. for (var i = 3; i < len; i+= 4) {
  32. var alpha = imgData[i];
  33. var offset = alpha * 4;
  34.  
  35. if (!offset) {
  36. continue;
  37. }
  38.  
  39. var finalAlpha;
  40. if (opacity > 0) {
  41. finalAlpha = opacity;
  42. } else {
  43. if (alpha < maxOpacity) {
  44. if (alpha < minOpacity) {
  45. finalAlpha = minOpacity;
  46. } else {
  47. finalAlpha = alpha;
  48. }
  49. } else {
  50. finalAlpha = maxOpacity;
  51. }
  52. }
  53.  
  54. imgData[i-3] = palette[offset];
  55. imgData[i-2] = palette[offset + 1];
  56. imgData[i-1] = palette[offset + 2];
  57. imgData[i] = useGradientOpacity ? palette[offset + 3] : finalAlpha;
  58.  
  59. }
  60.  
  61. img.data = imgData;
  62. this.ctx.putImageData(img, x, y);
  63.  
  64. this._renderBoundaries = [1000, 1000, 0, 0];
  65.  
  66. },
采用这种利用透明度来对应获取颜色值的好处就是这种渐变的过程比较柔和,渐变的效果更好。
 
so一开始为什么先绘制alpha渐变的黑点呢?是因为在纯色图像上方便计算它的alpha分量,这样两点相交的区域就会根据alpha分量进行叠加,在转成彩图的时候就可以生成相应的值。
为什么不直接用彩色点叠加呢?是因为彩色点的RGBA并不是简单的线性叠加关系。
 
在Cesium上使用的原理比较简单,就是根据输入点的坐标范围计算一个包围盒,创建一个rectangle geometry。然后呢,通过heatmap.js生成热力图,当做纹理贴到rectangle上面。在每一层级设置不同的radius,相当于在相机缩放的时候每一级都会生成一张热力图,然后更换纹理,实现缩放时的聚合离散效果。
 
这个过程需要注意的是以下几点:
1. 如何将经纬度值映射到纹理上对应位置?
首先需要计算生成纹理的宽高像素,这里我仿照了cesiumheatmap的算法,根据rectangle投影后的范围和初始heatmap的设置的canvasSize参数来计算出一个宽高值。
 
2.用heatmap绘出的canvas贴到rectangle上会有黑色背景
这个可以在shader里面将黑色像素过滤掉即可。
 
'vec4 heightValue = texture2D(image, materialInput.st);' +
'if(heightValue.r<1.0/255.0) heightValue.a= 0.0; ' +
 
3.缩放实现聚合离散
上面提到过了,根据几个层级范围设置一个radius数组,相机缩放到哪个层级就相应的改变它的radius进行重绘,然后替换纹理。
 最终实现效果还可以,能够平滑过渡。

在Openlayer中实现热力图起始是很方便的。具体可参考下面的几篇文章。

Openlayers中热力图的实现

openlayers3 ol3热力图 json

进一步学习的参考资料

至于绘制的过程和原理、及完整代码,可以参考
http://www.wangshaoxing.com/blog/how-to-draw-a-heatmap.html
code
https://github.com/wshxbqq/WebGL-HeatMap

 
 
 
 
 
 

Cesium热力图实现的更多相关文章

  1. cesium 热力图

  2. Cesium专栏-热力图(附源码下载)

    Cesium Cesium 是一款面向三维地球和地图的,世界级的JavaScript开源产品.它提供了基于JavaScript语言的开发包,方便用户快速搭建一款零插件的虚拟地球Web应用,并在性能,精 ...

  3. cesium 水面、淹没 效果

    水面效果 参考: http://cesiumcn.org/topic/158.html http://api.rivermap.cn/cesium/rivermap/map.html https:// ...

  4. Cesium学习系列汇总

    内容比较多,完整看完需要大概10分钟,废话不多说,撸起袖子,加油干!!! 1.前言 按照套路,先介绍一下什么是Cesium. Cesium ['siːzɪəm]是JavaScript开源库,通过Ces ...

  5. 将Cesium Tools用于更好的构建管理

    Cesium中文网:http://cesiumcn.org/ | 国内快速访问:http://cesium.coinidea.com/ Cesium技术正在给建筑业带来革命性的变化.我们与 partn ...

  6. cesium自定义气泡窗口infoWindow

    一.自定义气泡窗口与cesium默认窗口效果对比: 1.cesium点击弹出气泡窗口显示的位置固定在地图的右上角,默认效果: 2.对于习惯arcgis或者openlayer气泡窗口样式的giser来说 ...

  7. cesium核心类Viewer简介

    1.简单描述Viewer Viewer类是cesium的核心类,是地图可视化展示的主窗口,cesium程序应用的切入口,扮演必不可少的核心角色. 官网的英文解析如下: A base widget fo ...

  8. Cesium简介以及离线部署运行

    Cesium简介 cesium是国外一个基于JavaScript编写的使用WebGL的地图引擎,一款开源3DGIS的js库.cesium支持3D,2D,2.5D形式的地图展示,可以自行绘制图形,高亮区 ...

  9. 基于开源项目SharpMap的热力图(HeatLayer)实现。

    当前公司需要一个用时较少的热力图呈现方案,在避免较底层的GDI开发和比较了多家GIS产品的实际效果之后,团队决定用sharpMap的API来实现,由于之前框架采用的是另外一个开源项目GMap.net, ...

随机推荐

  1. 笔记-python-urllib

    笔记-python-urllib 1.      简介 PYTHON3中将urllib,urllib2整合到URLLIB中 包括以下模块 urllib.request 请求模块(核心) urllib. ...

  2. INDEX && PRIMARY KEY && UNIQUE KEY

    When I have do some sql tody, some confusion come up to me. Its about the index && PRIMARY K ...

  3. 光学字符识别OCR-2

    灰度聚类 接着我们就对图像的色彩进行聚类.聚类的有两个事实依据:         1.灰度分辨率   肉眼的灰度分辨率大概为40,因此对于像素值254和255,在我们肉眼看来都 只是白色:       ...

  4. JavaScript简单继承

    很多C#或C++开发人员习惯使用继承来开发项目,所以当他们想学习JavaScript语言时,第一个问题一般是:“我怎么在JavaScript中使用继承?”. 实际上JavaScript使用了一种不同于 ...

  5. tar.xz结尾的文件的解压缩方法

    例如: codeblocks-13.12-1_i386.debian.stable.tar 这个压缩包也是两层压缩,外面是xz压缩方式,里层是tar压缩方式. 解压缩方法: $xz -d ***.ta ...

  6. Selenium WebDriver- 操作frame中的页面元素

    #encoding=utf-8 import unittest import time from selenium import webdriver from selenium.webdriver i ...

  7. [python学习篇][廖雪峰][2][高级函数] map 和reduce

    我们先看map.map()函数接收两个参数,一个是函数,一个是序列,map将传入的函数依次作用到序列的每个元素,并把结果作为新的list返回. 举例说明,比如我们有一个函数f(x)=x2,要把这个函数 ...

  8. [办公软件][2]screenToGif

    https://github.com/NickeManarin/ScreenToGif/wiki/help 下载: .Net Framework 4.6.1   https://www.microso ...

  9. 2017ICPC北京 J:Pangu and Stones

    #1636 : Pangu and Stones 时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 In Chinese mythology, Pangu is the fi ...

  10. Welcome-to-Swift-23访问控制(Access Control)

    访问控制可以限定你在源文件或模块中访问代码的级别,也就是说可以控制哪些代码你可以访问,哪些代码你不能访问.这个特性可以让我们隐藏功能实现的一些细节,并且可以明确的指定我们提供给其他人的接口中哪些部分是 ...