由于后期的websocket会发送大量的数据,Cesium可能会面临性能瓶颈,而MapV是一个专门处理大规模地理数据可视化的库,它在处理大数据量时具有较好的性能和渲染效果,结合使用Cesium和MapV,请问我该如何在原有的cesium基础上进行Cesium来展示整体的3D地球模型,并将大量的轨迹点和轨迹线数据传递给MapV来进行可视化处理。以提高性能和渲染效果?求大佬们帮帮忙
<template>
<div class="container">
<div class="map">
<div id="cesiumContainer">
</div>
<div class="getLongLat">
经度:{{ longitude }},纬度:{{ latitude }}
</div>
<!--系统设置 -->
<div class="right_top">
<el-dropdown ref="dropdown1" trigger="click">
<ul>
<li>显示设置</li>
</ul>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>
<el-checkbox v-model="checkedPoint" label="显示轨迹点" size="large" style="color: #606266;" />
</el-dropdown-item>
<el-dropdown-item>
<el-checkbox v-model="checkedLines" label="显示轨迹线" size="large" style="color: #606266;" />
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div>
</div>
</template>
<script setup >
import { onMounted, onUnmounted, reactive, defineProps, ref, computed, watch, } from 'vue';
import * as Cesium from 'cesium';
import * as MapV from 'mapv'
import { SampledPositionProperty } from "cesium";
import CesiumNavigation from 'cesium-navigation-es6'
import api from '@/api/api';
import back from '@/api/back'
import { ElMessage } from 'element-plus';
import emitter from '@/api/eventpus/eventbus';
import { map } from 'lodash';
let socketInfo = 'ws://192.168.0.45:8089/kunchen-plsms-websocket/webSocket'
let ws = new WebSocket(socketInfo)
let pingInterval = ref(null) // 心跳间隔ID
let viewer = null
let entity = null
// 从服务器获取用户角色名
const userRole = computed(() => {
return window.localStorage.getItem('roles')
})
const dropdown1 = ref()
// 点击地图获取经纬度
let longitude = ref(null);
let latitude = ref(null);
let isFirstExecution = true;//处理第一次监听watch的标志
onMounted(() => {
init() //加载3d地图,
webValue()//加载websocket
LocusModel()//3D地图模型
AdmainInfo()//基站设备显示
getautoDrive()//获取申请路径规划
})
// 地图的基础配置
function init() {
viewer = new Cesium.Viewer('cesiumContainer', {
//cesium的查看器的基本属性
baseLayerPicker: false,//配置图层底图的图标
//加载谷歌影像地图,UrlTemplateImageryProvider该接口是加载谷歌地图服务的接口
imageryProvider: new Cesium.ArcGisMapServerImageryProvider({
// url: "https://map.geoq.cn/arcgis/rest/services/ChinaOnlineStreetPurplishBlue/MapServer",
url: 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer' //卫星地图
}),
//需要纯色背景必须设置
contextOptions: {
webgl: {
alpha: true,
}
},
infoBox: false,//去除原生自带右上角弹窗
sceneMode: 3,//初始场景模式 1 2D模式 2 2D循环模式 3 3D模式 Cesium.SceneMode
geocoder: false, //查找位置工具
homeButton: true, // 视角返回初始位置
sceneModePicker: false, //选择视角模式,有三种 3D,2D,哥伦比视图
baseLayerPicker: false, //图层选择器,选择要显示的地图服务和地形服务
navigationHelpButton: false, //导航帮助按钮,显示默认的地图控制帮助
animation: false, //动画器件,控制视图动画的播放速度.
// creditContainer: "credit", // 版权显示,显示数据归属
timeline: false, //时间线,指示当前时间,并允许用户跳到特定的时间
fullscreenButton: false, //全屏按钮
vrButton: false,
baseLayerPicker: false,
// terrainProvider: Cesium.createWorldTerrain({ // 设置cesium世界地形
// requestWaterMask: true,//地形的开启
// requestVertexNormals: true,//水面效果的开启
// })
})
// viewer.scene.skyBox.show = false //关闭天空盒,否则会显示天空颜色
//背景透明
// 取消标点的双击事件
viewer.cesiumWidget.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
viewer.scene.globe.depthTestAgainstTerrain = false;
viewer.scene.backgroundColor = new Cesium.Color(0.0, 0.0, 0.0, 0.0);
//关闭大气
viewer.scene.skyAtmosphere.show = false
//抗锯齿
viewer.scene.fxaa = true;
viewer.scene.postProcessStages.fxaa.enabled = true;
//清除月亮太阳
viewer.scene.moon.show = false
viewer.scene.sun.show = false
// 设置最小缩放级别
// viewer.scene.screenSpaceCameraController.minimumZoomDistance = 20; // 米为单位
// 去除logo 版权显示,显示数据归属
viewer.cesiumWidget.creditContainer.style.display = "none";
// 设置初始默认视角
// let camera = { longitude: 104.2255077, latitude: 30.57514052 }
let camera = { longitude: 104.22499925, latitude: 30.57339262 }
viewer.camera.setView({
destination: Cesium.Cartesian3.fromDegrees(camera.longitude, camera.latitude, 150),
orientation: {
heading: 6.283185307179581,//方向
// heading: Cesium.Math.toRadians(18),//方向
pitch: Cesium.Math.toRadians(-45),//视角 设置俯仰角度为-45度
roll: 0.0
}
});
// 修改homeButton的默认返回位置
viewer.homeButton.viewModel.command.beforeExecute.addEventListener(function (e) {
e.cancel = true;
viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(camera.longitude, camera.latitude, 150),
orientation: {
heading: 6.283185307179581,//方向
pitch: Cesium.Math.toRadians(-45),//视角 设置俯仰角度为-45度
roll: 0.0
}
})
})
// 显示帧率
viewer.scene.debugShowFramesPerSecond = false;
// 添加罗盘,比例尺等控件
//配置项
var options = {
// 用于在使用重置导航重置地图视图时设置默认视图控制。接受的值是Cesium.Cartographic 和 Cesium.Rectangle.
defaultResetView: Cesium.Rectangle.fromDegrees(80, 22, 130, 50),
// 用于启用或禁用罗盘。
enableCompass: false,
// 用于启用或禁用缩放控件。
enableZoomControls: true,
// 用于启用或禁用距离图例。
enableDistanceLegend: true,
// 用于启用或禁用指南针外环。
enableCompassOuterRing: true
};
// 初始化罗盘
new CesiumNavigation(viewer, options);
// 鼠标移动获取地图经纬度
let handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction(function (event) {
// 获取地图画布尺寸
let canvas = viewer.scene.canvas;
let canvasWidth = canvas.clientWidth;
let canvasHeight = canvas.clientHeight;
// 检查鼠标是否在地图画布内
let mouseX = event.endPosition.x;
let mouseY = event.endPosition.y;
if (mouseX < 0 || mouseX > canvasWidth || mouseY < 0 || mouseY > canvasHeight) {
return; // 鼠标不在地图画布内,忽略该事件
}
// 获取鼠标对应的地图坐标
let cartesian = viewer.camera.pickEllipsoid(event.endPosition);
if (!cartesian) {
return; // 没有获取到合法的地图坐标,忽略该事件
}
let cartographic = Cesium.Cartographic.fromCartesian(cartesian);
let lng = Cesium.Math.toDegrees(cartographic.longitude); // 经度
let lat = Cesium.Math.toDegrees(cartographic.latitude); // 纬度
let coordinate = {
longitude: Number(lng.toFixed(8)),
latitude: Number(lat.toFixed(8)),
};
longitude.value = coordinate.longitude;
latitude.value = coordinate.latitude;
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
}
// 加载websocket
function webValue() {
// 初始Websocket
const onOpen = () => {
const data = JSON.stringify({ command: 1, serializeAlgorithm: 1, token: window.localStorage.getItem('token') });
ws.send(data); // 发送JSON字符串
// 设置心跳间隔为5秒
pingInterval.value = window.setInterval(() => {
const pingMessage = JSON.stringify({
command: 2,
serializeAlgorithm: 1
});
ws.send(pingMessage);
}, 5000);
};
if (window.localStorage.getItem('CardUID') === null) {
return ElMessage({
message: '车辆终端ID未绑定,请点击右上角的“设置”图标进行绑定。',
type: 'warning',
showClose: true
});
}
// 接收websocket数据
const onMessage = async (event) => {
const data = JSON.parse(event.data);
if (data.data == undefined) {
console.log('未响应');
} else {
processPositionParams(data.data);
}
};
// 封装处理位置参数的函数
const positionParams = []; // 用于存储所有的位置参数数据
const bufferLimit = 50; // 缓冲区大小限制,可以根据需求调整
function processPositionParams(data) {
const targetingTypes = {
0x10: 'RTK',
0x11: 'UWB',
0x12: '融合'
};
const newPositionParam = {
TargetingType: targetingTypes[data.type],
UID: data.uid,
Angle: data.angle,
Height: data.height,
longitude: data.longitude,
latitude: data.latitude
};
positionParams.push(newPositionParam);
// 超过缓冲区大小限制时移除最旧的位置参数数据
if (positionParams.length > bufferLimit) {
positionParams.shift();
}
const labelText = `终端ID:${data.uid},定位类型:${targetingTypes[data.type]}\n经度:${data.longitude},纬度:${data.latitude}\n角度:${data.angle},高度:${data.height}`;
CarModel(positionParams, newPositionParam.Angle, data.uid); // 车辆模型
pointPolyline(positionParams, labelText, data.uid) // 轨迹点和轨迹线
}
const onClose = () => {
window.clearInterval(pingInterval.value); // 清除心跳间隔定时器
console.log('Websocket连接关闭');
webValue();
};
const onError = (error) => {
console.log('Websocket连接错误');
ws.close();
window.clearInterval(pingInterval.value); // 清除心跳间隔定时器
webValue();
};
ws.addEventListener('open', onOpen);
ws.addEventListener('message', onMessage);
ws.addEventListener('close', onClose);
ws.addEventListener('error', onError);
}
// 退出登录
emitter.on('logout', () => {
// 关闭 Websocket 连接
ws.close();
window.clearInterval(pingInterval.value); // 清除心跳间隔定时器
});
// 绘制车辆模型和轨迹点、轨迹线
let checkedPoint = ref(true);
let checkedLines = ref(false);
// 保存checkedPoint和checkedLines的值到LocalStorage
function saveCheckedValues() {
localStorage.setItem('checkedPoint', JSON.stringify(checkedPoint.value));
localStorage.setItem('checkedLines', JSON.stringify(checkedLines.value));
}
// 从LocalStorage中获取checkedPoint和checkedLines的值
function loadCheckedValues() {
const storedCheckedPoint = localStorage.getItem('checkedPoint');
const storedCheckedLines = localStorage.getItem('checkedLines');
if (storedCheckedPoint) {
checkedPoint.value = JSON.parse(storedCheckedPoint);
}
if (storedCheckedLines) {
checkedLines.value = JSON.parse(storedCheckedLines);
}
}
// 监听checkedPoint和checkedLines的变化,并保存到LocalStorage
watch([checkedPoint, checkedLines], saveCheckedValues, { deep: true });
// 在页面加载时加载checkedPoint和checkedLines的值
window.addEventListener('load', loadCheckedValues);
// 车辆模型
let carModelData = null;
function CarModel(positions, angle, uid) {
const headingPitchRoll = new Cesium.HeadingPitchRoll(
Cesium.Math.toRadians(angle + 90),
Cesium.Math.toRadians(0),
Cesium.Math.toRadians(0)
);
const scaleByDistance = new Cesium.NearFarScalar(200, 120, 1200, 0.6);
const distanceDisplayCondition = new Cesium.DistanceDisplayCondition(0, 10000);
// 获取最新的位置
const latestPosition = positions[positions.length - 1];
const cartesian3 = Cesium.Cartesian3.fromDegrees(latestPosition.longitude, latestPosition.latitude, 0.4);
if (!carModelData) {
carModelData = viewer.entities.add({
position: cartesian3,
orientation: Cesium.Transforms.headingPitchRollQuaternion(cartesian3, headingPitchRoll),
model: {
uri: "http://127.0.0.1:5501/car/scene.gltf",
// color: Cesium.Color.CORAL,
scale: 75,
scaleByDistance,
distanceDisplayCondition,
},
});
} else {
carModelData.position = cartesian3;
carModelData.orientation = Cesium.Transforms.headingPitchRollQuaternion(cartesian3, headingPitchRoll);
}
}
// 轨迹点 和 轨迹线 和 标签文字npm add mapv
let polylineEntity = null;
let entityList = [];
let positionsArray = [];
function pointPolyline(positions, labelText, uid) {
const cartesian3Array = [];
positionsArray = positions.flatMap(pos => [pos.longitude, pos.latitude]);
// 移除之前的实体点
for (const entity of entityList) {
viewer.entities.remove(entity);
}
entityList = [];
for (const position of positions) {
let entityIndex = entityList.length;
const cartesian3 = Cesium.Cartesian3.fromDegrees(
position.longitude,
position.latitude,
0.02
);
const pointLabel = new Cesium.Entity({
position: cartesian3,
point: {
pixelSize: 10,
color: Cesium.Color.BLUE,
show: checkedPoint.value,
outlineColor: Cesium.Color.WHITE,
outlineWidth: 1,
},
// label: {
// text: labelText,
// font: '14pt monospace',
// show: new Cesium.CallbackProperty(() => {
// return entityIndex === entityList.length - 1;
// }, false),
// style: Cesium.LabelStyle.FILL_AND_OUTLINE,
// outlineWidth: 2,
// verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
// pixelOffset: new Cesium.Cartesian2(0, -9),
// fillColor: Cesium.Color.WHITE,//标签的文本填充颜色。
// outlineColor: Cesium.Color.WHITE,//标签的文本轮廓颜色。
// },
});
entityList.push(pointLabel);
viewer.entities.add(pointLabel);
cartesian3Array.push(cartesian3);
}
// 更新轨迹线位置
if (polylineEntity) {
polylineEntity.polyline.positions = new Cesium.CallbackProperty(() => {
return Cesium.Cartesian3.fromDegreesArray(positionsArray);
}, false);
polylineEntity.polyline.show = checkedLines.value;
} else {
// 创建新的轨迹线
polylineEntity = viewer.entities.add({
polyline: {
positions: new Cesium.CallbackProperty(() => {
return Cesium.Cartesian3.fromDegreesArray(positionsArray);
}, false),
width: 15,
material: Cesium.Color.RED,
},
});
}
}
// 获取父组件传递的停车场信息的经纬度
let props = defineProps({
cesiumData: Object,
})
// 3D地图模型
function LocusModel() {
const positions = [{ longitude: 104.2255077, latitude: 30.57514052 }]//停车场区域中心点
positions.forEach((position, index) => {
// 加载3d地图模型
position = Cesium.Cartesian3.fromDegrees(position.longitude, position.latitude);
let entity = new Cesium.Entity({
position: position,
model: {
uri: 'http://127.0.0.1:5501/parking/lq_parking_space.gltf',
// uri: '/car/lq_parking_space.gltf',
show: true,
maximumSize: 1,
minimumPixelSize: 0.1,
scale: 1, //放大倍数
// 当距离小于300米时,模型的缩放比例为1,当距离大于1200米时,缩放比例为0.4
scaleByDistance: new Cesium.NearFarScalar(300, 1, 1200, 0.5), //设置随图缩放距离和比例
distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 10000), //设置可见距离 10000米可见
// color: new Cesium.Color(1, 1, 1, .5)
},
// 旋转模型
orientation: Cesium.Transforms.headingPitchRollQuaternion(
position,
new Cesium.HeadingPitchRoll(
Cesium.Math.toRadians(-66), // 设置这个属性即可(顺时针旋转的角度值)
Cesium.Math.toRadians(0),
Cesium.Math.toRadians(0)
)
),
});
viewer.entities.add(entity)
});
}
// 点击“预约车位内的按钮”, 实现全局路径规划
function getautoDrive() {
// 创建 Polyline 坐标数组
const positions = JSON.parse(localStorage.getItem('requestData'));
if (positions === null) {
return;
}
// 转成Cesium里的Cartesian3坐标
const cartesianPositions = positions.map(pos => {
return Cesium.Cartesian3.fromDegrees(pos.pos_lon, pos.pos_lat);
});
// 创建几何实例
const polylineGeometry = new Cesium.PolylineGeometry({
positions: cartesianPositions,
width: 5,
vertexFormat: Cesium.PolylineColorAppearance.VERTEX_FORMAT,
});
// 创建GeometryInstance,将几何实例赋值给geometry
const geometryInstance = new Cesium.GeometryInstance({
geometry: polylineGeometry,
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.GREEN)
},
});
// 加载primitive,通过geometryInstances可以将多个PolygonGeometry一次性加载出来
const polylinePrimitive = new Cesium.Primitive({
geometryInstances: geometryInstance,
appearance: new Cesium.PolylineColorAppearance(),
compressVertices: true
});
viewer.scene.primitives.add(polylinePrimitive);
}
// 在onUnmounted钩子函数中销毁Cesium Viewer
// onUnmounted(() => {
// // viewer.destroy()
// })
</script>
用Cesium的Viewer实例,并加载地球场景,然后,使用MapV库来处理大规模数据的可视化需求,包括轨迹点和轨迹线、车辆模型等内容的渲染。
@demo123456789 贴了一大段代码,还请把具体的逻辑需求说明清楚。
我大概理解的意思是:
想在cesium里面 嵌入百度地图mapv的一些效果,因为其效率高。
还请跳转到:https://www.cnblogs.com/xt112233/p/17062288.html
参考开源项目地址:https://github.com/LiZzhi/cesium-plugin
mapvLayer这部分 模块
@demo123456789 而且cesium本身就具有处理大规模数据的能力,就是大尺度下gis的可视化需求的产物,所以具体需求具体优化,不知道你说的具体瓶颈在哪里
@hawk86104 具体的就是后期websocket会发送100000+的数据,所以就会导致现在的纯cesium进行轨迹线的实时移动就会很卡顿
@hawk86104 第一个连接我看见过,没有实现成功
@demo123456789 首先大量的几何体 建议使用:primitive 基本几何体 集合渲染出来。详见:https://blog.csdn.net/m0_46496355/article/details/131565459
改写你的轨迹绘制方法
@hawk86104 大佬,可以帮帮忙实现一下如何将CarModel()函数和pointPolyline()函数,分别换成primitive进行渲染吗?谢谢大佬