Cesium-热力图实现

  • Post author:
  • Post category:其他




1 插件选择heatmap.js

官网:			http://www.patrick-wied.at/static/heatmapjs/
api: 			http://www.patrick-wied.at/static/heatmapjs/docs.html
官网例子:		http://www.patrick-wied.at/static/heatmapjs/examples.html



2 为什么选择heatmap.js

轻便/操作简单/兼容性高,目前发现最好的轮子



3 heatmap.js学习



3.1 helloworld入手

效果图:

在这里插入图片描述

代码:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="/heatmap.js-master/build/heatmap.min.js"></script>
</head>
<style>
    .heatmap {
        width: 500px;
        height: 500px;
        border: solid 1px red;
    }
</style>

<body>
    <div class="heatmap">

    </div>
</body>
<script>
    var heatmapInstance = h337.create({
        container: document.querySelector('.heatmap')
    });
    heatmapInstance.setData({
        max: 100,
        data: [{
            x: 10,
            y: 15,
            value: 100
        }]
    });
</script>

</html>

这里基本可以知道:

(1) 引用规则

(2) 显示规则

(3) 数据规则



3.2 最小化配置demo

效果图

在这里插入图片描述

这个例子我们可以获得什么?大概就是知道了,卧槽,原来效果差不多是这样的.



3.3 api学习

名称 作用
create(configObject) 使用h337.create创建热图实例。可以使用configObject自定义热图,configObject参数是必需的
heatmapInstance.addData(object array)
heatmapInstance.setData(object) 用数据集初始化一个热图实例
heatmapInstance.setDataMax(number) 更改数据集的上限并触发完全重新渲染
heatmapInstance.setDataMin(number) 更改数据集的下限并触发完全重新渲染
heatmapInstance.configure(configObject) 初始化后重新配置热图实例。触发完全重新渲染
heatmapInstance.getValueAt(object) 返回数据点位置的值
heatmapInstance.getData() 返回一个可持久且可重新导入(带有setData)的JSON对象
heatmapInstance.getDataURL() 热图实例的base64编码的dataURL
heatmapInstance.repaint() 返回heatmapInstance,重绘整个heatmap画布

贴上初始化options的属性

 var heatmapInstance = h337.create({
        container: document.querySelector('.heatmap'),
        // backgroundColor: "rgba(0,142,0,0.8)", //控制背景颜色
        // gradient: {
        //     '.3': 'rgb(0, 119, 255)',
        //     '.6': 'rgb(255, 136, 0)',
        //     '.9': 'rgb(255, 0, 0)'
        // }
        // radius: 100 //热力点半径
        // opacity:0.7 //热力点透明度 
        // maxOpacity: 0.8,//热图中最大值具有的最大不透明度
        // minOpacity:0.2//热图中最小值的最小不透明度
        // onExtremaChange:function(data){
        //     console.log(data);
        // }
        // blur:0.5//将应用于所有数据点的模糊因子。模糊因子越高,渐变将越平滑
        // xField://数据点中x坐标的属性名称
        // yField://数据点中y坐标的属性名称
        // valueField://数据点中值的名称
    });

关于gradient属性解释下:

热力图的值,假设是0–100,如果0用冷色调(蓝色)表示,100用红色表示,那么在这之间,通过gradient设置间断颜色,则可以形成渐变色;

效果图如下:

在这里插入图片描述



cesium融合heatmap.js

效果图先行:

在这里插入图片描述

诶,看看,差不多了,有内味道了;但是这样肯定是不行的,只是单纯的叠加…那么如果像百度地图啊,那种贴到地球表面的效果呢?而且在webgl中,我们一般拿到的数据也是经纬度数据,比如:

{
	longitude:113.215,
	latitude:23.256,
	value:100
}

所以要思考几个问题:

  1. 数据转换
  2. 表现形式

    要满足好看,方便,高效的原理

    幸好,这一切都有了解决办法

解决方案:

(1) 数据转换

cesium已经提供了屏幕坐标转换为经纬的方法

(2) 表现形式

为了满足热力图的任意性,我们把其贴在多边形的图形范围中,那么我们首先要有一个任意多边形

效果图

在这里插入图片描述

代码:

viewer.scene.globe.depthTestAgainstTerrain = false;
    var drawType = new Cesium.PolygonOutlineGeometry.fromPositions({
        positions: Cesium.Cartesian3.fromDegreesArray([
            -95, 37.0,
            -95, 32.0, 
            -90, 33.0,          
            -90, 31.0,                 
        ]),

    })
    var polyGon = new Cesium.GeometryInstance({
        geometry: drawType,
        attributes: {
            color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.WHITE)
        }
    })
    var object = new Cesium.Primitive({
        geometryInstances: polyGon,
        appearance: new Cesium.PerInstanceColorAppearance({
            // flat : true,
            renderState: {
                lineWidth: Math.min(20.0, scene.maximumAliasedLineWidth)
            }
        })
    })
    viewer.scene.primitives.add(object);

然后我们要把热力图,贴到多边形中

经过本屌一下午不懈的努力,终于做出来了:

在这里插入图片描述

期间踩了一个坑,就是千万不要用PolygonOutlineGeometry去绘制多边形啊,因为它只是边,没办法填充canvas

代码:

//关闭地形检测
    viewer.scene.globe.depthTestAgainstTerrain = false;
    //得到heatmap.js绘制出的canvas
    var canvas = document.querySelector('.heatmap-canvas');
    //使用primitive绘制任意多边形
    viewer.scene.primitives.add(new Cesium.Primitive({
        geometryInstances: new Cesium.GeometryInstance({
            geometry: new Cesium.PolygonGeometry.fromPositions({
                positions: Cesium.Cartesian3.fromDegreesArray([
                    -95, 37.0,
                    -95, 32.0,
                    -90, 31.0,
                    -90, 33.0,
                ]),

            }),
        }),
        appearance: new Cesium.MaterialAppearance({
            flat: true,
            translucent: true,
            material: new Cesium.Material({
                fabric: {
                    type: 'Image',
                    uniforms: {
                        image: canvas,//绑定canvas
                    },
                }
            }),
        })
    }));



继续深挖

至此呢,一个基本的轮廓就已经成型了;但是,这还不能用于生产环境;我们知道,GIS给出的热力图,基本都是经纬度类型的.跟平面坐标关系不大,那么有什么思路能够解决这个问题呢?

1 利用cesium自带的笛卡尔3接口将WGS84经纬度坐标转成平面二维坐标,然后传入heatmap.js的对象绘制出来;但是呢,这样存在一个问题,首先是效率的问题,由于数据量基本都是万,十万级别的,前端进行转换效率会有影响;其次,当转换完成后,绘制出来的热力点由于不支持小数点,会造成精度失真,理论上,这种方法基本被淘汰

2 我们知道,cesium是能够绘制单点的,那么我们能不能根据经纬度,将绘制的单点画出来,然后在将热力点贴上去呢?理论上效率和精度都保证了.但是实现的过程当中由于是leaf插件,和cesium不匹配,就没去研究了.

3 然后去研究了cesium的插件cesium-heatmap…看heatmap.js源码的时候,发现竟然不能实时刷新radius…我的个老天啊…

然后默默的去改源码…竟然还是用的gruntfile进行打包的…emmmm,实属头疼.

默默的去加载cesium-heatmap的情形:

在这里插入图片描述

这样算是解决了经纬转换的关系;但是源码作者也是用rectangle进行映射的,感觉这种算法会比较失真,最最关键的是改了heatmap的源码竟然没有办法进行configure刷新.那我用滚轮进行缩放的动态效果怎么实现?而且效率也堪忧…

算了,博主默默的去改heatmap的源码并做坐标转换试试了.



版权声明:本文为wangbiao9292原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。