cesium热力图笔记

总结

cesium热力图通过heatmap.js实现,heatmap.js通过颜色和高度直观展示数据分布,生成三维的热力图。

  1. 首先准备数据点位,包含坐标,值,还有颜色数组
  2. 安装下载heatmap.js,通过heatmap算法生成三维热力图
  3. 计算热力图的边界以防分割
  4. 通过贴图的方式将热力图贴到地图上 热力图效果

一、创建cesium

js 复制代码
const viewer = new Cesium.Viewer('cesiumContainer');

二、下载安装heatmap.js

js 复制代码
npm install heatmap.js

下载完之后,还需将源码中一段代码注释掉,或者运行时会报错。在node_modules中找到heatmap.js的文件夹,选择build-》heatmap文件。

img.data在不少浏览器中是只读的,所以运行会报错,要将代码注释掉。

三、创建heatmap工具类

js 复制代码
import h337 from "heatmap.js";
import * as Cesium from "cesium";

export default class Heatmap {
  constructor(viewer, opt) {
    this.viewer = viewer;
    this.opt = opt || {};
    this.dotList = []
    this.list = this.opt.list || [];
    if (!this.list || this.list.length < 2) {
      console.log("热力图点位不得少于3个!");
      return;
    }
    this.dom = undefined;
    this.id = Number(
      new Date().getTime() + "" + Number(Math.random() * 1000).toFixed(0)
    );
    this.canvasw = 200;
    this.createDom();
    
    //配置参数
    let config = {
      container: document.getElementById(`easy3d-heatmap-${this.id}`),
      radius: this.opt.raduis ||20, 
      maxOpacity: 0.7,
      minOpacity: 0,
      blur: 0.75,
      gradient: this.opt.gradient || {  //颜色范围
        ".1": "blue",
        ".5": "green",
        ".7": "yellow",
        ".99": "red",
      },
    };
    //创建热力图
    this.heatmapInstance = h337.create(config);
    this.init();
  }

  init() {
    this.hierarchy = [];
    for (let ind = 0; ind < this.list.length; ind++) {
      let position = Cesium.Cartesian3.fromDegrees(
        this.list[ind].lnglat[0],
        this.list[ind].lnglat[1]
      );
      this.hierarchy.push(position);

      let type = this.list[ind].params.type
      //在地图上添加实体
      let dot = this.viewer.entities.add({
        id:type+this.list[ind].id,
        name:type,
        position: position,
        label: {
          text: type +':'+ Math.floor(this.list[ind].value)+'°C',
          heightReference: 1,
          scale: 0.5,
          verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
        },
        monitoItems: {
          ...this.list[ind].params
        },
        show: true // 确保为true
      });
      this.dotList.push(dot)
    }
    this.polygon = undefined;
    
    const bound = this.getBound(this.hierarchy);
    if (!bound) return;
    let points = [];
    let x_axios = Cesium.Cartesian3.subtract(
      bound.rightTop,
      bound.leftTop,
      new Cesium.Cartesian3()
    );
    x_axios = Cesium.Cartesian3.normalize(x_axios, new Cesium.Cartesian3());
    let y_axios = Cesium.Cartesian3.subtract(
      bound.leftBottom,
      bound.leftTop,
      new Cesium.Cartesian3()
    );
    y_axios = Cesium.Cartesian3.normalize(y_axios, new Cesium.Cartesian3());
    const girthX = Cesium.Cartesian3.distance(bound.rightTop, bound.leftTop);
    const girthY = Cesium.Cartesian3.distance(bound.leftBottom, bound.leftTop);
    for (let i = 0; i < this.hierarchy.length; i++) {
      const p1 = this.hierarchy[i];
      const p_origin = Cesium.Cartesian3.subtract(
        p1,
        bound.leftTop,
        new Cesium.Cartesian3()
      );
      const diffX = Cesium.Cartesian3.dot(p_origin, x_axios);
      const diffY = Cesium.Cartesian3.dot(p_origin, y_axios);
      points.push({
        x: Number((diffX / girthX) * this.canvasw).toFixed(0),
        y: Number((diffY / girthY) * this.canvasw).toFixed(0),
        value: this.list[i].value,
      });
    }
    this.heatmapInstance.addData(points);
    this.createPolygon([
      bound.leftTop,
      bound.leftBottom,
      bound.rightBottom,
      bound.rightTop,
    ]);
  }

  // 以面的形式添加
  createPolygon(positions) {
    this.polygon = this.viewer.entities.add({
      polygon: {
        hierarchy: new Cesium.PolygonHierarchy(positions),
        material: this.heatmapInstance.getDataURL(),
        heightReference: 1,
      },
    });
    this.viewer.zoomTo(this.polygon);
  }

  // 以地图服务的形式添加
  createProvider() {}

  createDom() {
    this.dom = window.document.createElement("div");
    this.dom.id = `easy3d-heatmap-${this.id}`;
    this.dom.className = `easy3d-heatmap`;
    this.dom.style.width = this.canvasw + "px";
    this.dom.style.height = this.canvasw + "px";
    this.dom.style.position = "absolute";
    this.dom.style.display = "none";
    let mapDom = window.document.getElementById(this.viewer.container.id);

    mapDom.appendChild(this.dom);
  }

  destory() {
    let dom = document.getElementById(`easy3d-heatmap-${this.id}`);
    if (dom) dom.remove();
    if (this.polygon) {
      this.viewer.entities.remove(this.polygon);
      this.polygon = undefined;
    }
    if(this.dotList.length){
      for(let i = 0 ; i<this.dotList.length;i++){
        this.viewer.entities.remove(this.dotList[i]);
      }
    }
  }

  // 扩展边界 防止出现热力图被分割
  getBound(positions) {
    let rect = this.toRectangle(positions); // 转为正方形
    let lnglats = cUtil.cartesiansToLnglats(rect);
    let minLat = Number.MAX_VALUE,
      maxLat = Number.MIN_VALUE,
      minLng = Number.MAX_VALUE,
      maxLng = Number.MIN_VALUE;
    const length = rect.length;
    for (let i = 0; i < length; i++) {
      const lnglat = lnglats[i];
      if (lnglat[0] < minLng) {
        minLng = lnglat[0];
      }
      if (lnglat[0] > maxLng) {
        maxLng = lnglat[0];
      }

      if (lnglat[1] < minLat) {
        minLat = lnglat[1];
      }
      if (lnglat[1] > maxLat) {
        maxLat = lnglat[1];
      }
    }

    const diff_lat = maxLat - minLat;
    const diff_lng = maxLng - minLng;

    minLat = minLat - diff_lat / length;
    maxLat = maxLat + diff_lat / length;
    minLng = minLng - diff_lng / length;
    maxLng = maxLng + diff_lng / length;

    return {
      leftTop: Cesium.Cartesian3.fromDegrees(minLng, maxLat),
      leftBottom: Cesium.Cartesian3.fromDegrees(minLng, minLat),
      rightTop: Cesium.Cartesian3.fromDegrees(maxLng, maxLat),
      rightBottom: Cesium.Cartesian3.fromDegrees(maxLng, minLat),
    };
  }

  // 任何图形均转化为正方形
  toRectangle(hierarchy) {
    if (!hierarchy) return;
    let boundingSphere = Cesium.BoundingSphere.fromPoints(
      hierarchy,
      new Cesium.BoundingSphere()
    );
    let center = boundingSphere.center;
    const radius = boundingSphere.radius;

    let modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(center.clone());
    let modelMatrix_inverse = Cesium.Matrix4.inverse(
      modelMatrix.clone(),
      new Cesium.Matrix4()
    );
    let roate_y = new Cesium.Cartesian3(0, 1, 0);

    let arr = [];
    for (let i = 45; i <= 360; i += 90) {
      let roateZ_mtx = Cesium.Matrix3.fromRotationZ(
        Cesium.Math.toRadians(i),
        new Cesium.Matrix3()
      );
      let yaix_roate = Cesium.Matrix3.multiplyByVector(
        roateZ_mtx,
        roate_y,
        new Cesium.Cartesian3()
      );
      yaix_roate = Cesium.Cartesian3.normalize(
        yaix_roate,
        new Cesium.Cartesian3()
      );
      let third = Cesium.Cartesian3.multiplyByScalar(
        yaix_roate,
        radius,
        new Cesium.Cartesian3()
      );
      let poi = Cesium.Matrix4.multiplyByPoint(
        modelMatrix,
        third.clone(),
        new Cesium.Cartesian3()
      );

      arr.push(poi);
    }

    return arr;
  }
}
const cUtil = {
  cartesiansToLnglats: function (cartesians) {
    const lnglats = [];
    for (let i = 0; i < cartesians.length; i++) {
      const cartesian = cartesians[i];
      var cartographic = Cesium.Cartographic.fromCartesian(cartesian);
      var latitude = Cesium.Math.toDegrees(cartographic.latitude);
      var longitude = Cesium.Math.toDegrees(cartographic.longitude);
      lnglats.push([longitude, latitude]);
    }

    return lnglats;
  },
};

四、使用

js 复制代码
//数据示例
 let list = oldlist?.map((item) => {
    return {
      id: item.id,
      lnglat: [item.lon, item.lat],
      value: item.value,
      params: { ...item, type: type },
    };
  });
//实例化
let hotObj = new Heatmap(window.viewer, {
    list: list,
    ...opt,
  });
//销毁热力图
hotObj.destory();
相关推荐
然我9 分钟前
react-router-dom 完全指南:从零实现动态路由与嵌套布局
前端·react.js·面试
一_个前端17 分钟前
Vite项目中SVG同步转换成Image对象
前端
202618 分钟前
12. npm version方法总结
前端·javascript·vue.js
用户876128290737419 分钟前
mapboxgl中对popup弹窗添加事件
前端·vue.js
帅夫帅夫20 分钟前
JavaScript继承探秘:从原型链到ES6 Class
前端·javascript
a别念m20 分钟前
HTML5 离线存储
前端·html·html5
goldenocean1 小时前
React之旅-06 Ref
前端·react.js·前端框架
子林super1 小时前
【非标】es屏蔽中心扩容协调节点
前端
前端拿破轮1 小时前
刷了这么久LeetCode了,挑战一道hard。。。
前端·javascript·面试
代码小学僧1 小时前
「双端 + 响应式」企业官网开发经验分享
前端·css·响应式设计