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();
相关推荐
陈哥聊测试几秒前
当DevOps落地实施撞上技术债务,如何量化债务突破困局
前端·自动化运维·devops
sorryhc5 分钟前
【AI解读源码系列】ant design mobile——CapsuleTabs胶囊选项卡
前端·javascript·react.js
狗头大军之江苏分军11 分钟前
频繁跳槽和稳定工作有什么区别?真的比稳定工作的人差吗?
前端·后端
木子雨廷13 分钟前
Flutter 局部刷新小组件汇总
前端·flutter
用户527096487449019 分钟前
组件库按需引入改造
前端
CryptoRzz29 分钟前
使用Java对接印度股票市场API开发指南
前端·后端
码间舞30 分钟前
道路千万条,安全第一条!要对付XSS等攻击你有什么手段?你知道什么是CSP吗?
前端·后端·安全
狗头大军之江苏分军30 分钟前
第一份工作选错了,会毁掉未来吗?
前端
顾辰逸you31 分钟前
uniapp--HBuilderx编辑器
前端·uni-app
吉星9527ABC35 分钟前
使用烛线图展示二进制01离散量趋势图
前端·echarts·离散量展示·烛线图