|
@@ -1,5 +1,19 @@
|
|
|
<template>
|
|
|
<div id="cesiumContainer" style="height: 100%;width: 100%;"></div>
|
|
|
+
|
|
|
+ <!-- 控制按钮 -->
|
|
|
+ <div class="control-buttons">
|
|
|
+ <button class="control-btn" @click="goToHomeView" title="返回首页视角">
|
|
|
+ <i class="fas fa-home"></i>
|
|
|
+ </button>
|
|
|
+ <button class="control-btn" @click="togglePOIDisplay" title="显示/隐藏POI点">
|
|
|
+ <i class="fas fa-map-marker-alt" :class="{ 'active': poiVisible }"></i>
|
|
|
+ </button>
|
|
|
+ <button class="control-btn" @click="toggleTyphoon" title="显示/隐藏台风并切换视角">
|
|
|
+ <i class="fas fa-wind" :class="{ 'active': typhoonVisible }"></i>
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+
|
|
|
<!-- 自定义弹框组件 -->
|
|
|
<div v-if="selectedPoint" class="custom-popup" :style="{
|
|
|
left: `${popupPosition.x}px`,
|
|
@@ -39,7 +53,7 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import { ref, onMounted, onUnmounted } from 'vue'
|
|
|
+import { ref, onMounted, onUnmounted, watch } from 'vue'
|
|
|
import * as Cesium from 'cesium';
|
|
|
import "cesium/Build/CesiumUnminified/Widgets/widgets.css";
|
|
|
import JYLData from '@/assets/Data/THJYL.json'
|
|
@@ -52,6 +66,11 @@ const popupPosition = ref({ x: 0, y: 0 });
|
|
|
let handler = null;
|
|
|
let viewer = null;
|
|
|
|
|
|
+// 控制按钮相关变量
|
|
|
+const poiVisible = ref(true); // POI点显示状态
|
|
|
+const typhoonVisible = ref(true); // 台风显示状态
|
|
|
+const poiEntities = ref([]); // 存储POI实体,用于控制显示隐藏
|
|
|
+
|
|
|
// 台风相关变量
|
|
|
const fengquanLayers = ref([]);
|
|
|
const myEntityCollection = ref(null);
|
|
@@ -59,15 +78,103 @@ const tbentity = ref(null);
|
|
|
const iii = ref(0);
|
|
|
const currentPointObj = ref(null);
|
|
|
let typhoonInterval = null;
|
|
|
+// 台风相关实体集合,用于控制显示隐藏
|
|
|
+const typhoonRelatedEntities = ref({
|
|
|
+ paths: [],
|
|
|
+ forecasts: [],
|
|
|
+ warnings: []
|
|
|
+});
|
|
|
|
|
|
-// 计算页面缩放比例(与autofit配置一致)
|
|
|
+// 计算页面缩放比例
|
|
|
const getScaleRatio = () => {
|
|
|
const designWidth = 1920;
|
|
|
const designHeight = 1080;
|
|
|
return Math.min(window.innerWidth / designWidth, window.innerHeight / designHeight);
|
|
|
};
|
|
|
|
|
|
+// 返回首页视角
|
|
|
+const goToHomeView = () => {
|
|
|
+ if (viewer) {
|
|
|
+ viewer.camera.flyTo({
|
|
|
+ destination: Cesium.Cartesian3.fromDegrees(120.169103, 31.226174, 500000),
|
|
|
+ orientation: {
|
|
|
+ heading: Cesium.Math.toRadians(0),
|
|
|
+ pitch: Cesium.Math.toRadians(-90),
|
|
|
+ },
|
|
|
+ duration: 1
|
|
|
+ });
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 切换POI点显示/隐藏
|
|
|
+const togglePOIDisplay = () => {
|
|
|
+ poiVisible.value = !poiVisible.value;
|
|
|
+ poiEntities.value.forEach(entity => {
|
|
|
+ if (entity && entity.billboard) {
|
|
|
+ entity.billboard.show = poiVisible.value;
|
|
|
+ entity.label.show = poiVisible.value;
|
|
|
+ }
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+// 切换台风显示/隐藏并跳转视角
|
|
|
+const toggleTyphoon = () => {
|
|
|
+ typhoonVisible.value = !typhoonVisible.value;
|
|
|
+
|
|
|
+ // 控制台风相关实体显示/隐藏
|
|
|
+ if (myEntityCollection.value) {
|
|
|
+ myEntityCollection.value.show = typhoonVisible.value;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 控制风圈显示/隐藏
|
|
|
+ fengquanLayers.value.forEach(entity => {
|
|
|
+ if (entity) {
|
|
|
+ entity.show = typhoonVisible.value;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 控制路径线显示/隐藏
|
|
|
+ typhoonRelatedEntities.value.paths.forEach(entity => {
|
|
|
+ if (entity) {
|
|
|
+ entity.show = typhoonVisible.value;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 控制预报路径显示/隐藏
|
|
|
+ typhoonRelatedEntities.value.forecasts.forEach(entity => {
|
|
|
+ if (entity) {
|
|
|
+ entity.show = typhoonVisible.value;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 控制警戒线显示/隐藏
|
|
|
+ typhoonRelatedEntities.value.warnings.forEach(entity => {
|
|
|
+ if (entity) {
|
|
|
+ entity.show = typhoonVisible.value;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 控制台风标记显示/隐藏
|
|
|
+ if (tbentity.value) {
|
|
|
+ tbentity.value.show = typhoonVisible.value;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果显示台风,则跳转到台风视角
|
|
|
+ if (typhoonVisible.value) {
|
|
|
+ viewer.camera.flyTo({
|
|
|
+ destination: Cesium.Cartesian3.fromDegrees(120, 20, 4025692.0),
|
|
|
+ });
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
onMounted(async () => {
|
|
|
+ // 设置HTML和body样式确保没有边距
|
|
|
+ document.documentElement.style.height = '100%';
|
|
|
+ document.body.style.height = '100%';
|
|
|
+ document.body.style.margin = '0';
|
|
|
+ document.body.style.padding = '0';
|
|
|
+
|
|
|
// 初始化Cesium viewer
|
|
|
viewer = new Cesium.Viewer('cesiumContainer', {
|
|
|
timeline: false,
|
|
@@ -81,47 +188,43 @@ onMounted(async () => {
|
|
|
vrButton: false,
|
|
|
selectionIndicator: false,
|
|
|
infoBox: false,
|
|
|
- //加载地形效果
|
|
|
+ // 加载地形效果
|
|
|
terrainProvider: await Cesium.createWorldTerrainAsync({
|
|
|
requestVertexNormals: true,
|
|
|
- // requestWaterMask: true,
|
|
|
})
|
|
|
});
|
|
|
|
|
|
- // 多块倾斜摄影瓦片集的URL列表(根据实际路径修改)
|
|
|
+ // 多块倾斜摄影瓦片集的URL列表
|
|
|
const tilesetUrls = [
|
|
|
"http://localhost:9003/model/TSQ1234/tileset.json",
|
|
|
"http://localhost:9003/model/SY123/tileset.json",
|
|
|
];
|
|
|
|
|
|
- // 存储加载成功的瓦片集(可选,用于后续管理)
|
|
|
+ // 存储加载成功的瓦片集
|
|
|
const loadedTilesets = [];
|
|
|
|
|
|
- // 批量加载瓦片集(不调整视角)
|
|
|
+ // 批量加载瓦片集
|
|
|
async function loadMultipleTilesets() {
|
|
|
try {
|
|
|
- // 遍历URL列表,并行加载所有瓦片集
|
|
|
const promises = tilesetUrls.map(async (url, index) => {
|
|
|
try {
|
|
|
const tileset = await Cesium.Cesium3DTileset.fromUrl(url, {
|
|
|
- maximumScreenSpaceError: 32, // 细节层级控制
|
|
|
+ maximumScreenSpaceError: 32,
|
|
|
dynamicScreenSpaceError: true,
|
|
|
skipLevelOfDetail: true,
|
|
|
maximumConcurrentRequests: 5,
|
|
|
tileCacheSize: 100
|
|
|
});
|
|
|
- // 添加到场景
|
|
|
viewer.scene.primitives.add(tileset);
|
|
|
loadedTilesets.push(tileset);
|
|
|
console.log(`第${index + 1}块瓦片集加载完成`);
|
|
|
return tileset;
|
|
|
} catch (error) {
|
|
|
console.error(`第${index + 1}块瓦片集加载失败:`, error);
|
|
|
- return null; // 单块失败不影响其他块
|
|
|
+ return null;
|
|
|
}
|
|
|
});
|
|
|
|
|
|
- // 等待所有瓦片集加载(无论成功与否)
|
|
|
await Promise.all(promises);
|
|
|
console.log("所有瓦片集加载操作已完成");
|
|
|
} catch (error) {
|
|
@@ -129,7 +232,7 @@ onMounted(async () => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 执行加载(加载后保持原有视口)
|
|
|
+ // 执行加载
|
|
|
loadMultipleTilesets();
|
|
|
|
|
|
// 定义距离显示条件和缩放属性
|
|
@@ -142,7 +245,7 @@ onMounted(async () => {
|
|
|
JYLData.forEach((item) => {
|
|
|
const position = Cesium.Cartesian3.fromDegrees(
|
|
|
parseFloat(item.LGTD),
|
|
|
- parseFloat(item.LTTD)
|
|
|
+ parseFloat(item.LTTD)
|
|
|
);
|
|
|
const entity = viewer.entities.add({
|
|
|
position: position,
|
|
@@ -179,9 +282,10 @@ onMounted(async () => {
|
|
|
}
|
|
|
});
|
|
|
entityDataMap.set(entity.id, item);
|
|
|
+ poiEntities.value.push(entity);
|
|
|
});
|
|
|
|
|
|
- // 天地图图层
|
|
|
+ // 添加天地图图层
|
|
|
const tdtLayer = new Cesium.WebMapTileServiceImageryProvider({
|
|
|
url: `https://t0.tianditu.com/img_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={TileMatrix}&TILEROW={TileRow}&TILECOL={TileCol}&tk=${TDTTK}`,
|
|
|
layer: "tdt",
|
|
@@ -228,7 +332,7 @@ onMounted(async () => {
|
|
|
|
|
|
if (data) {
|
|
|
selectedPoint.value = data;
|
|
|
- // 计算图标屏幕坐标,固定显示在图标上方
|
|
|
+ // 计算图标屏幕坐标
|
|
|
const entityPosition = viewer.scene.cartesianToCanvasCoordinates(pickedObject.id.position._value);
|
|
|
if (entityPosition) {
|
|
|
popupPosition.value = {
|
|
@@ -247,6 +351,11 @@ onMounted(async () => {
|
|
|
|
|
|
// 初始化台风相关功能
|
|
|
initTyphoonVisualization();
|
|
|
+
|
|
|
+ // 监听窗口大小变化,确保地图铺满
|
|
|
+ window.addEventListener('resize', handleResize);
|
|
|
+ // 初始触发一次 resize 确保地图正确显示
|
|
|
+ handleResize();
|
|
|
});
|
|
|
|
|
|
// 初始化台风可视化
|
|
@@ -348,7 +457,7 @@ const processPoints = (points) => {
|
|
|
});
|
|
|
|
|
|
// 添加台风路径线
|
|
|
- viewer.entities.add({
|
|
|
+ const pathEntity = viewer.entities.add({
|
|
|
polyline: {
|
|
|
positions: Cesium.Cartesian3.fromDegreesArray(lineArr),
|
|
|
width: 3,
|
|
@@ -356,6 +465,7 @@ const processPoints = (points) => {
|
|
|
material: Cesium.Color.RED
|
|
|
}
|
|
|
});
|
|
|
+ typhoonRelatedEntities.value.paths.push(pathEntity);
|
|
|
|
|
|
if (points.length > 0) {
|
|
|
// 初始化预报路径
|
|
@@ -395,7 +505,7 @@ const initForeast = (data) => {
|
|
|
});
|
|
|
|
|
|
// 添加预报路径线
|
|
|
- viewer.entities.add({
|
|
|
+ const forecastEntity = viewer.entities.add({
|
|
|
polyline: {
|
|
|
positions: Cesium.Cartesian3.fromDegreesArray(lineArr),
|
|
|
width: 2,
|
|
@@ -405,13 +515,14 @@ const initForeast = (data) => {
|
|
|
})
|
|
|
}
|
|
|
});
|
|
|
+ typhoonRelatedEntities.value.forecasts.push(forecastEntity);
|
|
|
});
|
|
|
};
|
|
|
|
|
|
// 初始化警戒线
|
|
|
const initJJ = () => {
|
|
|
// 24小时警戒线
|
|
|
- viewer.entities.add({
|
|
|
+ const line24h = viewer.entities.add({
|
|
|
name: '24小时警戒线',
|
|
|
polyline: {
|
|
|
positions: Cesium.Cartesian3.fromDegreesArray([
|
|
@@ -422,9 +533,10 @@ const initJJ = () => {
|
|
|
clampToGround: true
|
|
|
}
|
|
|
});
|
|
|
+ typhoonRelatedEntities.value.warnings.push(line24h);
|
|
|
|
|
|
// 48小时警戒线
|
|
|
- viewer.entities.add({
|
|
|
+ const line48h = viewer.entities.add({
|
|
|
name: '48小时警戒线',
|
|
|
polyline: {
|
|
|
positions: Cesium.Cartesian3.fromDegreesArray([
|
|
@@ -435,9 +547,10 @@ const initJJ = () => {
|
|
|
clampToGround: true
|
|
|
}
|
|
|
});
|
|
|
+ typhoonRelatedEntities.value.warnings.push(line48h);
|
|
|
|
|
|
// 警戒线标签
|
|
|
- viewer.entities.add({
|
|
|
+ const label24h = viewer.entities.add({
|
|
|
position: Cesium.Cartesian3.fromDegrees(126.129019, 29.104287),
|
|
|
label: {
|
|
|
fillColor: Cesium.Color.RED,
|
|
@@ -445,8 +558,9 @@ const initJJ = () => {
|
|
|
font: '14pt monospace'
|
|
|
}
|
|
|
});
|
|
|
+ typhoonRelatedEntities.value.warnings.push(label24h);
|
|
|
|
|
|
- viewer.entities.add({
|
|
|
+ const label48h = viewer.entities.add({
|
|
|
position: Cesium.Cartesian3.fromDegrees(132, 20),
|
|
|
label: {
|
|
|
fillColor: Cesium.Color.YELLOW,
|
|
@@ -454,6 +568,7 @@ const initJJ = () => {
|
|
|
font: '14pt monospace'
|
|
|
}
|
|
|
});
|
|
|
+ typhoonRelatedEntities.value.warnings.push(label48h);
|
|
|
};
|
|
|
|
|
|
// 添加台风动画
|
|
@@ -506,7 +621,10 @@ const adds = (data) => {
|
|
|
|
|
|
iii.value = (iii.value + 1) % data.length;
|
|
|
removeTFLayer();
|
|
|
- addTyphoonCircle();
|
|
|
+ // 只有当台风可见时才添加风圈
|
|
|
+ if (typhoonVisible.value) {
|
|
|
+ addTyphoonCircle();
|
|
|
+ }
|
|
|
}, 200);
|
|
|
};
|
|
|
|
|
@@ -572,7 +690,7 @@ const removeTFLayer = () => {
|
|
|
|
|
|
// 添加台风风圈
|
|
|
const addTyphoonCircle = () => {
|
|
|
- if (!currentPointObj.value) return;
|
|
|
+ if (!currentPointObj.value || !typhoonVisible.value) return;
|
|
|
|
|
|
const circles = ['circle7', 'circle10', 'circle12'];
|
|
|
circles.forEach(item => {
|
|
@@ -653,6 +771,9 @@ onUnmounted(() => {
|
|
|
clearInterval(typhoonInterval);
|
|
|
}
|
|
|
|
|
|
+ // 移除窗口大小变化监听
|
|
|
+ window.removeEventListener('resize', handleResize);
|
|
|
+
|
|
|
// 销毁事件处理器
|
|
|
if (handler) {
|
|
|
handler.destroy();
|
|
@@ -667,6 +788,43 @@ onUnmounted(() => {
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
+
|
|
|
+/* 控制按钮样式 */
|
|
|
+.control-buttons {
|
|
|
+ position: absolute;
|
|
|
+ top: 200px;
|
|
|
+ left: 150px;
|
|
|
+ z-index: 100;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.control-btn {
|
|
|
+ width: 40px;
|
|
|
+ height: 40px;
|
|
|
+ border-radius: 50%;
|
|
|
+ background-color: rgba(255, 255, 255, 0.9);
|
|
|
+ border: none;
|
|
|
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: all 0.2s ease;
|
|
|
+ color: #333;
|
|
|
+}
|
|
|
+
|
|
|
+.control-btn:hover {
|
|
|
+ background-color: white;
|
|
|
+ transform: scale(1.1);
|
|
|
+}
|
|
|
+
|
|
|
+.control-btn .active {
|
|
|
+ color: #1E88E5;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
.custom-popup {
|
|
|
position: absolute;
|
|
|
z-index: 1000;
|