cesium 添加点位自定义弹窗跟随场景移动 完整代码演示可直接copy使用
1 效果图:
2 深入理解
就是原始点位的数据
id>property
点位真实渲染到球体上的笛卡尔坐标系
id>_polyline 的路径下 可以通过
3 代码示例
<!DOCTYPE html>
<html lang="en"><head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link href="https://cesium.com/downloads/cesiumjs/releases/1.85/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
<script src="https://unpkg.com/@turf/turf/turf.min.js"></script>
<script src="https://cesium.com/downloads/cesiumjs/releases/1.85/Build/Cesium/Cesium.js"></script>
<script src="https://cdn.staticfile.net/vue/2.7.0/vue.min.js"></script>
</head><body>
<div id="app">
<button @click="mapClick">主动触发点位弹窗</button>
<button @click="removeLayer('site_layer')">清除点位图层</button>
<div id="cesiumContainer">
<div id="popup">
<div class="cesium-popup" @closeMap="closeMapPop" v-if="popVisible" ref="overlay">
<div class="pop_header">
<p class="rhpk_info"> {{ overlayChartObj.name }}基本信息</p>
<div class="map_close" @click="closeMapPop">x</div>
</div>
<sitePop>
<div class="button_box">
<div>自动监测</div>
</div>
</sitePop>
</div>
</div>
</div>
</div><script>
var viewer = null
new Vue({
el: '#app',
data: {
popVisible: false,
overlayChartObj: {},
list: [
{ longitude: 109.306058, latitude: 30.86751, id: 0, name: "标点一" },
{ longitude: 109.306058, latitude: 30.56751, id: 1, name: "标点二" },
]
},
mounted() {
this.mapCreate()
},
methods: {
async mapCreate() {
Cesium.Ion.defaultAccessToken =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI2ZjZlMzAxNS1jY2ZmLTQ0MzctYTE1MS0zNzMxZjhkZjlhYTgiLCJpZCI6MTIyNTg4LCJpYXQiOjE2NzQ5NzMxNTB9.GKIVHIsKN-AWd4vjLOIPm9Fji8tIEvZd5AXf-rTvFk0";
viewer = new Cesium.Viewer('cesiumContainer', {
geocoder: false,
homeButton: false,
animation: false,
fullscreenButton: false,
sceneModePicker: false,
timeline: true, 。
navigationHelpButton: false,
baseLayerPicker: false,
infoBox: false,
scene3DOnly: false,
selectionIndicator: false,
baselLayerPicker: false,
});
let terrainProvider = Cesium.createWorldTerrain({ //添加世界地形能看到山脉的高低起伏
requestVertexNormals: true, //开启地形光照
requestWaterMask: true, // 开启水面波纹
});
viewer.terrainProvider = terrainProvider;
viewer.debugShowFramesPerSecond = true//显示当前的帧率信息,这样你就可以实时观察到地图的渲染性能
viewer.scene.screenSpaceCameraController.minimumZoomDistance = 1;// 设置了摄像机的最小缩放距离
viewer.camera.setView({ // 相机视口
destination: Cesium.Cartesian3.fromDegrees(109.306058, 30.86751, 8000),
orientation: {
heading: Cesium.Math.toRadians(0), //控制视口左右旋转,也就是沿Y轴旋转,0时为正北方向
pitch: Cesium.Math.toRadians(-25), // 控制视口上下旋转,也就是沿X轴旋转,-90时为俯视地面
roll: 0, // 控制视口翻转角度,也就是沿Z轴旋转,0时为不翻转
},
//奉节 坐标
});
this.handlePinClick(); // 注册点位点击事件
this.bindMapMove(); // 注册场景移动事件
this.renderSiteCesiumPoint(this.list)
},
}
})
</script></body>
</html>
完整代码示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link href="https://cesium.com/downloads/cesiumjs/releases/1.85/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
<script src="https://unpkg.com/@turf/turf/turf.min.js"></script>
<script src="https://cesium.com/downloads/cesiumjs/releases/1.85/Build/Cesium/Cesium.js"></script>
<script src="https://cdn.staticfile.net/vue/2.7.0/vue.min.js"></script>
<style>
#cesiumContainer {
width: 1920px;
height: 1080px;
}
#cesiumContainer .cesium-widget-credits {
display: none;
}
.cesium-popup {
position: relative;
width: 267px;
min-height: 152px;
background: pink;
/* background: url(~@/assets/images/mapPop.png) no-repeat; */
background-size: cover;
}
.cesium-popup .pop_header {
width: 431px;
height: 57px;
line-height: 57px;
/* background: url(~@/assets/images/mapHeader.png) no-repeat 100% 100%; */
}
.cesium-popup .rhpk_info {
font-size: 18px;
font-weight: bold;
color: #fff;
padding-left: 70px;
box-sizing: border-box;
}
.cesium-popup .map_close {
position: absolute;
top: 16px;
right: 20px;
width: 29px;
height: 29px;
/* background: url(~@/assets/images/mapClose.png) no-repeat 100% 100%; */
cursor: pointer;
}
.cesium-popup .map_con {
padding: 20px 20px;
width: 100%;
box-sizing: border-box;
}
.cesium-popup .info {
display: flex;
font-size: 16px;
color: #fff;
margin-bottom: 20px;
}
.cesium-popup .title {
width: 85px;
font-weight: 400;
color: #57bcd2;
}
.cesium-popup .button_box {
position: absolute;
bottom: 30px;
left: 0px;
width: 100%;
display: flex;
justify-content: flex-end;
}
.cesium-popup .button_box div {
width: 100px;
height: 27px;
/* background: url(~@/assets/images/mapButton.png) no-repeat 100% 100%; */
line-height: 27px;
text-align: center;
cursor: pointer;
margin: 0 10px;
}
</style>
</head>
<body>
<div id="app">
<button @click="mapClick">主动触发点位弹窗</button>
<button @click="removeLayer('site_layer')">清除点位图层</button>
<div id="cesiumContainer">
<div id="popup">
<div class="cesium-popup" @closeMap="closeMapPop" v-if="popVisible" ref="overlay">
<div class="pop_header">
<p class="rhpk_info"> {{ overlayChartObj.name }}基本信息</p>
<div class="map_close" @click="closeMapPop">x</div>
</div>
<sitePop>
<div class="button_box">
<div>自动监测</div>
</div>
</sitePop>
</div>
</div>
</div>
</div>
<script>
var viewer = null
new Vue({
el: '#app',
data: {
popVisible: false,
overlayChartObj: {},
list: [
{ longitude: 109.306058, latitude: 30.86751, id: 0, name: "标点一" },
{ longitude: 109.306058, latitude: 30.56751, id: 1, name: "标点二" },
]
},
mounted() {
this.mapCreate()
},
methods: {
async mapCreate() {
Cesium.Ion.defaultAccessToken =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI2ZjZlMzAxNS1jY2ZmLTQ0MzctYTE1MS0zNzMxZjhkZjlhYTgiLCJpZCI6MTIyNTg4LCJpYXQiOjE2NzQ5NzMxNTB9.GKIVHIsKN-AWd4vjLOIPm9Fji8tIEvZd5AXf-rTvFk0";
viewer = new Cesium.Viewer('cesiumContainer', {
geocoder: false,
homeButton: false,
animation: false,
fullscreenButton: false,
sceneModePicker: false,
timeline: true, 。
navigationHelpButton: false,
baseLayerPicker: false,
infoBox: false,
scene3DOnly: false,
selectionIndicator: false,
baselLayerPicker: false,
});
let terrainProvider = Cesium.createWorldTerrain({ //添加世界地形能看到山脉的高低起伏
requestVertexNormals: true, //开启地形光照
requestWaterMask: true, // 开启水面波纹
});
viewer.terrainProvider = terrainProvider;
var lastCameraHeight = 0;
var heightStableCount = 0;
var threshold = 5; // 高度稳定的阈值
var maxStableCount = 30; // 达到稳定的帧数
function isTerrainProvider() {
var cameraHeight =
viewer.camera.positionCartographic.height;
if (Math.abs(cameraHeight - lastCameraHeight) < threshold) {
heightStableCount++;
} else {
heightStableCount = 0;
}
if (heightStableCount >= maxStableCount) {
console.log("地形加载完成");
// 在这里执行地形加载完成后的操作
viewer.scene.preRender.removeEventListener(
isTerrainProvider
);
}
lastCameraHeight = cameraHeight;
}
viewer.debugShowFramesPerSecond = true//显示当前的帧率信息,这样你就可以实时观察到地图的渲染性能
viewer.scene.screenSpaceCameraController.minimumZoomDistance = 1;// 设置了摄像机的最小缩放距离
viewer.camera.setView({ // 相机视口
destination: Cesium.Cartesian3.fromDegrees(109.306058, 30.86751, 8000),
orientation: {
heading: Cesium.Math.toRadians(0), //控制视口左右旋转,也就是沿Y轴旋转,0时为正北方向
pitch: Cesium.Math.toRadians(-25), // 控制视口上下旋转,也就是沿X轴旋转,-90时为俯视地面
roll: 0, // 控制视口翻转角度,也就是沿Z轴旋转,0时为不翻转
},
//奉节 坐标
});
this.handlePinClick(); // 注册点位点击事件
this.bindMapMove(); // 注册场景移动事件
this.renderSiteCesiumPoint(this.list)
},
renderSiteCesiumPoint(projectList) {
this.removeLayer('site_layer')
for (let i = 0; i < projectList.length; i++) {
const pro = projectList[i];
let longitude = pro.longitude || 0
let latitude = pro.latitude || 0
let imgUrl = `./site.png`;
viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees( // 设置点位的经纬度和高度
longitude,
latitude,
240
),
name: pro.name,
id: `site_layer_${pro.id}`, // site_layer就是自定义layerName后续可通过这个移除
property: pro,
polyline: {
show: true, //是否显示,默认显示
// 关键点在设置一条竖直的线
positions: Cesium.Cartesian3.fromDegreesArrayHeights([
longitude,
latitude,
0,
longitude,
latitude,
240,
]),
width: 1, //线的宽度(像素),默认为1
material: Cesium.Color.fromCssColorString("#069BBF").withAlpha(1),
},
billboard: {
width: 33, // 点位的大小
height: 33,
image: imgUrl,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // 设置垂直方向的对齐方式
},
});
}
},
removeLayer(layerName) { // 根据图层名移除对应的图层组
let entityList = [];
let count = 0
while (count < 8) {
viewer.entities.values.forEach(function (entity) {
if (entity._id.includes(layerName)) {
viewer.entities.remove(entity);
}
});
count++;
}
},
// 点击地图上的点
handlePinClick(data = null, typePop = null) {
const cesiumViewer = viewer;
const tt = this;
if (data != null && typePop != null) {
console.log('主动触发', data, typePop)
tt.overlayChartObj = Object.assign(data, {
layer: typePop
});
tt.getProjDetailPop({
x: 951.25,
y: 600
}, data);
return
}
const handle3D = new Cesium.ScreenSpaceEventHandler(
cesiumViewer.scene.canvas
);
handle3D.setInputAction((movement) => {
const pick = cesiumViewer.scene.pick(movement.position);
console.log("pick===>", pick, pick.id); // pick.id是点击后获取对应的实体
console.log("pick===>",pick.id.polyline.positions.value.getValue());
if (!pick.id) {
console.log("清除监听事件===>", pick, pick.id);
this.popVisible = false;
// 清除监听事件
cesiumViewer.scene.postRender.removeEventListener(this.bindMapMove);
return;
}
const obj = pick.id.property;
// 解决点击不同点数据不更换问题
if (tt.overlayChartObj != {}) {
tt.popVisible = false;
tt.overlayChartObj = {};
}
const coordinate = movement.position;
console.log("coordinate===>", coordinate);
if (pick.id._id.includes("rhpwk_layer")) {
tt.overlayChartObj = Object.assign(obj, {
layer: "rhpwk_layer"
});
tt.getProjDetailPop(coordinate, obj);
} else if (pick.id._id.includes("site_layer")) {
tt.overlayChartObj = Object.assign(obj, {
layer: "site_layer" //使用site_layer类型的点位弹窗
});
tt.getProjDetailPop(coordinate, obj);
} else if (pick.id._id.includes("camera_layer")) {
tt.overlayChartObj = Object.assign(obj, {
layer: "camera_layer"
});
tt.getProjDetailPop(coordinate, obj);
} else if (pick.id._id.includes("wry_layer")) {
tt.overlayChartObj = Object.assign(obj, {
layer: "wry_layer"
});
tt.getProjDetailPop(coordinate, obj);
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
},
getProjDetailPop(coordinate, obj) {
this.showOverlayChart(coordinate, obj);
},
// 显示
showOverlayChart(position, data) {
const pop = document.getElementById("popup");
pop.style.position = "absolute";
pop.style.top = position.y + "px";
pop.style.left = position.x + "px";
pop.style.zIndex = 99;
this.overlayChartObj = data;
this.popVisible = true;
},
bindMapMove() {
const cesiumViewer = viewer;
console.log('cesiumViewer', cesiumViewer)
const tt = this;
cesiumViewer.scene.preRender.addEventListener(() => {
if (!tt.popVisible) return;
const scratch = Cesium.Cartesian2();
const position = Cesium.Cartesian3.fromDegrees(
Number(tt.overlayChartObj.longitude),
Number(tt.overlayChartObj.latitude),
300
);
const canvasPosition = cesiumViewer.scene.cartesianToCanvasCoordinates(
position,
scratch
);
if (Cesium.defined(canvasPosition)) {
tt.setPopPosition(canvasPosition);
}
});
},
setPopPosition(position) {
const pop = document.getElementById("popup");
pop.style.top = position.y + "px";
pop.style.left = position.x + "px";
},
closeMapPop() {
this.popVisible = false;
// 清除监听事件
viewer.scene.postRender.removeEventListener(
this.bindMapMove
);
},
// 主动触发点位弹窗
mapClick() {
this.handlePinClick(this.list[0], 'site_layer') // 点位数据 及弹窗类型
},
}
})
</script>
</body>
</html>