Commit a7e11619 authored by xinzhedeai's avatar xinzhedeai

add:guiji page

parent e255598a
...@@ -74,6 +74,19 @@ export const constantRoutes = [ ...@@ -74,6 +74,19 @@ export const constantRoutes = [
} }
] ]
}, },
{
path: '/guiji',
component: Layout,
redirect: 'noredirect',
children: [
{
path: 'index',
component: () => import('@/views/index-guiji'),
name: 'Index',
meta: { title: '首页2-zz', icon: 'dashboard', affix: true }
}
]
},
{ {
path: '/user', path: '/user',
component: Layout, component: Layout,
......
<template>
<div class="app-container home">
<!-- Cesium地图容器 -->
<div id="cesiumContainer" class="cesium-container"></div>
<!-- 人员实时追踪控制面板 -->
<div class="tracking-panel">
<div class="panel-header">
<h3>人员实时追踪</h3>
<select v-model="selectedPerson" @change="onPersonSelect">
<option value="">选择人员</option>
<option
v-for="person in personnelList"
:key="person.perName"
:value="person.perName"
>
{{ person.perName }}
</option>
</select>
</div>
<!-- 时间轴组件 -->
<div class="timeline-container">
<div class="time-info">
<span>当前时间: {{ currentTimeDisplay }}</span>
</div>
<input
type="range"
v-model.number="currentTimeIndex"
:min="0"
:max="timePoints.length - 1"
@input="onTimelineChange"
class="timeline-slider"
/>
<div class="time-labels">
<span>{{ formatTime(timePoints[0]) }}</span>
<span>{{ formatTime(timePoints[timePoints.length - 1]) }}</span>
</div>
</div>
<!-- 控制面板按钮 -->
<div class="control-buttons">
<button @click="startTracking" :disabled="isTracking">开始追踪</button>
<button @click="stopTracking" :disabled="!isTracking">停止追踪</button>
<button @click="showFullPath">显示完整轨迹</button>
<button @click="clearTrails">清除轨迹</button>
</div>
</div>
</div>
</template>
<script>
export default {
name: "Index",
data() {
return {
// 版本号
version: "3.8.9",
// Cesium查看器实例
viewer: null,
tileset: null,
personnelList: [],
personModelInterval: null,
trackingInterval: null,
bgEntities: {}, // 所有的key都对应一个实体对象。而人员的实体对应的key不是数字。
// 轨迹追踪相关数据
selectedPerson: "", // 选中的人员
timePoints: [], // 时间点数组
currentTimeIndex: 0, // 当前时间索引
personTrajectories: {}, // 人员轨迹数据 { 人员名称: [{time, lng, lat, height}] }
trailEntities: {}, // 轨迹实体
isTracking: false, // 是否正在实时追踪
currentTimeDisplay: "", // 当前显示时间
};
},
mounted() {
// 组件挂载后初始化地图
this.initCesium();
// 初始化时间点数据
this.initTimePoints();
},
beforeDestroy() {
// 组件销毁前清理地图资源
if (this.viewer) {
this.viewer.destroy();
this.viewer = null;
}
if (this.personModelInterval) {
clearInterval(this.personModelInterval);
}
if (this.trackingInterval) {
clearInterval(this.trackingInterval);
}
},
methods: {
// 初始化时间点数据(生成过去1小时的时间点,每5分钟一个)
initTimePoints() {
const now = new Date();
const points = [];
// 生成过去1小时的时间点,每5分钟一个
for (let i = 60; i >= 0; i -= 5) {
const time = new Date(now.getTime() - i * 60 * 1000);
points.push(time);
}
this.timePoints = points;
this.currentTimeIndex = points.length - 1; // 默认显示最新时间
this.currentTimeDisplay = this.formatTime(points[points.length - 1]);
// 初始化人员轨迹数据
this.initPersonTrajectories();
},
// 初始化人员轨迹数据
initPersonTrajectories() {
// 将笛卡尔坐标转换为经纬度坐标作为基准点
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;
// 为每个人生成轨迹数据
this.personnelList.forEach((person) => {
const trajectories = [];
let currentLng = baseLongitude + (Math.random() - 0.5) * 0.0001;
let currentLat = baseLatitude + (Math.random() - 0.5) * 0.0001;
this.timePoints.forEach((time) => {
// 生成平滑的移动路径
currentLng += (Math.random() - 0.5) * 0.00002;
currentLat += (Math.random() - 0.5) * 0.00002;
trajectories.push({
time,
lng: currentLng,
lat: currentLat,
height: baseHeight,
});
});
this.personTrajectories[person.perName] = trajectories;
});
},
// 格式化时间显示
formatTime(date = new Date()) {
const hours = date.getHours().toString().padStart(2, "0");
const minutes = date.getMinutes().toString().padStart(2, "0");
return `${hours}:${minutes}`;
},
// 人员选择变化处理
onPersonSelect() {
this.clearTrails();
if (this.selectedPerson) {
this.updatePersonPositionByTimeIndex(this.currentTimeIndex);
this.showFullPath();
}
},
// 时间轴变化处理
onTimelineChange() {
this.currentTimeDisplay = this.formatTime(
this.timePoints[this.currentTimeIndex]
);
this.updatePersonPositionByTimeIndex(this.currentTimeIndex);
},
// 根据时间索引更新人员位置
updatePersonPositionByTimeIndex(index) {
if (
!this.selectedPerson ||
!this.personTrajectories[this.selectedPerson]
) {
return;
}
const trajectory = this.personTrajectories[this.selectedPerson][index];
if (!trajectory) return;
// 更新人员实体位置
const personEntity = this.bgEntities[this.selectedPerson];
if (personEntity) {
const position = Cesium.Cartesian3.fromDegrees(
trajectory.lng,
trajectory.lat,
trajectory.height
);
personEntity.position.setValue(position);
// 更新描述信息,包含时间
personEntity.description = `<div><h4>${
this.selectedPerson
}</h4><p>时间: ${this.formatTime(trajectory.time)}</p></div>`;
}
// 移动相机到人员位置
if (this.isTracking) {
this.viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(
trajectory.lng,
trajectory.lat,
trajectory.height + 100
),
duration: 0.5,
});
}
},
// 开始实时追踪
startTracking() {
if (!this.selectedPerson) {
alert("请先选择要追踪的人员");
return;
}
this.isTracking = true;
this.currentTimeIndex = this.timePoints.length - 1;
// 设置定时器模拟时间流逝
this.trackingInterval = setInterval(() => {
// 模拟新时间点的到来
const now = new Date();
this.timePoints.push(now);
// 为每个人员生成新的位置数据
this.personnelList.forEach((person) => {
if (!this.personTrajectories[person.perName]) {
this.personTrajectories[person.perName] = [];
}
const lastPos =
this.personTrajectories[person.perName][
this.personTrajectories[person.perName].length - 1
];
let newLng = lastPos.lng + (Math.random() - 0.5) * 0.00002;
let newLat = lastPos.lat + (Math.random() - 0.5) * 0.00002;
this.personTrajectories[person.perName].push({
time: now,
lng: newLng,
lat: newLat,
height: lastPos.height,
});
});
// 更新到最新时间
this.currentTimeIndex = this.timePoints.length - 1;
this.currentTimeDisplay = this.formatTime(now);
this.updatePersonPositionByTimeIndex(this.currentTimeIndex);
// 如果选择了当前人员,显示最新轨迹
if (this.selectedPerson) {
this.updateTrailDisplay();
}
}, 5000); // 每5秒更新一次
},
// 停止实时追踪
stopTracking() {
this.isTracking = false;
if (this.trackingInterval) {
clearInterval(this.trackingInterval);
this.trackingInterval = null;
}
},
// 显示完整轨迹
showFullPath() {
if (
!this.selectedPerson ||
!this.personTrajectories[this.selectedPerson]
) {
return;
}
this.clearTrails();
this.updateTrailDisplay();
},
// 更新轨迹显示
updateTrailDisplay() {
const trajectories = this.personTrajectories[this.selectedPerson];
if (!trajectories || trajectories.length < 2) return;
// 创建轨迹点数组
const positions = [];
trajectories.forEach((point) => {
positions.push(
Cesium.Cartesian3.fromDegrees(point.lng, point.lat, point.height)
);
});
// 创建轨迹线
const trailEntity = this.viewer.entities.add({
polyline: {
positions: positions,
width: 3,
material: new Cesium.PolylineGlowMaterialProperty({
glowPower: 0.5,
color: Cesium.Color.YELLOW,
}),
clampToGround: false,
},
});
this.trailEntities[this.selectedPerson] = trailEntity;
// 添加轨迹点标记
trajectories.forEach((point, index) => {
// 只显示部分点,避免过多标记影响性能
if (
index % Math.ceil(trajectories.length / 10) === 0 ||
index === trajectories.length - 1
) {
const pointEntity = this.viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(
point.lng,
point.lat,
point.height + 5
),
point: {
pixelSize: 5,
color:
index === trajectories.length - 1
? Cesium.Color.RED
: Cesium.Color.GREEN,
outlineColor: Cesium.Color.WHITE,
outlineWidth: 2,
},
description: `<div><p>时间: ${this.formatTime(
point.time
)}</p></div>`,
});
if (!this.trailEntities[`${this.selectedPerson}_point_${index}`]) {
this.trailEntities[`${this.selectedPerson}_point_${index}`] =
pointEntity;
}
}
});
},
// 清除轨迹
clearTrails() {
for (let key in this.trailEntities) {
this.viewer.entities.remove(this.trailEntities[key]);
}
this.trailEntities = {};
},
createPersonModel() {
if (!this.personModelInterval) {
this.personModelInterval = setInterval(() => {
console.log("开始获取实时数据");
if (this.bgEntities) {
for (let key in this.bgEntities) {
if (isNaN(parseFloat(key))) {
// 非数字键名的是人员实体
this.viewer.entities.remove(this.bgEntities[key]);
delete this.bgEntities[key];
}
}
}
// 从API获取最新的人员定位数据
this.personCardList((list) => {
console.log("人员数据", this.personnelList);
// 创建新实体
for (let item of this.personnelList) {
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.perName, position);
// 创建人员标记
let entity = this.viewer.entities.add({
position: position,
label: {
text: item.perName,
font: "16px",
backgroundColor: Cesium.Color.fromCssColorString("#173349"),
showBackground: true,
fillColor: Cesium.Color.YELLOW,
depthTestAgainstTerrain: false, // 禁用地形深度测试
pixelOffset: new Cesium.Cartesian2(0, -35),
},
billboard: {
image: "/poi-marker-default.png",
scale: 0.5,
},
description: `<div><h4>${item.perName}${item.status}</h4></div>`,
fixedFrame: Cesium.Transforms.eastNorthUpToFixedFrame(position),
});
entity.info = item; // 添加 info 属性
this.bgEntities[item.perName] = entity; // 存储新实体
}
// 如果已经选择了人员,更新其位置和轨迹
if (this.selectedPerson) {
this.updatePersonPositionByTimeIndex(this.currentTimeIndex);
}
});
}, 10000); // 每10秒刷新一次
}
},
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", // 头像
});
}
// 初始化轨迹数据(如果尚未初始化)
if (Object.keys(this.personTrajectories).length === 0) {
this.initPersonTrajectories();
}
fn(this.personnelList);
},
showPersonInfo(description) {
// 使用 Cesium 的 InfoBox 显示信息
this.viewer.selectedEntity = this.viewer.entities.add({
description: description,
});
this.viewer.infoBox.frame.sandbox =
"allow-same-origin allow-top-navigation allow-pointer-lock allow-popups allow-forms allow-scripts";
this.viewer.infoBox.frame.src = "about:blank";
this.viewer.infoBox.frame.contentDocument.write(description);
this.viewer.infoBox.frame.contentDocument.close();
this.viewer.infoBox.viewModel.showInfo = true;
this.viewer.scene.globe.depthTestAgainstTerrain = true;
},
goTarget(href) {
window.open(href, "_blank");
},
/**
* 初始化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: true, // 全屏按钮
geocoder: true, // 地址搜索
homeButton: true, // 主页按钮
infoBox: true, // 信息框
sceneModePicker: true, // 场景模式选择器
selectionIndicator: true, // 选择指示器
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();
}
} 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(); // 定位后创建人员模型
},
});
} catch (error) {
console.error("定位到模型上方失败:", error);
}
},
},
};
</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: 87vw;
height: 87vh;
position: absolute;
top: 0;
left: 0;
z-index: 1;
}
// 追踪控制面板样式
.tracking-panel {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
width: 80%;
background: rgba(0, 0, 0, 0.8);
border-radius: 8px;
padding: 15px;
color: white;
z-index: 100;
.panel-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
h3 {
margin: 0;
font-size: 18px;
}
select {
padding: 5px 10px;
border-radius: 4px;
border: none;
background: #333;
color: white;
}
}
.timeline-container {
margin-bottom: 15px;
.time-info {
margin-bottom: 10px;
text-align: center;
}
.timeline-slider {
width: 100%;
margin-bottom: 10px;
}
.time-labels {
display: flex;
justify-content: space-between;
font-size: 12px;
}
}
.control-buttons {
display: flex;
justify-content: center;
gap: 10px;
button {
padding: 8px 16px;
border-radius: 4px;
border: none;
background: #173349;
color: white;
cursor: pointer;
&:hover:not(:disabled) {
background: #1e4763;
}
&:disabled {
background: #555;
cursor: not-allowed;
}
}
}
}
}
</style>
\ No newline at end of file
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