效果如下图:
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