|
|
|
|
<template>
|
|
|
|
|
<div>
|
|
|
|
|
<div ref="cesiumContainerRef" class="cesium-container"></div>
|
|
|
|
|
<div class="btn-group">
|
|
|
|
|
<!-- <el-button class="get-view-btn" size="small" type="primary" @click="getCurrentViewInfo"
|
|
|
|
|
style="margin-right: 10px;">获取当前视角</el-button> -->
|
|
|
|
|
<!-- <el-button class="view-btn" size="small" type="success" @click="switchView('overview')"
|
|
|
|
|
style="margin-right: 10px;">概览视角</el-button> -->
|
|
|
|
|
<!-- <el-button class="view-btn" size="small" type="warning" @click="switchView('view1')"
|
|
|
|
|
style="margin-right: 10px;">视角1</el-button>
|
|
|
|
|
<el-button class="view-btn" size="small" type="warning" @click="switchView('view2')"
|
|
|
|
|
style="margin-right: 10px;">视角2</el-button> -->
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
|
import { onMounted, onBeforeUnmount, ref, watch } from 'vue';
|
|
|
|
|
import coordtransform from 'coordtransform';
|
|
|
|
|
import { MapApi } from "@/api/shorepower/map";
|
|
|
|
|
import { toRaw } from 'vue'
|
|
|
|
|
// ⚠️ 注意:通过 window 访问全局 Cesium 对象
|
|
|
|
|
const Cesium = window.Cesium;
|
|
|
|
|
|
|
|
|
|
const props = defineProps({
|
|
|
|
|
shorePowerList: {
|
|
|
|
|
type: Array,
|
|
|
|
|
required: true,
|
|
|
|
|
// 可选:添加自定义 validator(更严格)
|
|
|
|
|
validator(value) {
|
|
|
|
|
return Array.isArray(value) && value.every(item =>
|
|
|
|
|
typeof item === 'object' &&
|
|
|
|
|
item !== null &&
|
|
|
|
|
typeof item.position === 'string'
|
|
|
|
|
// 注意:无法验证是否符合 ShorePowerBerth 结构(因为 JS 没有接口)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
shipDataList: {
|
|
|
|
|
type: Array,
|
|
|
|
|
required: true,
|
|
|
|
|
// 可选:添加自定义 validator(更严格)
|
|
|
|
|
validator(value) {
|
|
|
|
|
return Array.isArray(value) && value.every(item =>
|
|
|
|
|
typeof item === 'object' &&
|
|
|
|
|
item !== null &&
|
|
|
|
|
typeof item.shipName === 'string'
|
|
|
|
|
// 注意:无法验证是否符合 ShipRespVo 结构(因为 JS 没有接口)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
onInstanceClick: {
|
|
|
|
|
type: Function,
|
|
|
|
|
default: (data) => { }
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 自定义流动线材质类
|
|
|
|
|
class PolylineFlowMaterialProperty {
|
|
|
|
|
constructor(options) {
|
|
|
|
|
options = options || {};
|
|
|
|
|
this._definitionChanged = new Cesium.Event();
|
|
|
|
|
this._color = undefined;
|
|
|
|
|
this._colorSubscription = undefined;
|
|
|
|
|
this.color = options.color || Cesium.Color.YELLOW;
|
|
|
|
|
this.duration = options.duration || 2000; // 加快速度,从2000ms减少到1000ms
|
|
|
|
|
this.percent = options.percent || 0.1;
|
|
|
|
|
this.gradient = options.gradient || 0.01;
|
|
|
|
|
this._time = (new Date()).getTime();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
get isConstant() {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
get definitionChanged() {
|
|
|
|
|
return this._definitionChanged;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
getType() {
|
|
|
|
|
return 'PolylineFlow';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
getValue(time, result) {
|
|
|
|
|
if (!result) {
|
|
|
|
|
result = {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result.color = Cesium.Property.getValueOrClonedDefault(this._color, time, Cesium.Color.WHITE, result.color);
|
|
|
|
|
result.image = undefined;
|
|
|
|
|
result.time = ((new Date()).getTime() - this._time) % this.duration / this.duration;
|
|
|
|
|
result.percent = this.percent;
|
|
|
|
|
result.gradient = this.gradient;
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
equals(other) {
|
|
|
|
|
return this === other ||
|
|
|
|
|
(other instanceof PolylineFlowMaterialProperty &&
|
|
|
|
|
Cesium.Property.equals(this._color, other._color) &&
|
|
|
|
|
this.duration === other.duration &&
|
|
|
|
|
this.percent === other.percent &&
|
|
|
|
|
this.gradient === other.gradient);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Object.defineProperties(PolylineFlowMaterialProperty.prototype, {
|
|
|
|
|
color: Cesium.createPropertyDescriptor('color')
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 注册自定义材质
|
|
|
|
|
Cesium.Material.PolylineFlowType = 'PolylineFlow';
|
|
|
|
|
Cesium.Material._materialCache.addMaterial(Cesium.Material.PolylineFlowType, {
|
|
|
|
|
fabric: {
|
|
|
|
|
type: Cesium.Material.PolylineFlowType,
|
|
|
|
|
uniforms: {
|
|
|
|
|
color: new Cesium.Color(1.0, 1.0, 0.0, 1.0),
|
|
|
|
|
time: 0,
|
|
|
|
|
percent: 0.1,
|
|
|
|
|
gradient: 0.01
|
|
|
|
|
},
|
|
|
|
|
source: `
|
|
|
|
|
czm_material czm_getMaterial(czm_materialInput materialInput)
|
|
|
|
|
{
|
|
|
|
|
czm_material material = czm_getDefaultMaterial(materialInput);
|
|
|
|
|
vec2 st = materialInput.st;
|
|
|
|
|
float t = time * 3.0; // 调整箭头速度
|
|
|
|
|
|
|
|
|
|
// 基础线段颜色,确保线条始终可见
|
|
|
|
|
vec3 baseColor = color.rgb;
|
|
|
|
|
|
|
|
|
|
// 创建箭头效果
|
|
|
|
|
float alpha = 0.2; // 基础透明度
|
|
|
|
|
|
|
|
|
|
// 创建多个移动的箭头
|
|
|
|
|
float arrowPos1 = mod(t - st.s * 5.0, 5.0);
|
|
|
|
|
float arrow1 = smoothstep(0.7, 0.9, arrowPos1) * (1.0 - smoothstep(0.9, 1.1, arrowPos1));
|
|
|
|
|
float arrowHead1 = smoothstep(0.4, 0.9, arrowPos1) * (1.0 - smoothstep(0.9, 1.3, arrowPos1));
|
|
|
|
|
|
|
|
|
|
float arrowPos2 = mod(t - st.s * 5.0 + 2.5, 5.0);
|
|
|
|
|
float arrow2 = smoothstep(0.7, 0.9, arrowPos2) * (1.0 - smoothstep(0.9, 1.1, arrowPos2));
|
|
|
|
|
float arrowHead2 = smoothstep(0.4, 0.9, arrowPos2) * (1.0 - smoothstep(0.9, 1.3, arrowPos2));
|
|
|
|
|
|
|
|
|
|
// 组合箭头效果
|
|
|
|
|
float arrowEffect = max(max(arrow1, arrowHead1), max(arrow2, arrowHead2));
|
|
|
|
|
|
|
|
|
|
// 应用箭头效果到透明度
|
|
|
|
|
alpha = 0.2 + arrowEffect * 0.8;
|
|
|
|
|
|
|
|
|
|
material.alpha = alpha;
|
|
|
|
|
material.diffuse = baseColor;
|
|
|
|
|
return material;
|
|
|
|
|
}
|
|
|
|
|
`
|
|
|
|
|
},
|
|
|
|
|
translucent: function (material) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let viewer = null;
|
|
|
|
|
const cesiumContainerRef = ref(null); // 引用 DOM 元素
|
|
|
|
|
const history = ref(null); // 历史数据
|
|
|
|
|
const dataWithModels = ref([]); // 存储带有模型实例的数据列表
|
|
|
|
|
|
|
|
|
|
// 定义预设视角参数
|
|
|
|
|
const presetViews = {
|
|
|
|
|
overview: {
|
|
|
|
|
"longitude": 118.5132903711312,
|
|
|
|
|
"latitude": 38.84648563549342,
|
|
|
|
|
"height": 12675.768680075478,
|
|
|
|
|
"heading": 353.6789759103708,
|
|
|
|
|
"pitch": -36.657258995301596,
|
|
|
|
|
"roll": 0.0343469697692715
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 岸电箱点击回调函数
|
|
|
|
|
let onElectricalBoxClick = null;
|
|
|
|
|
let globalModelInstance = null
|
|
|
|
|
// 设置岸电箱点击回调函数的方法
|
|
|
|
|
const setElectricalBoxClickCallback = (callback) => {
|
|
|
|
|
onElectricalBoxClick = callback;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const createView = (obj) => {
|
|
|
|
|
return {
|
|
|
|
|
destination: Cesium.Cartesian3.fromDegrees(obj.longitude, obj.latitude, obj.height),
|
|
|
|
|
orientation: {
|
|
|
|
|
heading: Cesium.Math.toRadians(obj.heading),
|
|
|
|
|
pitch: Cesium.Math.toRadians(obj.pitch),
|
|
|
|
|
roll: Cesium.Math.toRadians(obj.roll)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onMounted(async () => {
|
|
|
|
|
if (!Cesium) {
|
|
|
|
|
console.error("Cesium 对象未找到,请检查 index.html 文件!");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 步骤 1: 初始化 Viewer 并配置为使用高德地图影像和平坦地形
|
|
|
|
|
try {
|
|
|
|
|
viewer = new Cesium.Viewer(cesiumContainerRef.value, {
|
|
|
|
|
// **核心设置:禁用所有默认影像,我们将手动添加高德地图**
|
|
|
|
|
imageryProvider: false,
|
|
|
|
|
// infoBox: true,
|
|
|
|
|
// **使用平坦地形,避免依赖 Cesium Ion**
|
|
|
|
|
terrainProvider: new Cesium.EllipsoidTerrainProvider(),
|
|
|
|
|
// sceneMode: Cesium.SceneMode.COLUMBUS_VIEW,
|
|
|
|
|
// 禁用所有不必要的UI控件
|
|
|
|
|
timeline: false,
|
|
|
|
|
animation: false,
|
|
|
|
|
baseLayerPicker: false,
|
|
|
|
|
geocoder: false,
|
|
|
|
|
sceneModePicker: false,
|
|
|
|
|
navigationHelpButton: false,
|
|
|
|
|
infoBox: false,
|
|
|
|
|
fullscreenButton: false,
|
|
|
|
|
homeButton: false,
|
|
|
|
|
|
|
|
|
|
// 启用抗锯齿和其他渲染优化
|
|
|
|
|
contextOptions: {
|
|
|
|
|
webgl: {
|
|
|
|
|
antialias: true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 禁用所有鼠标和键盘的相机控制操作
|
|
|
|
|
/* viewer.scene.screenSpaceCameraController.enableRotate = false;
|
|
|
|
|
viewer.scene.screenSpaceCameraController.enableTranslate = false;
|
|
|
|
|
viewer.scene.screenSpaceCameraController.enableZoom = false;
|
|
|
|
|
viewer.scene.screenSpaceCameraController.enableTilt = false;
|
|
|
|
|
viewer.scene.screenSpaceCameraController.enableLook = false; */
|
|
|
|
|
|
|
|
|
|
// 渲染优化设置
|
|
|
|
|
// 启用抗锯齿
|
|
|
|
|
viewer.scene.postProcessStages.fxaa.enabled = true;
|
|
|
|
|
|
|
|
|
|
// 启用深度测试,确保模型能够被地形正确遮挡
|
|
|
|
|
viewer.scene.globe.depthTestAgainstTerrain = true;
|
|
|
|
|
|
|
|
|
|
// 启用时钟动画,确保模型能够根据时间更新
|
|
|
|
|
viewer.clock.shouldAnimate = true;
|
|
|
|
|
|
|
|
|
|
// 相机控制设置
|
|
|
|
|
const controller = viewer.scene.screenSpaceCameraController;
|
|
|
|
|
// 设置最大缩放距离(相机到地球中心的最大距离)
|
|
|
|
|
// 500,000.0 米 = 500 公里
|
|
|
|
|
const MAX_DISTANCE = 500000.0;
|
|
|
|
|
|
|
|
|
|
// 启用缩放,然后设置其最大限制
|
|
|
|
|
controller.enableZoom = true;
|
|
|
|
|
controller.maximumZoomDistance = MAX_DISTANCE;
|
|
|
|
|
|
|
|
|
|
// (可选)同时限制最小缩放距离,防止滚轮进入模型内部
|
|
|
|
|
// 例如,限制最小距离为 1000.0 米
|
|
|
|
|
const MIN_HEIGHT = 1200;
|
|
|
|
|
controller.minimumZoomDistance = MIN_HEIGHT - 100;
|
|
|
|
|
|
|
|
|
|
const canvas = viewer.scene.canvas;
|
|
|
|
|
|
|
|
|
|
canvas.addEventListener(
|
|
|
|
|
"wheel",
|
|
|
|
|
(e) => {
|
|
|
|
|
const height = viewer.camera.positionCartographic.height;
|
|
|
|
|
|
|
|
|
|
// 只拦截“继续向内”
|
|
|
|
|
if (height <= MIN_HEIGHT && e.deltaY < 0) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
passive: false,
|
|
|
|
|
capture: true
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// viewer.scene.screenSpaceCameraController.minimumZoomDistance = 200;
|
|
|
|
|
// 步骤 2: 添加高德地图卫星影像底图
|
|
|
|
|
const gaodeImage = new Cesium.UrlTemplateImageryProvider({
|
|
|
|
|
url: "https://webst0{s}.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}",
|
|
|
|
|
subdomains: ['1', '2', '3', '4'],
|
|
|
|
|
layer: 'gaodeImgLayer',
|
|
|
|
|
style: 'default',
|
|
|
|
|
format: 'image/png',
|
|
|
|
|
maximumLevel: 16
|
|
|
|
|
});
|
|
|
|
|
const dataInfo = history.value ? history.value : await MapApi.getAllData()
|
|
|
|
|
const shipData = props.shipDataList
|
|
|
|
|
console.log('shipData', shipData)
|
|
|
|
|
const shorePowerList = props.shorePowerList
|
|
|
|
|
|
|
|
|
|
// const unitedData =
|
|
|
|
|
|
|
|
|
|
// 通用添加函数,遍历dataInfo并创建标记
|
|
|
|
|
let arr = {
|
|
|
|
|
arr1: [],
|
|
|
|
|
arr2: [],
|
|
|
|
|
arr3: [],
|
|
|
|
|
arr4: [],
|
|
|
|
|
arr5: []
|
|
|
|
|
}
|
|
|
|
|
// const addMarkersFromDataInfo = (dataInfo) => {
|
|
|
|
|
// // 更严格的数组检查
|
|
|
|
|
// if (!dataInfo || !Array.isArray(dataInfo) || dataInfo.length === 0) {
|
|
|
|
|
// console.log('No valid dataInfo array provided');
|
|
|
|
|
// return [];
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// // 创建新数组存储带有模型实例的数据
|
|
|
|
|
// const dataWithModelsArray = [];
|
|
|
|
|
|
|
|
|
|
// console.log(`Processing ${dataInfo.length} items`);
|
|
|
|
|
|
|
|
|
|
// dataInfo.forEach((item, index) => {
|
|
|
|
|
// // 检查基本数据结构
|
|
|
|
|
// if (!item) {
|
|
|
|
|
// console.warn(`Item at index ${index} is null or undefined`);
|
|
|
|
|
// return;
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// try {
|
|
|
|
|
// // 创建包含模型实例的数据项副本
|
|
|
|
|
// let itemWithModel = { ...item };
|
|
|
|
|
|
|
|
|
|
// // 解析data字段为JSON对象
|
|
|
|
|
// let dataObj;
|
|
|
|
|
// if (typeof item.data === 'string') {
|
|
|
|
|
// try {
|
|
|
|
|
// dataObj = JSON.parse(item.data);
|
|
|
|
|
// } catch (parseError) {
|
|
|
|
|
// console.warn(`Failed to parse data for item ${item.id || index}:`, parseError);
|
|
|
|
|
// dataWithModelsArray.push(itemWithModel); // 添加未解析成功的数据项
|
|
|
|
|
// return;
|
|
|
|
|
// }
|
|
|
|
|
// } else {
|
|
|
|
|
// dataObj = item.data;
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// // 检查dataObj是否有效
|
|
|
|
|
// if (!dataObj) {
|
|
|
|
|
// console.warn(`No data object found for item ${item.id || index}`);
|
|
|
|
|
// dataWithModelsArray.push(itemWithModel); // 添加无数据对象的数据项
|
|
|
|
|
// return;
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// // 获取坐标信息 - 更严格的验证
|
|
|
|
|
// let longitude, latitude;
|
|
|
|
|
// let wgsLon, wgsLat;
|
|
|
|
|
// if (dataObj.xy && Array.isArray(dataObj.xy) && dataObj.xy.length >= 2) {
|
|
|
|
|
// const wgsCoords = coordtransform.wgs84togcj02(dataObj.xy[1], dataObj.xy[0]);
|
|
|
|
|
// wgsLon = wgsCoords[0];
|
|
|
|
|
// wgsLat = wgsCoords[1];
|
|
|
|
|
// // 确保坐标是有效数字
|
|
|
|
|
// latitude = wgsLat;
|
|
|
|
|
// longitude = wgsLon;
|
|
|
|
|
// // 检查坐标是否为有效数字
|
|
|
|
|
// if (isNaN(latitude) || isNaN(longitude)) {
|
|
|
|
|
// console.warn(`Invalid coordinates for item ${item.id || index}:`, dataObj.xy);
|
|
|
|
|
// dataWithModelsArray.push(itemWithModel); // 添加坐标无效的数据项
|
|
|
|
|
// return;
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// // 检查坐标范围(简单验证)
|
|
|
|
|
// if (Math.abs(latitude) > 90 || Math.abs(longitude) > 180) {
|
|
|
|
|
// console.warn(`Coordinates out of range for item ${item.id || index}:`, dataObj.xy[1], dataObj.xy[0]);
|
|
|
|
|
// dataWithModelsArray.push(itemWithModel); // 添加坐标超出范围的数据项
|
|
|
|
|
// return;
|
|
|
|
|
// }
|
|
|
|
|
// } else {
|
|
|
|
|
// console.warn('无效的坐标信息:', item);
|
|
|
|
|
// dataWithModelsArray.push(itemWithModel); // 添加无有效坐标的数据项
|
|
|
|
|
// return;
|
|
|
|
|
// }
|
|
|
|
|
// console.log('dataObj.icon', dataObj.icon)
|
|
|
|
|
// if (dataObj.icon === 'ship_green' || dataObj.icon === 'ship_red') {
|
|
|
|
|
// const itemShipInfo = shipData.find(shipItem => (shipItem.shorePower.id === item.parentId) && item.type === 5)
|
|
|
|
|
// if (!itemShipInfo) {
|
|
|
|
|
// return;
|
|
|
|
|
// }
|
|
|
|
|
// const position = Cesium.Cartesian3.fromDegrees(wgsLon, wgsLat, 15);
|
|
|
|
|
// const statusPosition = Cesium.Cartesian3.fromDegrees(wgsLon, wgsLat, 1);
|
|
|
|
|
// const labelPosition = Cesium.Cartesian3.fromDegrees(wgsLon, wgsLat, 35);
|
|
|
|
|
// // 投放船模型
|
|
|
|
|
// const shipModel = viewer.entities.add({
|
|
|
|
|
// name: 'Cargo Ship Model',
|
|
|
|
|
// position: position,
|
|
|
|
|
// orientation: Cesium.Transforms.headingPitchRollQuaternion(
|
|
|
|
|
// position,
|
|
|
|
|
// new Cesium.HeadingPitchRoll(
|
|
|
|
|
// Cesium.Math.toRadians(dataObj.rotationAngle), // 航向角 (绕Z轴旋转)
|
|
|
|
|
// Cesium.Math.toRadians(0), // 俯仰角 (绕Y轴旋转)
|
|
|
|
|
// Cesium.Math.toRadians(0) // 翻滚角 (绕X轴旋转)
|
|
|
|
|
// )
|
|
|
|
|
// ),
|
|
|
|
|
// model: {
|
|
|
|
|
// uri: '/model/cargo_ship_07.glb',
|
|
|
|
|
// scale: 1.5, // 调整模型大小
|
|
|
|
|
// // minimumPixelSize: 100, // 确保模型至少显示为50像素大小
|
|
|
|
|
// // 启用深度测试,使模型能够被地形遮挡
|
|
|
|
|
// enableDepthTest: true,
|
|
|
|
|
// // 启用背光剔除,提高渲染性能
|
|
|
|
|
// backFaceCulling: true
|
|
|
|
|
// }
|
|
|
|
|
// });
|
|
|
|
|
|
|
|
|
|
// // 保存模型实例到数据项
|
|
|
|
|
// itemWithModel.modelInstance = shipModel;
|
|
|
|
|
// itemWithModel.modelType = 'ship';
|
|
|
|
|
// itemWithModel = { ...itemWithModel, ...itemShipInfo };
|
|
|
|
|
|
|
|
|
|
// viewer.entities.add({
|
|
|
|
|
// position: labelPosition, // 船上方约10米
|
|
|
|
|
// label: {
|
|
|
|
|
// text: itemShipInfo.shipBasicInfo.name || `Marker-${item.id || index}`,
|
|
|
|
|
// font: '20px sans-serif',
|
|
|
|
|
// fillColor: Cesium.Color.LIME,
|
|
|
|
|
// outlineColor: Cesium.Color.BLACK,
|
|
|
|
|
// outlineWidth: 2,
|
|
|
|
|
// style: Cesium.LabelStyle.FILL_AND_OUTLINE,
|
|
|
|
|
// pixelOffset: new Cesium.Cartesian2(0, -30), // 偏移量,避免与模型重叠
|
|
|
|
|
// disableDepthTestDistance: Number.POSITIVE_INFINITY // 始终可见
|
|
|
|
|
// }
|
|
|
|
|
// });
|
|
|
|
|
// // 根据概率分布决定是否显示状态图标
|
|
|
|
|
// // 50%概率不显示,25%显示故障,25%显示离线
|
|
|
|
|
// const rand = Math.random();
|
|
|
|
|
// let statusImage = null;
|
|
|
|
|
// if (rand < 1) {
|
|
|
|
|
// // 50%概率:不显示状态图标
|
|
|
|
|
// statusImage = null;
|
|
|
|
|
// } else if (rand < 0.9) {
|
|
|
|
|
// // 25%概率:显示故障图标
|
|
|
|
|
// statusImage = '/img/故障.png';
|
|
|
|
|
// } else {
|
|
|
|
|
// // 25%概率:显示离线图标
|
|
|
|
|
// statusImage = '/img/离线.png';
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// // 只有当需要显示状态图标时才添加实体
|
|
|
|
|
// if (statusImage) {
|
|
|
|
|
// const overlayBillboard = viewer.entities.add({
|
|
|
|
|
// position: statusPosition,
|
|
|
|
|
// billboard: {
|
|
|
|
|
// image: statusImage,
|
|
|
|
|
// horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
|
|
|
|
|
// verticalOrigin: Cesium.VerticalOrigin.CENTER,
|
|
|
|
|
// disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
|
|
|
|
// scale: new Cesium.CallbackProperty(function (time, result) {
|
|
|
|
|
// // t 会随着时间不断变化
|
|
|
|
|
// const t = Cesium.JulianDate.toDate(time).getTime() / 1000;
|
|
|
|
|
// const pulse = 0.4 + Math.sin(t * 4) * 0.1; // (基准0.6, 幅度±0.2)
|
|
|
|
|
// return pulse;
|
|
|
|
|
// }, false)
|
|
|
|
|
// }
|
|
|
|
|
// });
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
// if (dataObj.type === 'text') {
|
|
|
|
|
// const labelPosition = Cesium.Cartesian3.fromDegrees(wgsLon, wgsLat, 35);
|
|
|
|
|
// viewer.entities.add({
|
|
|
|
|
// position: labelPosition, // 船上方约10米
|
|
|
|
|
// label: {
|
|
|
|
|
// text: dataObj.name || `Marker-${item.id || index}`,
|
|
|
|
|
// font: '20px sans-serif',
|
|
|
|
|
// fillColor: Cesium.Color.WHITE,
|
|
|
|
|
// outlineColor: Cesium.Color.BLACK,
|
|
|
|
|
// outlineWidth: 2,
|
|
|
|
|
// style: Cesium.LabelStyle.FILL_AND_OUTLINE,
|
|
|
|
|
// pixelOffset: new Cesium.Cartesian2(0, -30), // 偏移量,避免与模型重叠
|
|
|
|
|
// disableDepthTestDistance: Number.POSITIVE_INFINITY // 始终可见
|
|
|
|
|
// }
|
|
|
|
|
// });
|
|
|
|
|
// }
|
|
|
|
|
// if (dataObj.type === 'icon' && (dataObj.icon === 'interface_blue' || dataObj.icon === 'interface_red')) {
|
|
|
|
|
// const itemShipInfo = shipData.find(shipItem => (shipItem.shorePower.id === item.parentId) && item.type === 5)
|
|
|
|
|
// const position = Cesium.Cartesian3.fromDegrees(wgsLon, wgsLat, 1);
|
|
|
|
|
// const labelPosition = Cesium.Cartesian3.fromDegrees(wgsLon, wgsLat, 5);
|
|
|
|
|
// // 投放岸电箱模型
|
|
|
|
|
// const electricalBoxModel = viewer.entities.add({
|
|
|
|
|
// name: '岸电箱',
|
|
|
|
|
// position: position,
|
|
|
|
|
// // 添加唯一标识用于识别点击事件
|
|
|
|
|
// properties: {
|
|
|
|
|
// modelType: 'electrical_box',
|
|
|
|
|
// data: itemWithModel
|
|
|
|
|
// },
|
|
|
|
|
// orientation: Cesium.Transforms.headingPitchRollQuaternion(
|
|
|
|
|
// position,
|
|
|
|
|
// new Cesium.HeadingPitchRoll(
|
|
|
|
|
// Cesium.Math.toRadians(dataObj.rotationAngle), // 航向角 (绕Z轴旋转)
|
|
|
|
|
// Cesium.Math.toRadians(0), // 俯仰角 (绕Y轴旋转)
|
|
|
|
|
// Cesium.Math.toRadians(0) // 翻滚角 (绕X轴旋转)
|
|
|
|
|
// )
|
|
|
|
|
// ),
|
|
|
|
|
// model: {
|
|
|
|
|
// uri: '/model/electrical_box.glb',
|
|
|
|
|
// scale: 1.5, // 调整模型大小
|
|
|
|
|
// // minimumPixelSize: 10, // 确保模型至少显示为50像素大小
|
|
|
|
|
// // 启用深度测试,使模型能够被地形遮挡
|
|
|
|
|
// enableDepthTest: true,
|
|
|
|
|
// // 启用背光剔除,提高渲染性能
|
|
|
|
|
// backFaceCulling: true
|
|
|
|
|
// },
|
|
|
|
|
// });
|
|
|
|
|
// console.log(item)
|
|
|
|
|
// if (item.name.includes('国投-206泊位-插座箱') || item.name.includes('国投-207泊位-插座箱')) {
|
|
|
|
|
// arr.push(electricalBoxModel)
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// // 保存模型实例到数据项
|
|
|
|
|
// itemWithModel.modelInstance = electricalBoxModel;
|
|
|
|
|
// itemWithModel.modelType = 'electrical_box';
|
|
|
|
|
// itemWithModel = { ...itemWithModel, ...itemShipInfo };
|
|
|
|
|
|
|
|
|
|
// viewer.entities.add({
|
|
|
|
|
// // pickable: false,
|
|
|
|
|
// position: labelPosition, // 船上方约10米
|
|
|
|
|
// properties: {
|
|
|
|
|
// modelType: 'electrical_box',
|
|
|
|
|
// data: itemWithModel
|
|
|
|
|
// },
|
|
|
|
|
// label: {
|
|
|
|
|
// text: '岸电箱' || `Marker-${item.id || index}`,
|
|
|
|
|
// font: '16px sans-serif',
|
|
|
|
|
// fillColor: Cesium.Color.WHITE,
|
|
|
|
|
// outlineColor: Cesium.Color.BLACK,
|
|
|
|
|
// outlineWidth: 2,
|
|
|
|
|
// style: Cesium.LabelStyle.FILL_AND_OUTLINE,
|
|
|
|
|
// pixelOffset: new Cesium.Cartesian2(0, -30), // 偏移量,避免与模型重叠
|
|
|
|
|
// disableDepthTestDistance: Number.POSITIVE_INFINITY // 始终可见
|
|
|
|
|
// }
|
|
|
|
|
// });
|
|
|
|
|
|
|
|
|
|
// // 根据概率分布决定是否显示状态图标
|
|
|
|
|
// // 50%概率不显示,25%显示故障,25%显示离线
|
|
|
|
|
// console.log(shorePowerList, itemShipInfo)
|
|
|
|
|
// const x = itemShipInfo.shorePower.id;
|
|
|
|
|
// const itemObj = shorePowerList.find(obj => obj.id === x);
|
|
|
|
|
// const rand = Math.random();
|
|
|
|
|
// let statusImage = null;
|
|
|
|
|
// if ([3, 4, 6].includes(itemObj.status)) {
|
|
|
|
|
// // 50%概率:不显示状态图标
|
|
|
|
|
// statusImage = null;
|
|
|
|
|
// } else if ([1, 2, 8].includes(itemObj.status)) {
|
|
|
|
|
// // 25%概率:显示故障图标
|
|
|
|
|
// statusImage = '/img/故障.png';
|
|
|
|
|
// } else {
|
|
|
|
|
// // 25%概率:显示离线图标
|
|
|
|
|
// statusImage = '/img/离线.png';
|
|
|
|
|
// }
|
|
|
|
|
// // 只有当需要显示状态图标时才添加实体
|
|
|
|
|
// if (statusImage) {
|
|
|
|
|
// const overlayBillboard = viewer.entities.add({
|
|
|
|
|
// position: position,
|
|
|
|
|
// // pickable: false,
|
|
|
|
|
// properties: {
|
|
|
|
|
// modelType: 'electrical_box',
|
|
|
|
|
// data: itemWithModel
|
|
|
|
|
// },
|
|
|
|
|
// billboard: {
|
|
|
|
|
// image: statusImage,
|
|
|
|
|
// horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
|
|
|
|
|
// verticalOrigin: Cesium.VerticalOrigin.CENTER,
|
|
|
|
|
// disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
|
|
|
|
// scale: new Cesium.CallbackProperty(function (time, result) {
|
|
|
|
|
// // t 会随着时间不断变化
|
|
|
|
|
// const t = Cesium.JulianDate.toDate(time).getTime() / 1000;
|
|
|
|
|
// const pulse = 0.2 + Math.sin(t * 4) * 0.1; // (基准0.6, 幅度±0.2)
|
|
|
|
|
// return pulse;
|
|
|
|
|
// }, false)
|
|
|
|
|
// }
|
|
|
|
|
// });
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// // 创建红点标记
|
|
|
|
|
// /* const entity = viewer.entities.add({
|
|
|
|
|
// name: item.name || `Marker-${item.id || index}`,
|
|
|
|
|
// position: Cesium.Cartesian3.fromDegrees(wgsLon, wgsLat, 1),
|
|
|
|
|
// point: {
|
|
|
|
|
// pixelSize: 10,
|
|
|
|
|
// color: Cesium.Color.RED,
|
|
|
|
|
// outlineColor: Cesium.Color.WHITE,
|
|
|
|
|
// outlineWidth: 2
|
|
|
|
|
// },
|
|
|
|
|
// // 可以添加标签显示名称
|
|
|
|
|
// label: {
|
|
|
|
|
// text: item.name || `Marker-${item.id || index}`,
|
|
|
|
|
// font: '14px sans-serif',
|
|
|
|
|
// fillColor: Cesium.Color.WHITE,
|
|
|
|
|
// outlineColor: Cesium.Color.BLACK,
|
|
|
|
|
// outlineWidth: 2,
|
|
|
|
|
// style: Cesium.LabelStyle.FILL_AND_OUTLINE,
|
|
|
|
|
// verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
|
|
|
|
// pixelOffset: new Cesium.Cartesian2(0, -15)
|
|
|
|
|
// }
|
|
|
|
|
// });
|
|
|
|
|
// // 保存点标记实例到数据项
|
|
|
|
|
// itemWithModel.modelInstance = entity;
|
|
|
|
|
// itemWithModel.modelType = 'point';
|
|
|
|
|
// */
|
|
|
|
|
|
|
|
|
|
// // 将处理后的数据项添加到新数组
|
|
|
|
|
// dataWithModelsArray.push(itemWithModel);
|
|
|
|
|
// } catch (error) {
|
|
|
|
|
// console.error('Error processing item:', item, error);
|
|
|
|
|
// // 创建一个基本的数据项副本并添加到数组
|
|
|
|
|
// dataWithModelsArray.push({ ...item });
|
|
|
|
|
// }
|
|
|
|
|
// });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// return dataWithModelsArray;
|
|
|
|
|
// };
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 步骤 3: 将图层添加到 Viewer 中
|
|
|
|
|
viewer.imageryLayers.addImageryProvider(gaodeImage);
|
|
|
|
|
|
|
|
|
|
// const addStoreP
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 调用通用函数创建标记并获取带有模型实例的数据
|
|
|
|
|
// dataWithModels.value = addMarkersFromDataInfo(dataInfo);
|
|
|
|
|
const createMarker = () => {
|
|
|
|
|
const modelInstance = []
|
|
|
|
|
const labelInstance = []
|
|
|
|
|
const oneTitleInstance = []
|
|
|
|
|
const twoTitleInstance = []
|
|
|
|
|
shorePowerList.forEach((item) => {
|
|
|
|
|
const itemShipInfo = dataInfo.filter(mapitem => (item.id === mapitem.parentId) && mapitem.type === 5)
|
|
|
|
|
const result = itemShipInfo
|
|
|
|
|
.filter(item => {
|
|
|
|
|
try {
|
|
|
|
|
const data = JSON.parse(item.data);
|
|
|
|
|
return data.type === 'icon' && data.icon.includes('interface');
|
|
|
|
|
} catch (e) {
|
|
|
|
|
return false; // 忽略无法解析的 data
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.map(item => JSON.parse(item.data));
|
|
|
|
|
if (!result || result.length === 0) return
|
|
|
|
|
const dataObj = result[0]
|
|
|
|
|
let longitude, latitude;
|
|
|
|
|
let wgsLon, wgsLat;
|
|
|
|
|
const wgsCoords = coordtransform.wgs84togcj02(dataObj.xy[1], dataObj.xy[0]);
|
|
|
|
|
wgsLon = wgsCoords[0];
|
|
|
|
|
wgsLat = wgsCoords[1];
|
|
|
|
|
latitude = wgsLat;
|
|
|
|
|
longitude = wgsLon;
|
|
|
|
|
const xposition = Cesium.Cartesian3.fromDegrees(wgsCoords[0], wgsCoords[1], 1);
|
|
|
|
|
const xelectricalBoxModel = viewer.entities.add({
|
|
|
|
|
name: '岸电箱',
|
|
|
|
|
position: xposition,
|
|
|
|
|
// 添加唯一标识用于识别点击事件
|
|
|
|
|
properties: {
|
|
|
|
|
modelType: 'shorepower_box',
|
|
|
|
|
data: item
|
|
|
|
|
},
|
|
|
|
|
orientation: Cesium.Transforms.headingPitchRollQuaternion(
|
|
|
|
|
xposition,
|
|
|
|
|
new Cesium.HeadingPitchRoll(
|
|
|
|
|
Cesium.Math.toRadians(dataObj.rotationAngle), // 航向角 (绕Z轴旋转)
|
|
|
|
|
Cesium.Math.toRadians(0), // 俯仰角 (绕Y轴旋转)
|
|
|
|
|
Cesium.Math.toRadians(0) // 翻滚角 (绕X轴旋转)
|
|
|
|
|
)
|
|
|
|
|
),
|
|
|
|
|
model: {
|
|
|
|
|
uri: '/model/electrical_box.glb',
|
|
|
|
|
scale: 1.5,
|
|
|
|
|
enableDepthTest: true,
|
|
|
|
|
backFaceCulling: true
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
console.log(item.name)
|
|
|
|
|
if (['国投-206泊位-高压', '国投-207泊位-高压', '国投-208泊位-高压'].includes(item.name)) {
|
|
|
|
|
arr.arr1.push(xelectricalBoxModel)
|
|
|
|
|
}
|
|
|
|
|
if (['国投-209泊位-高压', '国投-210泊位-高压'].includes(item.name)) {
|
|
|
|
|
arr.arr2.push(xelectricalBoxModel)
|
|
|
|
|
}
|
|
|
|
|
if (['国投-204泊位-高压', '国投-205泊位-高压'].includes(item.name)) {
|
|
|
|
|
arr.arr3.push(xelectricalBoxModel)
|
|
|
|
|
}
|
|
|
|
|
if (['华电-806泊位-高压', '华电-807泊位-高压'].includes(item.name)) {
|
|
|
|
|
arr.arr4.push(xelectricalBoxModel)
|
|
|
|
|
}
|
|
|
|
|
if (['华电-808泊位-高压', '华电-809泊位-高压', '华电-810泊位-高压'].includes(item.name)) {
|
|
|
|
|
arr.arr5.push(xelectricalBoxModel)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
modelInstance.push({
|
|
|
|
|
model: xelectricalBoxModel,
|
|
|
|
|
type: 'shorepower_box',
|
|
|
|
|
data: item
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const MODEL_HEIGHT_METERS = 80;
|
|
|
|
|
const cartographic = Cesium.Cartographic.fromCartesian(xposition);
|
|
|
|
|
const labelAltitude = cartographic.height + MODEL_HEIGHT_METERS;
|
|
|
|
|
const labelPosition = Cesium.Cartesian3.fromRadians(
|
|
|
|
|
cartographic.longitude,
|
|
|
|
|
cartographic.latitude,
|
|
|
|
|
labelAltitude
|
|
|
|
|
);
|
|
|
|
|
const xelectricalBoxLabel = viewer.entities.add({
|
|
|
|
|
// 使用 3D 空间计算出的新位置
|
|
|
|
|
position: labelPosition,
|
|
|
|
|
name: '岸电箱 Label',
|
|
|
|
|
label: {
|
|
|
|
|
text: '岸电箱',
|
|
|
|
|
font: '24px sans-serif',
|
|
|
|
|
fillColor: Cesium.Color.WHITE,
|
|
|
|
|
outlineColor: Cesium.Color.BLACK,
|
|
|
|
|
outlineWidth: 2,
|
|
|
|
|
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
|
|
|
|
|
disableDepthTest: true,
|
|
|
|
|
scaleByDistance: new Cesium.NearFarScalar(1000, 1.0, 50000, 0.1)
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let statusImage = null;
|
|
|
|
|
if ([3, 4, 6].includes(item.status)) {
|
|
|
|
|
// 50%概率:不显示状态图标
|
|
|
|
|
statusImage = null;
|
|
|
|
|
} else if ([1, 2, 8].includes(item.status)) {
|
|
|
|
|
// 25%概率:显示故障图标
|
|
|
|
|
statusImage = '/img/故障.png';
|
|
|
|
|
} else {
|
|
|
|
|
// 25%概率:显示离线图标
|
|
|
|
|
statusImage = '/img/离线.png';
|
|
|
|
|
}
|
|
|
|
|
// 只有当需要显示状态图标时才添加实体
|
|
|
|
|
if (statusImage) {
|
|
|
|
|
const overlayBillboard = viewer.entities.add({
|
|
|
|
|
position: xposition,
|
|
|
|
|
properties: {
|
|
|
|
|
modelType: 'shorepower_box',
|
|
|
|
|
data: item
|
|
|
|
|
},
|
|
|
|
|
billboard: {
|
|
|
|
|
image: statusImage,
|
|
|
|
|
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
|
|
|
|
|
verticalOrigin: Cesium.VerticalOrigin.CENTER,
|
|
|
|
|
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
|
|
|
|
scale: new Cesium.CallbackProperty(function (time, result) {
|
|
|
|
|
// t 会随着时间不断变化
|
|
|
|
|
const t = Cesium.JulianDate.toDate(time).getTime() / 1000;
|
|
|
|
|
const pulse = 0.2 + Math.sin(t * 4) * 0.1; // (基准0.6, 幅度±0.2)
|
|
|
|
|
return pulse;
|
|
|
|
|
}, false)
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
labelInstance.push(xelectricalBoxLabel)
|
|
|
|
|
})
|
|
|
|
|
shipData.forEach(item => {
|
|
|
|
|
// console.log(dataInfo)
|
|
|
|
|
|
|
|
|
|
const itemShipInfo = dataInfo.filter(shipItem => (item.shorePower.id === shipItem.parentId) && shipItem.type === 5)
|
|
|
|
|
// console.log(itemShipInfo)
|
|
|
|
|
const result = itemShipInfo
|
|
|
|
|
.filter(item => {
|
|
|
|
|
try {
|
|
|
|
|
const data = JSON.parse(item.data);
|
|
|
|
|
return data.type === 'icon' && data.icon.includes('ship_green');
|
|
|
|
|
} catch (e) {
|
|
|
|
|
return false; // 忽略无法解析的 data
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.map(item => JSON.parse(item.data));
|
|
|
|
|
if (!result || result.length === 0) return
|
|
|
|
|
const dataObj = result[0]
|
|
|
|
|
console.log(dataObj)
|
|
|
|
|
let longitude, latitude;
|
|
|
|
|
let wgsLon, wgsLat;
|
|
|
|
|
const wgsCoords = coordtransform.wgs84togcj02(dataObj.xy[1], dataObj.xy[0]);
|
|
|
|
|
wgsLon = wgsCoords[0];
|
|
|
|
|
wgsLat = wgsCoords[1];
|
|
|
|
|
latitude = wgsLat;
|
|
|
|
|
longitude = wgsLon;
|
|
|
|
|
const xposition = Cesium.Cartesian3.fromDegrees(wgsCoords[0], wgsCoords[1], 15);
|
|
|
|
|
const xelectricalBoxModel = viewer.entities.add({
|
|
|
|
|
// name: '岸电箱',
|
|
|
|
|
position: xposition,
|
|
|
|
|
// 添加唯一标识用于识别点击事件
|
|
|
|
|
properties: {
|
|
|
|
|
modelType: 'ship',
|
|
|
|
|
data: item
|
|
|
|
|
},
|
|
|
|
|
orientation: Cesium.Transforms.headingPitchRollQuaternion(
|
|
|
|
|
xposition,
|
|
|
|
|
new Cesium.HeadingPitchRoll(
|
|
|
|
|
Cesium.Math.toRadians(dataObj.rotationAngle), // 航向角 (绕Z轴旋转)
|
|
|
|
|
Cesium.Math.toRadians(0), // 俯仰角 (绕Y轴旋转)
|
|
|
|
|
Cesium.Math.toRadians(0) // 翻滚角 (绕X轴旋转)
|
|
|
|
|
)
|
|
|
|
|
),
|
|
|
|
|
model: {
|
|
|
|
|
uri: '/model/cargo_ship_07.glb',
|
|
|
|
|
scale: 1.5,
|
|
|
|
|
enableDepthTest: true,
|
|
|
|
|
backFaceCulling: true
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
modelInstance.push({
|
|
|
|
|
model: xelectricalBoxModel,
|
|
|
|
|
type: 'ship',
|
|
|
|
|
data: item
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const MODEL_HEIGHT_METERS = 80;
|
|
|
|
|
const cartographic = Cesium.Cartographic.fromCartesian(xposition);
|
|
|
|
|
const labelAltitude = cartographic.height + MODEL_HEIGHT_METERS;
|
|
|
|
|
const labelPosition = Cesium.Cartesian3.fromRadians(
|
|
|
|
|
cartographic.longitude,
|
|
|
|
|
cartographic.latitude,
|
|
|
|
|
labelAltitude
|
|
|
|
|
);
|
|
|
|
|
const xelectricalBoxLabel = viewer.entities.add({
|
|
|
|
|
// 使用 3D 空间计算出的新位置
|
|
|
|
|
position: labelPosition,
|
|
|
|
|
name: '船',
|
|
|
|
|
label: {
|
|
|
|
|
text: item.shipBasicInfo.name,
|
|
|
|
|
font: '28px sans-serif',
|
|
|
|
|
fillColor: Cesium.Color.LIME,
|
|
|
|
|
outlineColor: Cesium.Color.BLACK,
|
|
|
|
|
outlineWidth: 2,
|
|
|
|
|
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
|
|
|
|
|
disableDepthTest: true,
|
|
|
|
|
scaleByDistance: new Cesium.NearFarScalar(1000, 1.0, 50000, 0.1)
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
labelInstance.push(xelectricalBoxLabel)
|
|
|
|
|
|
|
|
|
|
/* if (!itemShipInfo) {
|
|
|
|
|
return;
|
|
|
|
|
} */
|
|
|
|
|
/* if (!itemShipInfo || itemShipInfo.length === 0) return
|
|
|
|
|
const itemShipInfoObj = itemShipInfo[0]
|
|
|
|
|
|
|
|
|
|
console.log('itemShipInfoObj', itemShipInfoObj)
|
|
|
|
|
console.log('dataInfo', dataInfo)
|
|
|
|
|
console.log('item', item) */
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 其他信息
|
|
|
|
|
dataInfo.forEach(item => {
|
|
|
|
|
// console.log(item)
|
|
|
|
|
if (['华能曹妃甸码头名字', '华电储运码头名字', '国投曹妃甸码头名字'].includes(item.name)) {
|
|
|
|
|
const dataObj = JSON.parse(item.data)
|
|
|
|
|
let longitude, latitude;
|
|
|
|
|
let wgsLon, wgsLat;
|
|
|
|
|
const wgsCoords = coordtransform.wgs84togcj02(dataObj.xy[1], dataObj.xy[0]);
|
|
|
|
|
wgsLon = wgsCoords[0];
|
|
|
|
|
wgsLat = wgsCoords[1];
|
|
|
|
|
// latitude = wgsLat;
|
|
|
|
|
// longitude = wgsLon;
|
|
|
|
|
const labelPosition = Cesium.Cartesian3.fromDegrees(wgsLon, wgsLat, 0);
|
|
|
|
|
const label = viewer.entities.add({
|
|
|
|
|
position: labelPosition, // 船上方约10米
|
|
|
|
|
label: {
|
|
|
|
|
text: dataObj.name || `Marker-${item.id || index}`,
|
|
|
|
|
font: '16px sans-serif',
|
|
|
|
|
fillColor: Cesium.Color.WHITE,
|
|
|
|
|
outlineColor: Cesium.Color.BLACK,
|
|
|
|
|
outlineWidth: 2,
|
|
|
|
|
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
|
|
|
|
|
pixelOffset: new Cesium.Cartesian2(0, -30), // 偏移量,避免与模型重叠
|
|
|
|
|
disableDepthTestDistance: Number.POSITIVE_INFINITY // 始终可见
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
twoTitleInstance.push(label)
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
dataInfo.forEach(item => {
|
|
|
|
|
// console.log(item)
|
|
|
|
|
if (['港区'].includes(item.name)) {
|
|
|
|
|
const dataObj = JSON.parse(item.data)
|
|
|
|
|
let longitude, latitude;
|
|
|
|
|
let wgsLon, wgsLat;
|
|
|
|
|
const wgsCoords = coordtransform.wgs84togcj02(dataObj.xy[1], dataObj.xy[0]);
|
|
|
|
|
wgsLon = wgsCoords[0];
|
|
|
|
|
wgsLat = wgsCoords[1];
|
|
|
|
|
// latitude = wgsLat;
|
|
|
|
|
// longitude = wgsLon;
|
|
|
|
|
const labelPosition = Cesium.Cartesian3.fromDegrees(wgsLon, wgsLat, 0);
|
|
|
|
|
const label = viewer.entities.add({
|
|
|
|
|
position: labelPosition, // 船上方约10米
|
|
|
|
|
label: {
|
|
|
|
|
text: dataObj.name || `Marker-${item.id || index}`,
|
|
|
|
|
font: '16px sans-serif',
|
|
|
|
|
fillColor: Cesium.Color.WHITE,
|
|
|
|
|
outlineColor: Cesium.Color.BLACK,
|
|
|
|
|
outlineWidth: 2,
|
|
|
|
|
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
|
|
|
|
|
pixelOffset: new Cesium.Cartesian2(0, -30), // 偏移量,避免与模型重叠
|
|
|
|
|
disableDepthTestDistance: Number.POSITIVE_INFINITY // 始终可见
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
oneTitleInstance.push(label)
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
return {
|
|
|
|
|
modelInstance,
|
|
|
|
|
labelInstance,
|
|
|
|
|
oneTitleInstance,
|
|
|
|
|
twoTitleInstance
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const { modelInstance, labelInstance, oneTitleInstance, twoTitleInstance } = createMarker()
|
|
|
|
|
globalModelInstance = modelInstance
|
|
|
|
|
// const xitemShipInfo = shipData.find(shipItem => (shipItem.shorePower.id === item.parentId) && item.type === 5)
|
|
|
|
|
const tempExe = [
|
|
|
|
|
{ lat: 38.95011597126687, lon: 118.45831747850713 },
|
|
|
|
|
{ lat: 38.94718179373664, lon: 118.46547582577227 },
|
|
|
|
|
{ lat: 38.95400625667645, lon: 118.45959803541668 },
|
|
|
|
|
{ lat: 38.97994932460509, lon: 118.45949374251144 },
|
|
|
|
|
{ lat: 38.99238637115607, lon: 118.45970382914341 },
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
// 创建流动线材质
|
|
|
|
|
function createFlowMaterial() {
|
|
|
|
|
return new PolylineFlowMaterialProperty({
|
|
|
|
|
color: Cesium.Color.YELLOW,
|
|
|
|
|
duration: 5000, // 动画持续时间(毫秒)
|
|
|
|
|
percent: 0.6, // 流动部分占整个线条的比例
|
|
|
|
|
gradient: 0.3 // 流动部分的渐变
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
function tempCreate(item) {
|
|
|
|
|
tempExe.forEach((exeItem, index) => {
|
|
|
|
|
const wgsCoords = coordtransform.wgs84togcj02(exeItem.lon, exeItem.lat);
|
|
|
|
|
const xposition = Cesium.Cartesian3.fromDegrees(exeItem.lon, exeItem.lat, 1);
|
|
|
|
|
const xelectricalBoxModel = viewer.entities.add({
|
|
|
|
|
name: '岸电箱',
|
|
|
|
|
position: xposition,
|
|
|
|
|
// 添加唯一标识用于识别点击事件
|
|
|
|
|
properties: {
|
|
|
|
|
modelType: 'electrical_box',
|
|
|
|
|
// data: itemWithModel
|
|
|
|
|
},
|
|
|
|
|
orientation: Cesium.Transforms.headingPitchRollQuaternion(
|
|
|
|
|
xposition,
|
|
|
|
|
new Cesium.HeadingPitchRoll(
|
|
|
|
|
Cesium.Math.toRadians(150), // 航向角 (绕Z轴旋转)
|
|
|
|
|
Cesium.Math.toRadians(0), // 俯仰角 (绕Y轴旋转)
|
|
|
|
|
Cesium.Math.toRadians(0) // 翻滚角 (绕X轴旋转)
|
|
|
|
|
)
|
|
|
|
|
),
|
|
|
|
|
model: {
|
|
|
|
|
uri: '/model/electrical_box.glb',
|
|
|
|
|
scale: 1.5,
|
|
|
|
|
enableDepthTest: true,
|
|
|
|
|
backFaceCulling: true
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
const MODEL_HEIGHT_METERS = 80;
|
|
|
|
|
|
|
|
|
|
// 1. 获取模型的世界坐标 (xposition)
|
|
|
|
|
// xposition = Cesium.Cartesian3.fromDegrees(wgsCoords[0], wgsCoords[1], 1);
|
|
|
|
|
|
|
|
|
|
// 2. 将标签的位置计算为:模型位置 + 高度偏移
|
|
|
|
|
// 我们需要沿着地球的法线方向(即向上)偏移。
|
|
|
|
|
|
|
|
|
|
// 获取模型位置的地理坐标 (经度, 纬度, 高度)
|
|
|
|
|
const cartographic = Cesium.Cartographic.fromCartesian(xposition);
|
|
|
|
|
|
|
|
|
|
// 增加高度,将标签放置在模型顶部上方
|
|
|
|
|
const labelAltitude = cartographic.height + MODEL_HEIGHT_METERS;
|
|
|
|
|
|
|
|
|
|
// 将新的地理坐标转换回世界坐标作为标签的位置
|
|
|
|
|
const labelPosition = Cesium.Cartesian3.fromRadians(
|
|
|
|
|
cartographic.longitude,
|
|
|
|
|
cartographic.latitude,
|
|
|
|
|
labelAltitude
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// 添加 Label 实体
|
|
|
|
|
viewer.entities.add({
|
|
|
|
|
// 使用 3D 空间计算出的新位置
|
|
|
|
|
position: labelPosition,
|
|
|
|
|
name: '岸电箱 Label',
|
|
|
|
|
label: {
|
|
|
|
|
text: '岸电箱',
|
|
|
|
|
font: '24px sans-serif',
|
|
|
|
|
fillColor: Cesium.Color.WHITE,
|
|
|
|
|
outlineColor: Cesium.Color.BLACK,
|
|
|
|
|
outlineWidth: 2,
|
|
|
|
|
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
|
|
|
|
|
|
|
|
|
|
// *** 关键设置 1:禁用深度测试,确保标签显示在模型上方 ***
|
|
|
|
|
// 确保它不会被模型或地球遮挡。
|
|
|
|
|
disableDepthTest: true,
|
|
|
|
|
|
|
|
|
|
// 关键设置 2:移除 pixelOffset(因为我们使用了 3D 位置偏移)
|
|
|
|
|
// pixelOffset: new Cesium.Cartesian2(0, 0),
|
|
|
|
|
|
|
|
|
|
// 关键设置 3:控制文字大小随距离缩放 (实现“真实大小”效果)
|
|
|
|
|
// 使用 near/far scalar 控制,避免缩放地图时文字过大
|
|
|
|
|
scaleByDistance: new Cesium.NearFarScalar(1000, 1.0, 50000, 0.1)
|
|
|
|
|
// ⚠️ 请根据您的场景调整这四个参数
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
// 创建连接线:将xelectricalBoxModel与arr中的每个实体连接
|
|
|
|
|
arr[`arr${index + 1}`].forEach((entity, idx) => {
|
|
|
|
|
// 创建唯一的线实体ID
|
|
|
|
|
const lineEntity = viewer.entities.add({
|
|
|
|
|
name: `connection_line_${idx}`,
|
|
|
|
|
polyline: {
|
|
|
|
|
positions: new Cesium.CallbackProperty(() => {
|
|
|
|
|
// 获取主岸电箱的位置
|
|
|
|
|
const mainPosition = xelectricalBoxModel.position.getValue(viewer.clock.currentTime);
|
|
|
|
|
// 获取目标实体的位置
|
|
|
|
|
const targetPosition = entity.position.getValue(viewer.clock.currentTime);
|
|
|
|
|
|
|
|
|
|
// 返回包含两个点的数组(起点和终点)
|
|
|
|
|
return [mainPosition, targetPosition];
|
|
|
|
|
}, false),
|
|
|
|
|
width: 3,
|
|
|
|
|
material: createFlowMaterial(),
|
|
|
|
|
// 启用深度测试,使线条能够被地形遮挡
|
|
|
|
|
enableDepthTest: true
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
console.log('lineEntity', lineEntity)
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
tempCreate()
|
|
|
|
|
|
|
|
|
|
// 设置初始视角
|
|
|
|
|
viewer.camera.flyTo(createView(presetViews.overview));
|
|
|
|
|
// 设置显示高度阈值
|
|
|
|
|
const SHOW_HEIGHT = 30000; // 高度 > 5000m 才显示模型等信息
|
|
|
|
|
// const targets = []; // 把需要控制的 entities push 进来
|
|
|
|
|
/* const targets = titleInstance
|
|
|
|
|
.filter(item => item.modelInstance)
|
|
|
|
|
.map(item => toRaw(item.modelInstance)); */
|
|
|
|
|
// console.log('targets', targets)
|
|
|
|
|
viewer.camera.changed.addEventListener(() => {
|
|
|
|
|
// 这个函数会在相机的任何属性发生变化后立即被调用。
|
|
|
|
|
const height = viewer.camera.positionCartographic.height;
|
|
|
|
|
// console.log("相机高度(即缩放级别)已变化:", height.toFixed(2), "米");
|
|
|
|
|
|
|
|
|
|
// 控制模型显示/隐藏
|
|
|
|
|
const visible = height <= SHOW_HEIGHT; // 1000m内才显示模型等信息
|
|
|
|
|
twoTitleInstance.forEach(entity => {
|
|
|
|
|
if (entity.show !== visible) {
|
|
|
|
|
entity.show = visible;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
oneTitleInstance.forEach(entity => {
|
|
|
|
|
if (entity.show === visible) {
|
|
|
|
|
entity.show = !visible;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 其他缩放级别逻辑
|
|
|
|
|
if (height < 100000) {
|
|
|
|
|
// 放大到街景级别
|
|
|
|
|
console.log("当前处于近距离缩放级别。");
|
|
|
|
|
} else if (height > 5000000) {
|
|
|
|
|
// 缩小到全球级别
|
|
|
|
|
console.log("当前处于远距离缩放级别。");
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 添加地图点击事件
|
|
|
|
|
/* const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
|
|
|
|
|
handler.setInputAction(function (movement) {
|
|
|
|
|
console.log('地图点击事件触发');
|
|
|
|
|
|
|
|
|
|
// 首先检查是否点击到了模型
|
|
|
|
|
const pickedObject = viewer.scene.pick(movement.position);
|
|
|
|
|
|
|
|
|
|
if (pickedObject && pickedObject.id && pickedObject.id.properties) {
|
|
|
|
|
// 检查是否是岸电箱模型
|
|
|
|
|
const properties = pickedObject.id.properties;
|
|
|
|
|
if (properties.modelType && properties.modelType.getValue() === 'shorepower_box') {
|
|
|
|
|
// 触发岸电箱点击回调
|
|
|
|
|
const electricalBoxData = properties.data.getValue();
|
|
|
|
|
console.log('岸电箱模型被点击:', electricalBoxData);
|
|
|
|
|
|
|
|
|
|
// 如果定义了回调函数,则调用它
|
|
|
|
|
if (typeof onElectricalBoxClick === 'function') {
|
|
|
|
|
onElectricalBoxClick(electricalBoxData);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 阻止进一步处理
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果不是点击模型,则处理地面点击
|
|
|
|
|
// 尝试多种方式获取点击位置
|
|
|
|
|
const ray = viewer.camera.getPickRay(movement.position);
|
|
|
|
|
|
|
|
|
|
// 方法1: 从地球表面拾取
|
|
|
|
|
let cartesian = viewer.scene.globe.pick(ray, viewer.scene);
|
|
|
|
|
|
|
|
|
|
// 如果从地球表面拾取失败,尝试从场景中拾取
|
|
|
|
|
if (!cartesian) {
|
|
|
|
|
cartesian = viewer.scene.pickPosition(movement.position);
|
|
|
|
|
console.log('使用场景拾取方法');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果仍然失败,尝试使用相机计算位置
|
|
|
|
|
if (!cartesian) {
|
|
|
|
|
// 获取屏幕中心点作为替代方案(仅用于调试)
|
|
|
|
|
console.log('无法从点击位置获取精确坐标');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cartesian) {
|
|
|
|
|
const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
|
|
|
|
|
if (cartographic) {
|
|
|
|
|
const longitude = Cesium.Math.toDegrees(cartographic.longitude);
|
|
|
|
|
const latitude = Cesium.Math.toDegrees(cartographic.latitude);
|
|
|
|
|
const gcj02 = coordtransform.gcj02towgs84(longitude, latitude);
|
|
|
|
|
console.log('点击位置经纬度:', {
|
|
|
|
|
longitude: gcj02[0],
|
|
|
|
|
latitude: gcj02[1]
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
console.log('无法从笛卡尔坐标转换为大地坐标');
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
console.log('未能获取到地球表面的点击位置');
|
|
|
|
|
}
|
|
|
|
|
}, Cesium.ScreenSpaceEventType.LEFT_CLICK); */
|
|
|
|
|
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
|
|
|
|
|
handler.setInputAction(function (movement) {
|
|
|
|
|
const pickedObject = viewer.scene.pick(movement.position);
|
|
|
|
|
|
|
|
|
|
// 获取点击位置的经纬度
|
|
|
|
|
const cartesian = viewer.scene.pickPosition(movement.position);
|
|
|
|
|
if (Cesium.defined(cartesian)) {
|
|
|
|
|
const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
|
|
|
|
|
const longitude = Cesium.Math.toDegrees(cartographic.longitude);
|
|
|
|
|
const latitude = Cesium.Math.toDegrees(cartographic.latitude);
|
|
|
|
|
console.log(`当前点击位置的经纬度: 经度 ${longitude}°, 纬度 ${latitude}°`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Cesium.defined(pickedObject)) {
|
|
|
|
|
const entity = pickedObject.id; // 获取实体对象
|
|
|
|
|
|
|
|
|
|
if (entity && entity.properties) {
|
|
|
|
|
// 从 properties 中读取自定义数据
|
|
|
|
|
const type = entity.properties.modelType.getValue();
|
|
|
|
|
|
|
|
|
|
console.log(`点击了一个实体。类型是: ${type}`);
|
|
|
|
|
if (type === 'shorepower_box' || type === 'ship') {
|
|
|
|
|
console.log(entity.properties.data.getValue())
|
|
|
|
|
// onElectricalBoxClick(entity.properties.data.getValue())
|
|
|
|
|
// 触发自定义点击事件
|
|
|
|
|
props.onInstanceClick({
|
|
|
|
|
type,
|
|
|
|
|
data: entity.properties.data.getValue()
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
// 如果您存储了 data: itemWithModel (一个复杂对象)
|
|
|
|
|
// const fullData = entity.properties.data.getValue();
|
|
|
|
|
|
|
|
|
|
// ... 接着执行您的业务逻辑,如弹出信息窗口等 ...
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
|
|
|
|
|
// 确保启用场景的鼠标事件
|
|
|
|
|
viewer.scene.screenSpaceCameraController.enableInputs = true;
|
|
|
|
|
|
|
|
|
|
// 启用拾取透明物体
|
|
|
|
|
viewer.scene.useDepthPicking = true;
|
|
|
|
|
|
|
|
|
|
console.log('Cesium 高德地图 Viewer 初始化成功');
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Cesium Viewer 初始化失败:', error);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 监听shorePowerList的变化
|
|
|
|
|
watch(
|
|
|
|
|
() => props.shorePowerList,
|
|
|
|
|
(newList, oldList) => {
|
|
|
|
|
console.log('ShorePowerList updated:', newList);
|
|
|
|
|
if (newList && newList.length > 0) {
|
|
|
|
|
// 当shorePowerList更新时,可以在这里添加相应的处理逻辑
|
|
|
|
|
// 例如:更新地图上的岸电箱标记、刷新连接线等
|
|
|
|
|
console.log('处理shorePowerList数据:', newList);
|
|
|
|
|
// 这里可以调用相关的更新函数来处理新数据
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{ deep: true } // 深度监听数组和对象的变化
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// 视角切换方法
|
|
|
|
|
const switchView = (viewName) => {
|
|
|
|
|
const viewParams = presetViews[viewName];
|
|
|
|
|
if (viewParams && viewer) {
|
|
|
|
|
viewer.camera.flyTo({
|
|
|
|
|
...createView(viewParams),
|
|
|
|
|
duration: 2.0 // 动画持续时间(秒)
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 切换到指定模型视图,并允许设置自定义的飞离距离。
|
|
|
|
|
* @param {Cesium.Entity} view - 要飞向的 Entity 实例。
|
|
|
|
|
* @param {number} [extraDistance=15000.0] - 额外的飞离距离 (米)。
|
|
|
|
|
* @param {number} [estimatedRadius=500.0] - 用于计算视角范围的估计模型半径 (米)。
|
|
|
|
|
*/
|
|
|
|
|
const switchModelView = (item, extraDistance = 1000, estimatedRadius = 1000.0) => {
|
|
|
|
|
const clickItem = toRaw(item)
|
|
|
|
|
console.log(item)
|
|
|
|
|
let entityInstance = null
|
|
|
|
|
if (clickItem.type === 'shorepower_box') {
|
|
|
|
|
console.log(clickItem.item)
|
|
|
|
|
const instance = globalModelInstance.filter((item) => item.type === 'shorepower_box').find(twoItem => twoItem.data
|
|
|
|
|
.id === clickItem.item.id)
|
|
|
|
|
console.log(instance)
|
|
|
|
|
if (instance.model) {
|
|
|
|
|
entityInstance = instance.model
|
|
|
|
|
}
|
|
|
|
|
} else if (clickItem.type === 'ship') {
|
|
|
|
|
console.log(clickItem.item)
|
|
|
|
|
console.log(globalModelInstance)
|
|
|
|
|
const instance = globalModelInstance.filter((item) => item.type === 'ship').find(twoItem => twoItem.data.shipBasicInfo
|
|
|
|
|
.id === clickItem.item.shipBasicInfo.id)
|
|
|
|
|
console.log(instance)
|
|
|
|
|
if (instance.model) {
|
|
|
|
|
entityInstance = instance.model
|
|
|
|
|
}
|
|
|
|
|
// 处理船舶点击
|
|
|
|
|
}
|
|
|
|
|
// return
|
|
|
|
|
console.log('目标模型实体:', entityInstance);
|
|
|
|
|
|
|
|
|
|
if (viewer && entityInstance && entityInstance.position) {
|
|
|
|
|
|
|
|
|
|
// 1. 获取模型当前的世界坐标
|
|
|
|
|
// 必须使用 getValue(viewer.clock.currentTime) 来获取当前准确的位置
|
|
|
|
|
const position = entityInstance.position.getValue(viewer.clock.currentTime);
|
|
|
|
|
|
|
|
|
|
if (!position) {
|
|
|
|
|
console.warn("无法获取模型位置,执行默认 flyTo。");
|
|
|
|
|
viewer.flyTo(entityInstance, { duration: 3.0 });
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2. 手动创建边界球 (因为 Entity 实例没有 readyPromise 或 boundingSphere 属性)
|
|
|
|
|
// 使用一个估计的半径来计算视角范围
|
|
|
|
|
const manualBoundingSphere = new Cesium.BoundingSphere(
|
|
|
|
|
position,
|
|
|
|
|
estimatedRadius // 使用估计半径
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// 3. 定义相机相对于边界球的偏移量 (Heading, Pitch, Range)
|
|
|
|
|
const cameraOffset = new Cesium.HeadingPitchRange(
|
|
|
|
|
Cesium.Math.toRadians(0.0), // Heading (航向), 0度朝北
|
|
|
|
|
Cesium.Math.toRadians(-45.0), // Pitch (俯仰), -45度斜向下看
|
|
|
|
|
manualBoundingSphere.radius + extraDistance // Range (距离) = 估计半径 + 额外距离
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// 4. 执行飞行动作
|
|
|
|
|
viewer.camera.flyToBoundingSphere(
|
|
|
|
|
manualBoundingSphere,
|
|
|
|
|
{
|
|
|
|
|
offset: cameraOffset,
|
|
|
|
|
duration: 3.0 // 飞行时间
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
console.log(`飞往模型中心,距离模型中心 ${(manualBoundingSphere.radius + extraDistance) / 1000} 公里。`);
|
|
|
|
|
|
|
|
|
|
} else if (viewer && entityInstance) {
|
|
|
|
|
// 作为一个简单的 fallback 选项
|
|
|
|
|
viewer.flyTo(entityInstance, { duration: 3.0 });
|
|
|
|
|
console.warn("模型位置属性无效,执行默认 flyTo。");
|
|
|
|
|
} else {
|
|
|
|
|
console.error("Cesium Viewer 或模型实例无效。");
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 获取当前视角信息的方法
|
|
|
|
|
const getCurrentViewInfo = () => {
|
|
|
|
|
if (!viewer) {
|
|
|
|
|
console.warn('Viewer 尚未初始化');
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
const camera = viewer.camera;
|
|
|
|
|
|
|
|
|
|
const position = camera.position; // 世界笛卡尔坐标系 x,y,z
|
|
|
|
|
const cartographic = Cesium.Cartographic.fromCartesian(position);
|
|
|
|
|
console.log(
|
|
|
|
|
{
|
|
|
|
|
longitude: Cesium.Math.toDegrees(cartographic.longitude),
|
|
|
|
|
latitude: Cesium.Math.toDegrees(cartographic.latitude),
|
|
|
|
|
height: cartographic.height,
|
|
|
|
|
heading: Cesium.Math.toDegrees(camera.heading),
|
|
|
|
|
pitch: Cesium.Math.toDegrees(camera.pitch),
|
|
|
|
|
roll: Cesium.Math.toDegrees(camera.roll)
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 暴露方法和数据给父组件使用
|
|
|
|
|
defineExpose({
|
|
|
|
|
switchView,
|
|
|
|
|
dataWithModels,
|
|
|
|
|
switchModelView,
|
|
|
|
|
setElectricalBoxClickCallback
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
onBeforeUnmount(() => {
|
|
|
|
|
// 销毁 Viewer,释放 WebGL 资源
|
|
|
|
|
if (viewer) {
|
|
|
|
|
viewer.destroy();
|
|
|
|
|
viewer = null;
|
|
|
|
|
console.log('Cesium Viewer 已销毁');
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
.cesium-container {
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 100vh;
|
|
|
|
|
margin: 0;
|
|
|
|
|
padding: 0;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.btn-group {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 10px;
|
|
|
|
|
z-index: 1000;
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 12rpx;
|
|
|
|
|
}
|
|
|
|
|
</style>
|