首页 > 其他分享 >vue+cesium+heatmap.js 实现热力图

vue+cesium+heatmap.js 实现热力图

时间:2024-06-07 16:00:11浏览次数:12  
标签:vue Cartesian3 viewer lnglat js var heatmap let Cesium

效果如下图:

1.安装heatmap.js

npm i heatmap.js

官网:heatmap.js : Dynamic Heatmaps for the Web (patrick-wied.at)

2. 把这两个文件放到项目里

heatmap.js

import util from "./util"
import h337 from 'heatmap.js'
/**
 * @description 二维热力图类,基于h337类扩展
 * @class
 */
class Heatmap {
    /**
     * @param {Cesium.Viewer} viewer 地图viewer对象 
     * @param {Object} opt 基础参数
     * @param {Array} opt.list 热力值数组
     * @param {Array} opt.raduis 热力点半径
     * @param {Array} opt.gradient 颜色配置
     */
    constructor(viewer, opt) {
        this.viewer = viewer;
        this.opt = opt || {};
        this.list = this.opt.list || [];
        if (!this.list || this.list.length < 2) {
            console.log("热力图点位不得少于3个!");
            return;
        }
        /**
         *@property {Cesium.Entity} polygon 热力图面
         */
        this.polygon = undefined;
        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 || 10,
            maxOpacity: .7,
            minOpacity: 0,
            blur: .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);
        }
        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({
            name:'heatentity',
            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;
        }
    }

    // 扩展边界 防止出现热力图被分割
    getBound(positions) {
        let rect = this.toRectangle(positions); // 转为正方形
        let lnglats = util.cartesiansToLnglats(rect,this.viewer);
        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;
    }


}

export default Heatmap;

util.js

/**
 * 三维基础方法
 * @example util.getCameraView(viewer);
 * @exports util
 * @alias util
 */
let util = {};
/**
 * 世界坐标转经纬度
 * @param {Cesium.Cartesian3 } cartesian 世界坐标
 * @param {Cesium.Viewer} viewer 当前viewer对象
 * @returns { Array } 经纬度坐标s
 */
util.cartesianToLnglat = function (cartesian, viewer) {
    if (!cartesian) return [];
    viewer = viewer || window.viewer;
    var lnglat = Cesium.Cartographic.fromCartesian(cartesian);
    var lat = Cesium.Math.toDegrees(lnglat.latitude);
    var lng = Cesium.Math.toDegrees(lnglat.longitude);
    var hei = lnglat.height;
    return [lng, lat, hei];
}

util.getViewCenter = (viewer) => {
    if (!viewer) return;
    var rectangle = viewer.camera.computeViewRectangle();
    var west = rectangle.west / Math.PI * 180;
    var north = rectangle.north / Math.PI * 180;
    var east = rectangle.east / Math.PI * 180;
    var south = rectangle.south / Math.PI * 180;
    return [(east + west) / 2, (north + south) / 2]
}

/**
 * 世界坐标数组转经纬度数组
 * @param {Cesium.Cartesian3[]} cartesians 世界坐标数组
 * @param {Cesium.Viewer} viewer 当前viewer对象
 * @returns { Array } 经纬度坐标数组
 */
util.cartesiansToLnglats = function (cartesians, viewer) {
    if (!cartesians || cartesians.length < 1) return;
    viewer = viewer || window.viewer;
    if (!viewer) {
        console.log('util.cartesiansToLnglats方法缺少viewer对象');
        return;
    }
    var arr = [];
    for (var i = 0; i < cartesians.length; i++) {
        arr.push(util.cartesianToLnglat(cartesians[i], viewer));
    }
    return arr;
}

/**
 * 经纬度坐标数组转世界坐标数组
 * @param {Array[]} lnglats 经纬度坐标数组
 * @returns {Cesium.Cartesian3[]} cartesians 世界坐标数组
 * @example util.lnglatsToCartesians([[117,40],[118.41]])
 */
util.lnglatsToCartesians = function (lnglats) {
    if (!lnglats || lnglats.length < 1) return;
    var arr = [];
    for (var i = 0; i < lnglats.length; i++) {
        var c3 = Cesium.Cartesian3.fromDegrees(lnglats[i][0], lnglats[i][1], lnglats[i][2] || 0);
        arr.push(c3);
    }
    return arr;
}

/**
 * 视角定位方法
 * @param {Object} opt 定位参数
 * @param {Cartesian3|Array} opt.center 当前定位中心点
 * @param {Number} opt.heading 当前定位偏转角度 默认为0 
 * @param {Number} opt.pitch 当前定位仰俯角 默认为-60
 * @param {Number} opt.range 当前定位距离 默认为1000米
 * @param {Cesium.Viewer} viewer 当前viewer对象
 */
util.flyTo = function (opt, viewer) {
    if (!viewer) {
        console.log('util.flyTo缺少viewer对象');
        return;
    }
    opt = opt || {};
    let center = opt.center;
    if (!center) {
        console.log("缺少定位坐标!");
        return;
    }
    if (center instanceof Cesium.Cartesian3) {
        viewer.camera.flyToBoundingSphere(new Cesium.BoundingSphere(center), {
            offset: new Cesium.HeadingPitchRange(
                Cesium.Math.toRadians(opt.heading || 0),
                Cesium.Math.toRadians(opt.pitch || -60),
                opt.range || 10000
            )
        });
    }
    if (center instanceof Array) {
        var boundingSphere = new Cesium.BoundingSphere(Cesium.Cartesian3.fromDegrees(center[0], center[1], center[2]));
        viewer.camera.flyToBoundingSphere(boundingSphere, {
            offset: new Cesium.HeadingPitchRange(
                Cesium.Math.toRadians(opt.heading || 0),
                Cesium.Math.toRadians(opt.pitch || -60),
                opt.range || 10000
            )
        });
    }
}

/**
 * 获取当相机姿态
 * @param {Cesium.Viewer} viewer 当前viewer对象
 * @returns {Object} cameraView 当前相机姿态
 */
util.getCameraView = function (viewer) {
    viewer = viewer || window.viewer;
    if (!viewer) {
        console.log('util.getCameraView缺少viewer对象');
        return;
    }
    var camera = viewer.camera;
    var position = camera.position;
    var heading = camera.heading;
    var pitch = camera.pitch;
    var roll = camera.roll;
    var lnglat = Cesium.Cartographic.fromCartesian(position);

    var cameraV = {
        "x": Cesium.Math.toDegrees(lnglat.longitude),
        "y": Cesium.Math.toDegrees(lnglat.latitude),
        "z": lnglat.height,
        "heading": Cesium.Math.toDegrees(heading),
        "pitch": Cesium.Math.toDegrees(pitch),
        "roll": Cesium.Math.toDegrees(roll)
    };
    return cameraV;
}

/**
 * 设置相机姿态 一般和getCameraView搭配使用
 * @param {Object} cameraView 相机姿态参数
 * @param {Number} cameraView.duration 定位所需时间
 * @param {Cesium.Viewer} viewer 当前viewer对象
 */
util.setCameraView = function (obj, viewer) {
    viewer = viewer || window.viewer;
    if (!viewer) {
        console.log('util.setCameraView缺少viewer对象');
        return;
    }
    if (!obj) return;
    var position = obj.destination || Cesium.Cartesian3.fromDegrees(obj.x, obj.y, obj.z); // 兼容cartesian3和xyz
    viewer.camera.flyTo({
        destination: position,
        orientation: {
            heading: Cesium.Math.toRadians(obj.heading || 0),
            pitch: Cesium.Math.toRadians(obj.pitch || 0),
            roll: Cesium.Math.toRadians(obj.roll || 0)
        },
        duration: obj.duration === undefined ? 3 : obj.duration,
        complete: obj.complete
    });
}

/**
 * 计算当前三角形面积
 * @param {Cesium.Cartesian3 } pos1 当前点坐标1
 * @param {Cesium.Cartesian3 } pos2 当前点坐标2
 * @param {Cesium.Cartesian3 } pos3 当前点坐标3
 * @returns {Number} area,面积
 */
util.computeAreaOfTriangle = function (pos1, pos2, pos3) {
    if (!pos1 || !pos2 || !pos3) {
        console.log("传入坐标有误!");
        return 0;
    }
    var a = Cesium.Cartesian3.distance(pos1, pos2);
    var b = Cesium.Cartesian3.distance(pos2, pos3);
    var c = Cesium.Cartesian3.distance(pos3, pos1);
    var S = (a + b + c) / 2;
    return Math.sqrt(S * (S - a) * (S - b) * (S - c));
}

export default util;

3. 用法 

在需要使用的页面引入heatmap.js

初始化完cesium后 直接用 Heatmap

例:import Heatmap from "@/utils/heatmap.js"

new Heatmap(viewer,{

list:[ ]

})

viewer 是初始化的cesium

//初始化cesium
initMap() {
    // Cesium token授权
    Cesium.Ion.defaultAccessToken = "";

    // 场景图层url
    let imageryUrl = new Cesium.WebMapTileServiceImageryProvider({
        url: "",
        layer: "cia",
        style: "default",
        tileMatrixSetID: "w",
        format: "tiles",
        maximumLevel: 18
    })

    let terrainUrl = ""
    // if (window._CONFIG["mark"] == "TEST") {
    //     imageryUrl = window._CONFIG['fileUrl'] + "/maptiles/lssk/SatelliteMap/{z}/{x}/{y}.png" //地图切片
    //     terrainUrl = window._CONFIG['fileUrl'] + "/damtiles/Extract_QingDao10m" //地形切片
    // }
    // if (window._CONFIG["mark"] == "PRODUCE") {
    //     imageryUrl = window._CONFIG['fileUrl'] + "/lssk/map/SatelliteMap/{z}/{x}/{y}.png" //地图切片
    //     terrainUrl = window._CONFIG['fileUrl'] + "/lssk/dem/Extract_QingDao10m" //地形切片
    // }

    // 场景地图基础配置
    viewer = new Cesium.Viewer("cesiumContainer", {
        infoBox: false, //默认弹窗控件
        geocoder: false, // 地理位置查询定位控件
        vrButton: false, //VR按钮控件
        homeButton: false, // 默认相机位置控件
        animation: true, // 控制场景动画的播放速度控件
        timeline: true, // 时间滚动条控件
        navigationHelpButton: false, // 默认的相机控制提示控件
        fullscreenButton: false, // 全屏控件
        sceneModePicker: false, //场景模式切换控件
        baseLayerPicker: false, // 底图切换控件
        selectionIndicator: false, //对象指示器控件
        // shadows: true,
        // imageryProvider: new Cesium.UrlTemplateImageryProvider({
        //     url: imageryUrl,
        //     // fileExtension: "png",
        // }),
        terrain: Cesium.Terrain.fromWorldTerrain(),
        // terrainProvider: new Cesium.CesiumTerrainProvider({
        //     url: terrainUrl,
        // }),  
    });
    viewer.imageryLayers.addImageryProvider(imageryUrl);
    viewer.scene.debugShowFramesPerSecond = false; //显示帧率
    viewer.timeline.container.style.display = "none"; //隐藏时间线控件
    viewer.animation.container.style.display = "none"; //隐藏动画控件
    viewer.scene.globe.depthTestAgainstTerrain = true; //开启地形遮挡
    viewer._cesiumWidget._creditContainer.style.display = "none"; //去除    Cesium版权信息
    // viewer.scene.screenSpaceCameraController.enableCollisionDetection = false;
    viewer.scene.globe.enableLighting = false;
    viewer.scene.postProcessStages.fxaa.enabled = true;
    //鼠标操作调整
    viewer.scene.screenSpaceCameraController.zoomEventTypes = [
        Cesium.CameraEventType.WHEEL,
        Cesium.CameraEventType.PINCH,
    ];

    viewer.scene.screenSpaceCameraController.tiltEventTypes = [
        Cesium.CameraEventType.PINCH,
        Cesium.CameraEventType.RIGHT_DRAG,
    ];
    // viewer.extend(Cesium.viewerCesium3DTilesInspectorMixin);
    // const inspectorViewModel = viewer.cesium3DTilesInspector.viewModel;


}
 new Heatmap(初始化cesium的对象, {
     //list 的格式如下
     list: [
       {
         lnglat:[120.538953,36.123219],
         value:70
       },
       {
          lnglat:[120.526754,36.152332],
          value:20
       },
       {
         lnglat:[120.527355,36.154413],
         value:30
       },
       {
         lnglat:[120.531872,36.168672],
         value:40
       },
       {
         lnglat:[120.525746,36.135881],
         value:50
       },
       {
         lnglat:[120.522506,36.137677],
         value:60
       },
     ]
  })

 具体使用:

<template>
    <div class="HomePage">
      <div id="cesiumContainer"></div>
    </div>
  </template>
  <script>
  var viewer, camera;
  import Heatmap from "@/utils/heatmap.js"
  import {
    initMap,
  } from "@/assets/js/config";
 
  export default {
    components: {
    
    },
    name: "HomePage",
    data() {
      return {
    
        heatList:[]
      };
    },
    methods: {
    },
    created() {},
    mounted() {
  
      window.viewer = viewer;
      window.camera = camera;
  
      initMap();
      // // 初始相机视角
      window.viewer.scene.camera.setView({
        destination: Cesium.Cartesian3.fromDegrees(
          120.6086174,
          36.048038,
          14567.8
        ),
        orientation: Cesium.HeadingPitchRoll.fromDegrees(330, -49, 0),
        endTransform: Cesium.Matrix4.IDENTITY,
      });
  
      let list = [];
      list = [
        {
          lnglat:[120.538953,36.123219],
          value:70
        },
        {
          lnglat:[120.526754,36.152332],
          value:20
        },
        {
          lnglat:[120.527355,36.154413],
          value:30
        },
        {
          lnglat:[120.531872,36.168672],
          value:40
        },{
          lnglat:[120.525746,36.135881],
          value:50
        },
        {
          lnglat:[120.522506,36.137677],
          value:60
        },
      ]
    //   for (let i = 0; i < 10; i++) {
    //       list.push({
    //           "lnglat": [
    //               120.65 + Math.random() * .1 * (Math.random() > 0.5 ? 1 : -1),
    //               36.24 + Math.random() * .1 * (Math.random() > 0.5 ? 1 : -1)
    //           ],
    //           "value": 10 * Math.random()
    //       })
    //   }
      new Heatmap(window.viewer, {
          list: list
      })
  
  
    
    },
    beforeUnmount() {
      // 销毁场景
      destoryMap();
    },
  };
  </script>
  <style scoped>
  .HomePage {
    width: 1920rem;
    height: 1080rem;
    position: absolute;
    top: 0;
    left: 0;
    background: #08162a;
    overflow: hidden;
  }
  .HomePage #cesiumContainer {
    position: absolute;
    width: 100%;
    height: 100%;
  }
  </style>
  

标签:vue,Cartesian3,viewer,lnglat,js,var,heatmap,let,Cesium
From: https://blog.csdn.net/m0_47114387/article/details/139527078

相关文章

  • vue3+TypeScript
    1.Vue3简介2020年9月18日,Vue.js发布版3.0版本,代号:OnePiece(n经历了:4800+次提交、40+个RFC、600+次PR、300+贡献者官方发版地址:Releasev3.0.0OnePiece·vuejs/core截止2023年10月,最新的公开版本为:3.3.41.1.【性能的提升】打包大小减少41%。初次渲染快......
  • 学习前端3DThreejs一篇就够了,从入门到实战
    vue安装three.jsnpminstall--savethree引入three.jsimport*asTHREEfrom'three'three.js结构### three.js坐标创建一个场景scene场景,camera相机,renderer渲染器创建一个场景this.scene=newTHREE.Scene()创建一个透视摄像机this.camera=newTHR......
  • HTML5+CSS3+JS小实例:网格图库
    实例:网格图库技术栈:HTML+CSS+JS效果:源码:【HTML】<!DOCTYPEhtml><htmllang="zh-CN"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width,initial-scale=1.0">......
  • Vue的data中对象新增属性后,页面却不渲染(未渲染)
    前提背景:在data中定义了一个数组 usableList:[],给数组中的每个对象添加checked属性(用来查看数据的勾选状态) 问题:勾选后,数组的数据已经改变了,但是未渲染到页面中 问题原因:在于在Vue实例创建时,对象新增的属性并未声明,因此就没有被Vue转换为响应式的属性,自然就不会触发视......
  • vue3多个表单一起校验
    当定义了多个表单但是保存时需要同时校验的时候可以这样做<template><el-form:model="userForm1"label-width="auto"ref="userFormRef1"></el-form><el-form:model="userForm2"label-width="auto......
  • 【S082】Springboot+Vue电子商城购物系统 购物 商务 前后端分离 含文档
    运行截图:用户登陆商城首页商品分类商品详情页我的购物车下单页面我的订单后台首页用户管理头像管理商品分类管理轮播图管理商品管理订单管理图表分析收入排行榜项目组成:项目源码:项目文档:源码获取⬇⬇⬇......
  • vue2使用Clodop插件打印表格,分页,每页显示页头页尾,自定义纸张大小
    一、前往lodop官网,下载插件,下载中心-Lodop和C-Lodop官网主站 这里下载的window64位的,将插件安装好,运行,会看到引入项目第一种、可以直接将script标签放入vue的head中,在项目运行时自动加载。第二种、也可以将js文件下载至本地,通过import引入,前提是将CLODOP对象export出......
  • Vue3入门 - vue3相比于vue2的优点,及如何创建Vue3项目
    目录一、认识Vue31.Vue2选项式API  vs Vue3组合式API2.Vue3的优势二、使用create-vue搭建Vue3项目1.认识create-vue2.使用create-vue创建项目3.熟悉项目和关键文件一、认识Vue31.Vue2选项式API  vs Vue3组合式API<script>exportdefault{......
  • 【JS封装-数组操作】强化编程实践:精选JavaScript函数封装集锦-关于数组操作(数组去重、
    目录数组去重数组快速排序过滤数组映射数组数组扁平化数组求和数组最大值数组最小值数组切片数组乱序(洗牌算法)数组去重/***去除数组中的重复项。*@param{Array}array要去重的数组。*@returns{Array}去重后的数组。*/functionuniqueArray(array......
  • vue开发环境搭建之安装
    一、node.js安装node.js官网地址:https://nodejs.org/en下载node.js下载完成后,双击安装一直下一步:二、设置镜像地址查看当前镜像源npmconfiggetregistry切换镜像源下载阿里云:npmconfigsetregistryhttps://registry.npm.aliyun.com/淘宝:npmconfigsetregis......