Commit 3e2b8acf authored by xinzhedeai's avatar xinzhedeai

add:编辑围栏 定位页

parent cde8d155
......@@ -74,6 +74,19 @@ export const constantRoutes = [
meta: { title: '首页', icon: 'dashboard', affix: true }
}
]
},
{
path: '/dw',
component: Layout,
redirect: 'noredirect',
children: [
{
path: 'index',
component: () => import('@/views/index-dw'),
name: 'Index',
meta: { title: '首页2-dw', icon: 'dashboard', affix: true }
}
]
},
// {
// path: '/zz',
......@@ -88,32 +101,32 @@ export const constantRoutes = [
// }
// ]
// },
{
path: '/guiji',
component: Layout,
redirect: 'noredirect',
children: [
{
path: 'index',
component: () => import('@/views/index-guiji'),
name: 'Index',
meta: { title: '首页2-gj', icon: 'dashboard', affix: true }
}
]
},
{
path: '/wl',
component: Layout,
redirect: 'noredirect',
children: [
{
path: 'screen',
component: () => import('@/views/index-wl'),
name: 'Screen',
meta: { title: '首页-WL', icon: 'dashboard', affix: true }
}
]
},
// {
// path: '/guiji',
// component: Layout,
// redirect: 'noredirect',
// children: [
// {
// path: 'index',
// component: () => import('@/views/index-guiji'),
// name: 'Index',
// meta: { title: '首页2-gj', icon: 'dashboard', affix: true }
// }
// ]
// },
// {
// path: '/wl',
// component: Layout,
// redirect: 'noredirect',
// children: [
// {
// path: 'screen',
// component: () => import('@/views/index-wl'),
// name: 'Screen',
// meta: { title: '首页-WL', icon: 'dashboard', affix: true }
// }
// ]
// },
{
path: '/user',
component: Layout,
......
<template>
<div class="app-container home">
<!-- Cesium地图容器 -->
<div id="cesiumContainer" class="cesium-container"></div>
</div>
</template>
<script>
import { getTokenApi } from "@/api/token";
import { localStg } from "@/utils/storage";
export default {
name: "Index",
data() {
return {
// 版本号
version: "3.8.9",
// Cesium查看器实例
viewer: null,
tileset: null,
personnelList: [
{
longitude: 121.70710830038986,
latitude: 36.853636402927986,
height: 100,
name: "张三",
status: "online", // 在线
avatar: "/static/images/avatars/zhangsan.png", // 头像
},
{
longitude: 121.70810830038986,
latitude: 36.854636402927986,
height: 100,
name: "李四",
status: "offline", // 离线
avatar: "/static/images/avatars/lisi.png", // 头像
},
// 其他人员数据...
],
intervaler: null,
bgEntities: {}, // 所有的key都对应一个实体对象。而人员的实体对应的key不是数字。
};
},
mounted() {
// 组件挂载后初始化地图
this.initCesium();
// 监听窗口大小变化
window.addEventListener("resize", this.handleResize);
},
beforeMount() {
localStg.set("token", 222233);
if (localStg.get("token")) {
return;
}
// 组件挂载前获取token
getTokenApi().then((res) => {
console.log("token", res);
localStg.set("token", res.data.data);
});
},
beforeDestroy() {
// 清理事件监听
window.removeEventListener("resize", this.handleResize);
// 清理地图资源
if (this.viewer) {
this.viewer.destroy();
this.viewer = null;
}
// 清理点击事件处理器
if (this.clickHandler) {
this.clickHandler.destroy();
this.clickHandler = null;
}
// 组件销毁前清理地图资源
if (this.viewer) {
this.viewer.destroy();
this.viewer = null;
}
// 清理定时器
if (this.intervaler) {
clearInterval(this.intervaler);
}
// 清空实体引用
this.bgEntities = {};
},
methods: {
// 新增:清除实体
clearEntities() {
if (this.viewer && this.bgEntities) {
for (let key in this.bgEntities) {
this.viewer.entities.remove(this.bgEntities[key]);
delete this.bgEntities[key];
}
}
},
/**
* 初始化Cesium地图
*/
async initCesium() {
Cesium.Ion.defaultAccessToken =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI0ZDAxZGFhYy02MjJlLTRiNzktODNhZi00N2VjZGY0NTk4YmIiLCJpZCI6Mjc0NDAxLCJpYXQiOjE3NjMzNDYwNTR9.ZQW2DZ4KaMGbHuwrtIbyI6EdSSvgMJUHmmD74eZW7PQ";
try {
// 创建Cesium Viewer实例
this.viewer = new Cesium.Viewer("cesiumContainer", {
// 设置地图提供者,这里使用默认的Bing Maps
imageryProvider: new Cesium.BingMapsImageryProvider({
url: "https://dev.virtualearth.net",
key: "AgcbDCAOb9zMfquaT4Z-MdHfx--9wUNrLRiiS7rIElFx8f-4lLulxZ0QnhqX5Lm6",
mapStyle: Cesium.BingMapsStyle.AERIAL,
}),
// 配置是否显示各种控件
animation: false, // 动画控件
baseLayerPicker: true, // 底图选择器
fullscreenButton: false, // 全屏按钮
geocoder: false, // 地址搜索
homeButton: false, // 主页按钮
infoBox: false, // 信息框
sceneModePicker: false, // 场景模式选择器
selectionIndicator: false, // 选择指示器
timeline: false, // 时间线
navigationHelpButton: false, // 导航帮助按钮
navigationInstructionsInitiallyVisible: false,
// 设置初始视图位置(中国区域)
center: Cesium.Cartesian3.fromDegrees(104.06, 30.67, 10000000),
});
this.viewer.scene.camera.setView({
// 视角-环翠
duration: 1,
destination: {
x: -2739843.563038797,
y: 4357442.794747324,
z: 3880768.3292693933,
},
orientation: {
heading: 6.037000745578596,
pitch: -1.2499586064720978,
roll: 0.000005306352659495417,
},
});
// 隐藏Cesium logo
this.viewer._cesiumWidget._creditContainer.style.display = "none";
const VUE_APP_GIS =
"http://192.168.2.11:8080/Apps/assets/media/gaoquyingji";
try {
const tileset = await Cesium.Cesium3DTileset.fromUrl(
VUE_APP_GIS + "/tiles/01_guanwei/tileset.json"
);
this.viewer.scene.primitives.add(tileset);
console.log("倾斜摄影模型加载成功tileset", tileset);
if (tileset) {
this.tileset = tileset;
this.locateToTileset();
// 添加鼠标点击事件监听器,用于控制人员信息label显示
this.addEntityClickHandler();
}
} catch (error) {
console.error(`加载倾斜摄影模型失败: ${error}`);
}
console.log("Cesium地图初始化成功");
} catch (error) {
console.error("Cesium地图初始化失败:", error);
}
},
/**
* 获取倾斜摄影模型的经纬度并将摄像机视角转向模型上方
*/
locateToTileset() {
if (!this.tileset || !this.viewer) {
console.error("模型或视图未准备就绪");
return;
}
try {
// 获取模型的边界球
const boundingSphere = this.tileset.boundingSphere;
if (!boundingSphere) {
console.error("无法获取模型边界");
return;
}
// 获取模型中心点的笛卡尔坐标
const center = boundingSphere.center;
// 将笛卡尔坐标转换为经纬度坐标
const cartographic = Cesium.Cartographic.fromCartesian(center);
const longitude = Cesium.Math.toDegrees(cartographic.longitude);
const latitude = Cesium.Math.toDegrees(cartographic.latitude);
const height = cartographic.height;
console.log("倾斜摄影模型中心点经纬度:", {
longitude: longitude,
latitude: latitude,
height: height,
});
// 计算合适的观察距离 - 基于模型半径的倍数
const distance = boundingSphere.radius * 2.5; // 可以根据需要调整倍数
// 设置相机位置在模型上方
const cameraPosition = Cesium.Cartesian3.fromRadians(
cartographic.longitude,
cartographic.latitude,
height + distance // 相机高度为模型最高点加上观察距离
);
// 计算相机看向模型中心的方向
const heading = Cesium.Math.toRadians(0); // 方向角
const pitch = Cesium.Math.toRadians(-90); // 俯仰角 - 负数表示向下看
const roll = Cesium.Math.toRadians(0); // 翻滚角
// 使用flyTo方法平滑过渡到目标位置
this.viewer.camera.flyTo({
destination: cameraPosition,
orientation: {
heading: heading,
pitch: pitch,
roll: roll,
},
duration: 2, // 过渡时间2秒
complete: () => {
console.log("相机已成功定位到模型上方");
// this.createPersonModel(); // 定位后创建人员模型
// 根据当前选择的类型创建相应的模型
this.createPersonModel(); // 定位后创建人员模型
},
});
} catch (error) {
console.error("定位到模型上方失败:", error);
}
},
personCardList(fn) {
// 将笛卡尔坐标转换为经纬度坐标
const cartographic = Cesium.Cartographic.fromCartesian({
x: -2686273.730145489,
y: 4293961.185794622,
z: 3863430.5618300107,
});
const baseLongitude = Cesium.Math.toDegrees(cartographic.longitude);
const baseLatitude = Cesium.Math.toDegrees(cartographic.latitude);
const baseHeight = cartographic.height + 1;
console.log("人员经纬度基准点:", {
longitude: baseLongitude,
latitude: baseLatitude,
height: baseHeight,
});
// 清除现有的人员列表
this.personnelList = [];
// 生成10个不同的经纬度坐标
const maxOffset = 0.00005;
for (let index = 0; index < 10; index++) {
// 生成随机偏移量
const lngOffset = (Math.random() - 0.5) * 2 * maxOffset;
const latOffset = (Math.random() - 0.5) * 2 * maxOffset;
this.personnelList.push({
lng: baseLongitude + lngOffset,
lat: baseLatitude + latOffset,
height: baseHeight,
perName: "张三" + index,
status: "online", // 在线
avatar: "/static/images/avatars/zhangsan.png", // 头像
});
}
fn(this.personnelList);
},
// 修改:更新点击事件处理器,支持车辆显示
addEntityClickHandler() {
// 创建鼠标事件处理器
this.clickHandler = new Cesium.ScreenSpaceEventHandler(
this.viewer.canvas
);
// 监听鼠标左键点击事件
this.clickHandler.setInputAction((click) => {
// 检测点击位置的实体
const pickedObject = this.viewer.scene.pick(click.position);
// 修复条件判断:检查是否有info属性
if (
Cesium.defined(pickedObject) &&
pickedObject.id &&
pickedObject.id.info
) {
// 获取点击的实体
const entity = pickedObject.id;
const entityInfo = entity.info;
// 如果已有详细信息label,则移除它
if (entity.detailLabel) {
this.viewer.entities.remove(entity.detailLabel);
delete entity.detailLabel;
return;
}
console.log("点击实体了", entityInfo);
// 根据实体类型显示不同的信息
let labelText = "";
// 人员信息
labelText = `${entityInfo.perName}\n状态: ${
entityInfo.status === "online" ? "在线" : "离线"
}\n高度: ${entityInfo.height.toFixed(2)}m`;
// 创建详细信息label
const detailLabel = this.viewer.entities.add({
position: entity.position.getValue(),
label: {
text: labelText,
font: "14px 微软雅黑",
backgroundColor: Cesium.Color.fromCssColorString("#173349"),
showBackground: true,
fillColor: Cesium.Color.WHITE,
pixelOffset: new Cesium.Cartesian2(0, -60),
eyeOffset: new Cesium.Cartesian3(0, 0, -10),
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
verticalOrigin: Cesium.VerticalOrigin.TOP,
scaleByDistance: new Cesium.NearFarScalar(1000, 1, 500000, 0.5),
disableDepthTestDistance: Number.POSITIVE_INFINITY, // 确保始终显示在最前面
},
});
// 将详细信息label关联到实体,便于后续管理
entity.detailLabel = detailLabel;
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
},
// 处理窗口大小变化
handleResize() {
// 重新调整Cesium视图大小
if (this.viewer) {
this.viewer.resize();
}
// 重新绘制图表
const chartContainer = document.getElementById("trendChart");
if (chartContainer && chartContainer.firstChild) {
chartContainer.removeChild(chartContainer.firstChild);
this.drawTrendChart();
}
},
// 1. 提取通用实体创建函数
createEntity(item, type) {
const isPerson = type === "person";
const idField = isPerson ? "perName" : "vehicleName";
const labelColor = isPerson ? Cesium.Color.YELLOW : Cesium.Color.CYAN;
const iconPath = isPerson
? "/poi-marker-default.png"
: "/poi-marker-vehicle.png";
let lng = Number(item.lng);
let lat = Number(item.lat);
let height = Number(item.height);
let position = Cesium.Cartesian3.fromDegrees(lng, lat, height);
console.log(item[idField], position);
// 创建实体
let entity = this.viewer.entities.add({
position: position,
label: {
text: item[idField],
font: "16px",
backgroundColor: Cesium.Color.fromCssColorString("#173349"),
showBackground: true,
fillColor: labelColor,
depthTestAgainstTerrain: false,
pixelOffset: new Cesium.Cartesian2(0, -35),
disableDepthTestDistance: Number.POSITIVE_INFINITY, // 确保label始终显示在最前面
heightReference: Cesium.HeightReference.CLAMP_TO_3D_TILE, // 与billboard保持一致
verticalOrigin: Cesium.VerticalOrigin.TOP, // 确保正确的垂直对齐
horizontalOrigin: Cesium.HorizontalOrigin.CENTER, // 确保正确的水平对齐
eyeOffset: new Cesium.Cartesian3(0, 0, 0), // 调整眼睛偏移量
scaleByDistance: new Cesium.NearFarScalar(1000, 1, 500000, 0.5), // 控制不同距离下的缩放比例
},
billboard: {
image: iconPath,
scale: 0.5,
heightReference: Cesium.HeightReference.CLAMP_TO_3D_TILE,
},
description: `<div><h4>${item[idField]}${item.status}</h4></div>`,
fixedFrame: Cesium.Transforms.eastNorthUpToFixedFrame(position),
});
entity.info = item;
entity.type = type;
this.bgEntities[item[idField]] = entity;
return entity;
},
// 2. 提取实体更新函数
updateEntity(entity, item) {
let position = Cesium.Cartesian3.fromDegrees(
item.lng,
item.lat,
item.height
);
entity.position.setValue(position);
entity.fixedFrame = Cesium.Transforms.eastNorthUpToFixedFrame(position);
},
// 3. 提取批量创建实体的函数
createEntities(entityList, type) {
const isPerson = type === "person";
const idField = isPerson ? "perName" : "vehicleName";
for (let item of entityList) {
this.createEntity(item, type);
}
},
// 4. 提取批量更新实体的函数(增量更新模式)
updateEntities(entityList, type) {
const isPerson = type === "person";
const idField = isPerson ? "perName" : "vehicleName";
for (let item of entityList) {
let entity = this.bgEntities[item[idField]];
if (entity && entity.type === type) {
// 更新现有实体
this.updateEntity(entity, item);
} else {
// 创建新实体
this.createEntity(item, type);
}
}
},
// 5. 提取定时器逻辑为可重用函数
setupEntityUpdateInterval(type, dataGenerator, interval = 10000) {
// 清除现有定时器
if (this.intervaler) {
clearInterval(this.intervaler);
this.intervaler = null;
}
// 设置新的定时器
this.intervaler = setInterval(() => {
console.log(`开始获取实时${type === "person" ? "人员" : "车辆"}数据`);
// 生成或获取最新数据
if (typeof dataGenerator === "function") {
dataGenerator();
}
const entityList =
type === "person" ? this.personnelList : this.vehicleList;
// 更新实体(这里可以选择清除重建或增量更新)
// 方案1:清除所有实体后重建
this.clearEntities();
this.createEntities(entityList, type);
// 方案2:增量更新(推荐,性能更好)
// this.updateEntities(entityList, type);
}, interval);
},
// 修改后的createPersonModel方法
createPersonModel() {
// 清除现有定时器
if (this.intervaler) {
clearInterval(this.intervaler);
this.intervaler = null;
}
// 清除现有实体
this.clearEntities();
// 立即生成并显示人员实体
console.log("立即创建人员实体");
this.personCardList((list) => {
console.log("人员数据", this.personnelList);
this.createEntities(this.personnelList, "person");
});
// 设置定时刷新
this.setupEntityUpdateInterval("person", () => {
this.personCardList(() => {
console.log("人员数据", this.personnelList);
});
});
},
},
};
</script>
<style scoped lang="scss">
.home {
width: 100%;
height: 100vh;
position: relative;
overflow: hidden;
blockquote {
padding: 10px 20px;
margin: 0 0 20px;
font-size: 17.5px;
border-left: 5px solid #eee;
}
hr {
margin-top: 20px;
margin-bottom: 20px;
border: 0;
border-top: 1px solid #eee;
}
.col-item {
margin-bottom: 20px;
}
ul {
padding: 0;
margin: 0;
}
font-family: "open sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 13px;
color: #676a6c;
overflow-x: hidden;
ul {
list-style-type: none;
}
h4 {
margin-top: 0px;
}
h2 {
margin-top: 10px;
font-size: 26px;
font-weight: 100;
}
p {
margin-top: 10px;
b {
font-weight: 700;
}
}
.update-log {
ol {
display: block;
list-style-type: decimal;
margin-block-start: 1em;
margin-block-end: 1em;
margin-inline-start: 0;
margin-inline-end: 0;
padding-inline-start: 40px;
}
}
// Cesium容器样式
.cesium-container {
// width: 100vw;
// height: 100vh;
width: 87vw;
height: 87vh;
position: absolute;
top: 0;
left: 0;
z-index: 1;
}
}
</style>
\ No newline at end of file
......@@ -39,17 +39,36 @@
</div>
<div class="fence-controls">
<button @click="startCreatingFence" :disabled="isCreatingFence">
<button
@click="startCreatingFence"
:disabled="isCreatingFence || isEditingFence"
>
创建围栏
</button>
<button @click="editSelectedFence" :disabled="!selectedFence">
编辑围栏
<button
@click="editSelectedFence"
:disabled="!selectedFence || isCreatingFence || isEditingFence"
:class="{ editing: isEditingFence }"
>
{{ isEditingFence ? "编辑中" : "编辑围栏" }}
</button>
<button @click="deleteSelectedFence" :disabled="!selectedFence">
<button
@click="deleteSelectedFence"
:disabled="!selectedFence || isCreatingFence || isEditingFence"
>
删除围栏
</button>
<button @click="stopCreatingFence" :disabled="!isCreatingFence">
取消创建
<button
@click="
isCreatingFence
? stopCreatingFence
: isEditingFence
? completeEditingFence
: null
"
:disabled="!isCreatingFence && !isEditingFence"
>
{{ isCreatingFence ? "取消创建" : "完成编辑" }}
</button>
</div>
......@@ -122,6 +141,15 @@ export default {
drawingEntity: null, // 绘制中的实体
fenceEntities: {}, // 围栏实体映射
// 电子围栏编辑相关
isEditingFence: false, // 是否正在编辑围栏
editingFence: null, // 当前编辑的围栏
vertexEntities: [], // 顶点实体集合
isDraggingVertex: false, // 是否正在拖拽顶点
draggedVertexIndex: -1, // 当前拖拽的顶点索引
temporaryPointEntities: null, // 临时点实体
originalRightClickHandler: null, // 原始右键点击处理器
// 告警相关
alarms: [], // 告警列表
};
......@@ -152,6 +180,8 @@ export default {
if (this.boundaryCheckInterval) {
clearInterval(this.boundaryCheckInterval);
}
// 清理编辑状态相关资源
this.stopEditingFence();
},
computed: {
// 获取当前类型的围栏列表
......@@ -162,6 +192,294 @@ export default {
},
},
methods: {
// 修改编辑围栏方法
editSelectedFence() {
if (!this.selectedFence || this.isCreatingFence) return;
this.startEditingFence(this.selectedFence);
},
// 开始编辑围栏
startEditingFence(fence) {
if (!this.viewer || this.isCreatingFence) {
console.error("Cesium viewer未初始化或正在创建围栏");
return;
}
// 设置编辑状态
this.isEditingFence = true;
this.editingFence = fence;
// 清除之前可能存在的顶点
this.clearVertexEntities();
console.log("显示围栏顶点");
// 显示围栏顶点
this.showFenceVertices(fence);
console.log("添加编辑模式事件监听");
// 添加鼠标事件监听
this.addEditModeEventListeners();
// 显示提示信息
alert("围栏编辑模式已开启,可拖拽黄色顶点改变围栏形状,右键完成编辑");
},
// 显示围栏顶点
showFenceVertices(fence) {
if (
!this.viewer ||
!fence ||
!fence.points ||
fence.points.length === 0
) {
console.error("无法显示围栏顶点:参数无效");
return;
}
console.log("创建顶点实体,共", fence.points.length, "个顶点");
// 创建顶点实体
fence.points.forEach((point, index) => {
try {
const vertexEntity = this.viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(point.lng, point.lat, 10),
point: {
pixelSize: 12, // 增大顶点大小,更容易点击
color: Cesium.Color.YELLOW,
outlineColor: Cesium.Color.BLACK,
outlineWidth: 2,
heightReference: Cesium.HeightReference.CLAMP_TO_3D_TILE,
disableDepthTestDistance: Number.POSITIVE_INFINITY, // 确保显示在最前面
},
// 为顶点添加ID用于识别
properties: {
vertexIndex: index,
isFenceVertex: true,
},
});
this.vertexEntities.push(vertexEntity);
console.log("创建顶点实体", index, "成功");
} catch (error) {
console.error("创建顶点实体失败:", error);
}
});
},
// 清除顶点实体
clearVertexEntities() {
if (
this.viewer &&
this.vertexEntities &&
this.vertexEntities.length > 0
) {
console.log("清除顶点实体");
this.vertexEntities.forEach((entity) => {
try {
this.viewer.entities.remove(entity);
} catch (error) {
console.error("移除顶点实体失败:", error);
}
});
this.vertexEntities = [];
}
},
// 添加编辑模式事件监听
addEditModeEventListeners() {
if (!this.viewer) {
console.error("Cesium viewer未初始化");
return;
}
console.log("移除现有事件监听器");
// 移除现有的事件监听器,避免冲突
try {
this.viewer.screenSpaceEventHandler.removeInputAction(
Cesium.ScreenSpaceEventType.LEFT_DOWN
);
this.viewer.screenSpaceEventHandler.removeInputAction(
Cesium.ScreenSpaceEventType.MOUSE_MOVE
);
this.viewer.screenSpaceEventHandler.removeInputAction(
Cesium.ScreenSpaceEventType.LEFT_UP
);
this.viewer.screenSpaceEventHandler.removeInputAction(
Cesium.ScreenSpaceEventType.RIGHT_CLICK
);
} catch (error) {
console.log("移除事件监听器时出错:", error);
}
console.log("添加左键按下事件");
// 添加左键按下事件用于开始拖拽
this.viewer.screenSpaceEventHandler.setInputAction((event) => {
this.handleVertexDragStart(event);
}, Cesium.ScreenSpaceEventType.LEFT_DOWN);
console.log("添加鼠标移动事件");
// 添加鼠标移动事件用于拖拽中
this.viewer.screenSpaceEventHandler.setInputAction((event) => {
this.handleVertexDragging(event);
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
console.log("添加左键释放事件");
// 添加左键释放事件用于结束拖拽
this.viewer.screenSpaceEventHandler.setInputAction((event) => {
this.handleVertexDragEnd(event);
}, Cesium.ScreenSpaceEventType.LEFT_UP);
console.log("添加右键点击事件");
// 添加右键点击事件用于完成编辑
this.viewer.screenSpaceEventHandler.setInputAction((event) => {
this.completeEditingFence();
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
},
// 处理顶点拖拽开始
handleVertexDragStart(event) {
console.log("开始拖拽顶点");
const pickedObject = this.viewer.scene.pick(event.position);
// 检查是否点击了顶点
if (
pickedObject &&
pickedObject.id &&
pickedObject.id.properties &&
pickedObject.id.properties.isFenceVertex &&
pickedObject.id.properties.vertexIndex !== undefined
) {
this.isDraggingVertex = true;
this.draggedVertexIndex = pickedObject.id.properties.vertexIndex;
// 改变拖拽中顶点的样式
pickedObject.id.point.color = Cesium.Color.RED;
console.log("开始拖拽顶点", this.draggedVertexIndex);
}
},
// 处理顶点拖拽中
handleVertexDragging(event) {
if (
!this.isDraggingVertex ||
this.draggedVertexIndex < 0 ||
!this.editingFence
) {
return;
}
const ray = this.viewer.camera.getPickRay(event.endPosition);
let intersection;
// 首先尝试从3D模型上拾取点
if (this.tileset) {
intersection = this.viewer.scene.pickFromRay(ray, [this.tileset]);
}
// 如果没有从模型上拾取到点,则从地球上拾取
if (!intersection || !intersection.position) {
intersection = this.viewer.scene.globe.pick(ray, this.viewer.scene);
}
if (intersection) {
const position = intersection.position || intersection;
const cartographic = Cesium.Cartographic.fromCartesian(position);
const longitude = Cesium.Math.toDegrees(cartographic.longitude);
const latitude = Cesium.Math.toDegrees(cartographic.latitude);
// 更新围栏点坐标
this.editingFence.points[this.draggedVertexIndex] = {
lng: longitude,
lat: latitude,
};
// 更新顶点实体位置
if (this.vertexEntities[this.draggedVertexIndex]) {
this.vertexEntities[this.draggedVertexIndex].position =
Cesium.Cartesian3.fromDegrees(longitude, latitude, 10);
}
// 重新渲染围栏
this.renderFence(this.editingFence);
}
},
// 处理顶点拖拽结束
handleVertexDragEnd(event) {
if (
this.isDraggingVertex &&
this.draggedVertexIndex >= 0 &&
this.vertexEntities[this.draggedVertexIndex]
) {
// 恢复顶点样式
this.vertexEntities[this.draggedVertexIndex].point.color =
Cesium.Color.YELLOW;
console.log("结束拖拽顶点", this.draggedVertexIndex);
}
this.isDraggingVertex = false;
this.draggedVertexIndex = -1;
},
// 完成围栏编辑
completeEditingFence() {
if (!this.isEditingFence || !this.editingFence) {
return;
}
console.log("完成围栏编辑");
// 移除顶点显示
this.clearVertexEntities();
// 重置编辑状态
this.isEditingFence = false;
this.editingFence = null;
this.isDraggingVertex = false;
this.draggedVertexIndex = -1;
// 移除编辑模式的事件监听
this.removeEditModeEventListeners();
// 重新渲染所有围栏
this.renderAllFences();
alert("围栏编辑已完成");
},
// 移除编辑模式事件监听
removeEditModeEventListeners() {
if (!this.viewer) {
return;
}
try {
console.log("移除编辑模式事件监听");
// 移除编辑模式添加的事件
this.viewer.screenSpaceEventHandler.removeInputAction(
Cesium.ScreenSpaceEventType.LEFT_DOWN
);
this.viewer.screenSpaceEventHandler.removeInputAction(
Cesium.ScreenSpaceEventType.MOUSE_MOVE
);
this.viewer.screenSpaceEventHandler.removeInputAction(
Cesium.ScreenSpaceEventType.LEFT_UP
);
this.viewer.screenSpaceEventHandler.removeInputAction(
Cesium.ScreenSpaceEventType.RIGHT_CLICK
);
} catch (error) {
console.log("移除编辑事件监听时出错:", error);
}
},
// 停止编辑围栏
stopEditingFence() {
if (this.isEditingFence) {
console.log("停止编辑围栏");
this.completeEditingFence();
}
},
// 新增:清除实体
clearEntities() {
if (this.viewer && this.bgEntities) {
......@@ -775,8 +1093,14 @@ export default {
},
// 停止创建电子围栏
stopCreatingFence() {
console.log("停止创建围栏");
this.isCreatingFence = false;
// 确保同时停止编辑状态
this.stopEditingFence();
// ... 现有代码 ...
// 移除临时绘制的围栏
if (this.drawingEntity && this.viewer) {
this.viewer.entities.remove(this.drawingEntity);
......@@ -793,7 +1117,6 @@ export default {
// 移除事件监听
if (this.viewer) {
// 只移除我们添加的事件处理器
try {
this.viewer.screenSpaceEventHandler.removeInputAction(
Cesium.ScreenSpaceEventType.LEFT_CLICK
......@@ -812,6 +1135,12 @@ export default {
// 选择围栏
selectFence(fence) {
console.log("选择围栏:", fence.name);
// 停止当前编辑
if (this.isEditingFence) {
this.stopEditingFence();
}
this.selectedFence = fence;
// 高亮显示选中的围栏
......@@ -820,12 +1149,13 @@ export default {
// 编辑选中的围栏
editSelectedFence() {
// 这里可以实现围栏编辑功能,简化版暂时只允许重命名
const newName = prompt("请输入新的围栏名称:", this.selectedFence.name);
if (newName) {
this.selectedFence.name = newName;
this.renderAllFences();
if (!this.selectedFence || this.isCreatingFence) {
console.log("无法编辑围栏:未选中围栏或正在创建围栏");
return;
}
console.log("开始编辑围栏:", this.selectedFence.name);
this.startEditingFence(this.selectedFence);
},
// 删除选中的围栏
......@@ -1188,6 +1518,15 @@ export default {
cursor: not-allowed;
}
}
// 添加编辑中的按钮样式
button.editing {
background: #ff6600 !important;
&:hover {
background: #ff8833 !important;
}
}
}
.fence-list {
......
<template>
<div class="app-container home">
<!-- Cesium地图容器 -->
<div id="cesiumContainer" class="cesium-container"></div>
</div>
<div class="app-container home">首页</div>
</template>
<script>
import { getTokenApi } from "@/api/token";
import { localStg } from "@/utils/storage";
export default {
name: "Index",
data() {
return {
// 版本号
version: "3.8.9",
// Cesium查看器实例
viewer: null,
tileset: null,
personnelList: [
{
longitude: 121.70710830038986,
latitude: 36.853636402927986,
height: 100,
name: "张三",
status: "online", // 在线
avatar: "/static/images/avatars/zhangsan.png", // 头像
},
{
longitude: 121.70810830038986,
latitude: 36.854636402927986,
height: 100,
name: "李四",
status: "offline", // 离线
avatar: "/static/images/avatars/lisi.png", // 头像
},
// 其他人员数据...
],
intervaler: null,
bgEntities: {}, // 所有的key都对应一个实体对象。而人员的实体对应的key不是数字。
};
},
mounted() {
// 组件挂载后初始化地图
this.initCesium();
// 监听窗口大小变化
window.addEventListener("resize", this.handleResize);
},
beforeMount() {
localStg.set("token", 222233);
if (localStg.get("token")) {
return;
}
// 组件挂载前获取token
getTokenApi().then((res) => {
console.log("token", res);
localStg.set("token", res.data.data);
});
},
beforeDestroy() {
// 清理事件监听
window.removeEventListener("resize", this.handleResize);
// 清理地图资源
if (this.viewer) {
this.viewer.destroy();
this.viewer = null;
}
// 清理点击事件处理器
if (this.clickHandler) {
this.clickHandler.destroy();
this.clickHandler = null;
}
// 组件销毁前清理地图资源
if (this.viewer) {
this.viewer.destroy();
this.viewer = null;
}
// 清理定时器
if (this.intervaler) {
clearInterval(this.intervaler);
}
// 清空实体引用
this.bgEntities = {};
},
methods: {
// 新增:清除实体
clearEntities() {
if (this.viewer && this.bgEntities) {
for (let key in this.bgEntities) {
this.viewer.entities.remove(this.bgEntities[key]);
delete this.bgEntities[key];
}
}
},
/**
* 初始化Cesium地图
*/
async initCesium() {
Cesium.Ion.defaultAccessToken =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI0ZDAxZGFhYy02MjJlLTRiNzktODNhZi00N2VjZGY0NTk4YmIiLCJpZCI6Mjc0NDAxLCJpYXQiOjE3NjMzNDYwNTR9.ZQW2DZ4KaMGbHuwrtIbyI6EdSSvgMJUHmmD74eZW7PQ";
try {
// 创建Cesium Viewer实例
this.viewer = new Cesium.Viewer("cesiumContainer", {
// 设置地图提供者,这里使用默认的Bing Maps
imageryProvider: new Cesium.BingMapsImageryProvider({
url: "https://dev.virtualearth.net",
key: "AgcbDCAOb9zMfquaT4Z-MdHfx--9wUNrLRiiS7rIElFx8f-4lLulxZ0QnhqX5Lm6",
mapStyle: Cesium.BingMapsStyle.AERIAL,
}),
// 配置是否显示各种控件
animation: false, // 动画控件
baseLayerPicker: true, // 底图选择器
fullscreenButton: false, // 全屏按钮
geocoder: false, // 地址搜索
homeButton: false, // 主页按钮
infoBox: false, // 信息框
sceneModePicker: false, // 场景模式选择器
selectionIndicator: false, // 选择指示器
timeline: false, // 时间线
navigationHelpButton: false, // 导航帮助按钮
navigationInstructionsInitiallyVisible: false,
// 设置初始视图位置(中国区域)
center: Cesium.Cartesian3.fromDegrees(104.06, 30.67, 10000000),
});
this.viewer.scene.camera.setView({
// 视角-环翠
duration: 1,
destination: {
x: -2739843.563038797,
y: 4357442.794747324,
z: 3880768.3292693933,
},
orientation: {
heading: 6.037000745578596,
pitch: -1.2499586064720978,
roll: 0.000005306352659495417,
},
});
// 隐藏Cesium logo
this.viewer._cesiumWidget._creditContainer.style.display = "none";
const VUE_APP_GIS =
"http://192.168.2.11:8080/Apps/assets/media/gaoquyingji";
try {
const tileset = await Cesium.Cesium3DTileset.fromUrl(
VUE_APP_GIS + "/tiles/01_guanwei/tileset.json"
);
this.viewer.scene.primitives.add(tileset);
console.log("倾斜摄影模型加载成功tileset", tileset);
if (tileset) {
this.tileset = tileset;
this.locateToTileset();
// 添加鼠标点击事件监听器,用于控制人员信息label显示
this.addEntityClickHandler();
}
} catch (error) {
console.error(`加载倾斜摄影模型失败: ${error}`);
}
console.log("Cesium地图初始化成功");
} catch (error) {
console.error("Cesium地图初始化失败:", error);
}
},
/**
* 获取倾斜摄影模型的经纬度并将摄像机视角转向模型上方
*/
locateToTileset() {
if (!this.tileset || !this.viewer) {
console.error("模型或视图未准备就绪");
return;
}
try {
// 获取模型的边界球
const boundingSphere = this.tileset.boundingSphere;
if (!boundingSphere) {
console.error("无法获取模型边界");
return;
}
// 获取模型中心点的笛卡尔坐标
const center = boundingSphere.center;
// 将笛卡尔坐标转换为经纬度坐标
const cartographic = Cesium.Cartographic.fromCartesian(center);
const longitude = Cesium.Math.toDegrees(cartographic.longitude);
const latitude = Cesium.Math.toDegrees(cartographic.latitude);
const height = cartographic.height;
console.log("倾斜摄影模型中心点经纬度:", {
longitude: longitude,
latitude: latitude,
height: height,
});
// 计算合适的观察距离 - 基于模型半径的倍数
const distance = boundingSphere.radius * 2.5; // 可以根据需要调整倍数
// 设置相机位置在模型上方
const cameraPosition = Cesium.Cartesian3.fromRadians(
cartographic.longitude,
cartographic.latitude,
height + distance // 相机高度为模型最高点加上观察距离
);
// 计算相机看向模型中心的方向
const heading = Cesium.Math.toRadians(0); // 方向角
const pitch = Cesium.Math.toRadians(-90); // 俯仰角 - 负数表示向下看
const roll = Cesium.Math.toRadians(0); // 翻滚角
// 使用flyTo方法平滑过渡到目标位置
this.viewer.camera.flyTo({
destination: cameraPosition,
orientation: {
heading: heading,
pitch: pitch,
roll: roll,
},
duration: 2, // 过渡时间2秒
complete: () => {
console.log("相机已成功定位到模型上方");
// this.createPersonModel(); // 定位后创建人员模型
// 根据当前选择的类型创建相应的模型
this.createPersonModel(); // 定位后创建人员模型
},
});
} catch (error) {
console.error("定位到模型上方失败:", error);
}
},
personCardList(fn) {
// 将笛卡尔坐标转换为经纬度坐标
const cartographic = Cesium.Cartographic.fromCartesian({
x: -2686273.730145489,
y: 4293961.185794622,
z: 3863430.5618300107,
});
const baseLongitude = Cesium.Math.toDegrees(cartographic.longitude);
const baseLatitude = Cesium.Math.toDegrees(cartographic.latitude);
const baseHeight = cartographic.height + 1;
console.log("人员经纬度基准点:", {
longitude: baseLongitude,
latitude: baseLatitude,
height: baseHeight,
});
// 清除现有的人员列表
this.personnelList = [];
// 生成10个不同的经纬度坐标
const maxOffset = 0.00005;
for (let index = 0; index < 10; index++) {
// 生成随机偏移量
const lngOffset = (Math.random() - 0.5) * 2 * maxOffset;
const latOffset = (Math.random() - 0.5) * 2 * maxOffset;
this.personnelList.push({
lng: baseLongitude + lngOffset,
lat: baseLatitude + latOffset,
height: baseHeight,
perName: "张三" + index,
status: "online", // 在线
avatar: "/static/images/avatars/zhangsan.png", // 头像
});
}
fn(this.personnelList);
},
// 修改:更新点击事件处理器,支持车辆显示
addEntityClickHandler() {
// 创建鼠标事件处理器
this.clickHandler = new Cesium.ScreenSpaceEventHandler(
this.viewer.canvas
);
// 监听鼠标左键点击事件
this.clickHandler.setInputAction((click) => {
// 检测点击位置的实体
const pickedObject = this.viewer.scene.pick(click.position);
// 修复条件判断:检查是否有info属性
if (
Cesium.defined(pickedObject) &&
pickedObject.id &&
pickedObject.id.info
) {
// 获取点击的实体
const entity = pickedObject.id;
const entityInfo = entity.info;
// 如果已有详细信息label,则移除它
if (entity.detailLabel) {
this.viewer.entities.remove(entity.detailLabel);
delete entity.detailLabel;
return;
}
console.log("点击实体了", entityInfo);
// 根据实体类型显示不同的信息
let labelText = "";
// 人员信息
labelText = `${entityInfo.perName}\n状态: ${
entityInfo.status === "online" ? "在线" : "离线"
}\n高度: ${entityInfo.height.toFixed(2)}m`;
// 创建详细信息label
const detailLabel = this.viewer.entities.add({
position: entity.position.getValue(),
label: {
text: labelText,
font: "14px 微软雅黑",
backgroundColor: Cesium.Color.fromCssColorString("#173349"),
showBackground: true,
fillColor: Cesium.Color.WHITE,
pixelOffset: new Cesium.Cartesian2(0, -60),
eyeOffset: new Cesium.Cartesian3(0, 0, -10),
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
verticalOrigin: Cesium.VerticalOrigin.TOP,
scaleByDistance: new Cesium.NearFarScalar(1000, 1, 500000, 0.5),
disableDepthTestDistance: Number.POSITIVE_INFINITY, // 确保始终显示在最前面
},
});
// 将详细信息label关联到实体,便于后续管理
entity.detailLabel = detailLabel;
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
},
// 处理窗口大小变化
handleResize() {
// 重新调整Cesium视图大小
if (this.viewer) {
this.viewer.resize();
}
// 重新绘制图表
const chartContainer = document.getElementById("trendChart");
if (chartContainer && chartContainer.firstChild) {
chartContainer.removeChild(chartContainer.firstChild);
this.drawTrendChart();
}
},
// 1. 提取通用实体创建函数
createEntity(item, type) {
const isPerson = type === "person";
const idField = isPerson ? "perName" : "vehicleName";
const labelColor = isPerson ? Cesium.Color.YELLOW : Cesium.Color.CYAN;
const iconPath = isPerson
? "/poi-marker-default.png"
: "/poi-marker-vehicle.png";
let lng = Number(item.lng);
let lat = Number(item.lat);
let height = Number(item.height);
let position = Cesium.Cartesian3.fromDegrees(lng, lat, height);
console.log(item[idField], position);
// 创建实体
let entity = this.viewer.entities.add({
position: position,
label: {
text: item[idField],
font: "16px",
backgroundColor: Cesium.Color.fromCssColorString("#173349"),
showBackground: true,
fillColor: labelColor,
depthTestAgainstTerrain: false,
pixelOffset: new Cesium.Cartesian2(0, -35),
disableDepthTestDistance: Number.POSITIVE_INFINITY, // 确保label始终显示在最前面
heightReference: Cesium.HeightReference.CLAMP_TO_3D_TILE, // 与billboard保持一致
verticalOrigin: Cesium.VerticalOrigin.TOP, // 确保正确的垂直对齐
horizontalOrigin: Cesium.HorizontalOrigin.CENTER, // 确保正确的水平对齐
eyeOffset: new Cesium.Cartesian3(0, 0, 0), // 调整眼睛偏移量
scaleByDistance: new Cesium.NearFarScalar(1000, 1, 500000, 0.5), // 控制不同距离下的缩放比例
},
billboard: {
image: iconPath,
scale: 0.5,
heightReference: Cesium.HeightReference.CLAMP_TO_3D_TILE,
},
description: `<div><h4>${item[idField]}${item.status}</h4></div>`,
fixedFrame: Cesium.Transforms.eastNorthUpToFixedFrame(position),
});
entity.info = item;
entity.type = type;
this.bgEntities[item[idField]] = entity;
return entity;
},
// 2. 提取实体更新函数
updateEntity(entity, item) {
let position = Cesium.Cartesian3.fromDegrees(
item.lng,
item.lat,
item.height
);
entity.position.setValue(position);
entity.fixedFrame = Cesium.Transforms.eastNorthUpToFixedFrame(position);
},
// 3. 提取批量创建实体的函数
createEntities(entityList, type) {
const isPerson = type === "person";
const idField = isPerson ? "perName" : "vehicleName";
for (let item of entityList) {
this.createEntity(item, type);
}
},
// 4. 提取批量更新实体的函数(增量更新模式)
updateEntities(entityList, type) {
const isPerson = type === "person";
const idField = isPerson ? "perName" : "vehicleName";
for (let item of entityList) {
let entity = this.bgEntities[item[idField]];
if (entity && entity.type === type) {
// 更新现有实体
this.updateEntity(entity, item);
} else {
// 创建新实体
this.createEntity(item, type);
}
}
},
// 5. 提取定时器逻辑为可重用函数
setupEntityUpdateInterval(type, dataGenerator, interval = 10000) {
// 清除现有定时器
if (this.intervaler) {
clearInterval(this.intervaler);
this.intervaler = null;
}
// 设置新的定时器
this.intervaler = setInterval(() => {
console.log(`开始获取实时${type === "person" ? "人员" : "车辆"}数据`);
// 生成或获取最新数据
if (typeof dataGenerator === "function") {
dataGenerator();
}
const entityList =
type === "person" ? this.personnelList : this.vehicleList;
// 更新实体(这里可以选择清除重建或增量更新)
// 方案1:清除所有实体后重建
this.clearEntities();
this.createEntities(entityList, type);
// 方案2:增量更新(推荐,性能更好)
// this.updateEntities(entityList, type);
}, interval);
},
// 修改后的createPersonModel方法
createPersonModel() {
// 清除现有定时器
if (this.intervaler) {
clearInterval(this.intervaler);
this.intervaler = null;
}
// 清除现有实体
this.clearEntities();
// 立即生成并显示人员实体
console.log("立即创建人员实体");
this.personCardList((list) => {
console.log("人员数据", this.personnelList);
this.createEntities(this.personnelList, "person");
});
// 设置定时刷新
this.setupEntityUpdateInterval("person", () => {
this.personCardList(() => {
console.log("人员数据", this.personnelList);
});
});
goTarget(href) {
window.open(href, "_blank");
},
},
};
......@@ -506,11 +21,6 @@ export default {
<style scoped lang="scss">
.home {
width: 100%;
height: 100vh;
position: relative;
overflow: hidden;
blockquote {
padding: 10px 20px;
margin: 0 0 20px;
......@@ -570,17 +80,5 @@ export default {
padding-inline-start: 40px;
}
}
// Cesium容器样式
.cesium-container {
// width: 100vw;
// height: 100vh;
width: 87vw;
height: 87vh;
position: absolute;
top: 0;
left: 0;
z-index: 1;
}
}
</style>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment