|
@@ -1,13 +1,10 @@
|
|
|
<template>
|
|
<template>
|
|
|
- <div id="cesiumContainer" class="cesium-ignore-autofit"
|
|
|
|
|
- style="width: 100%; height: 100%; position: relative; z-index: 1; transform: none !important; zoom: 1 !important;">
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
|
|
+ <div id="cesiumContainer" class="cesium-ignore-autofit" style="width: 100%; height: 100%; position: relative; z-index: 1; transform: none !important; zoom: 1 !important;"></div>
|
|
|
|
|
+
|
|
|
<!-- POI详情弹窗 -->
|
|
<!-- POI详情弹窗 -->
|
|
|
<div v-if="showPopup" class="poi-popup" :style="popupStyle">
|
|
<div v-if="showPopup" class="poi-popup" :style="popupStyle">
|
|
|
<div class="popup-header">
|
|
<div class="popup-header">
|
|
|
- <span class="popup-title">{{ poiData.name }} <a href="#" class="popup-detail"
|
|
|
|
|
- @click.prevent="goToTwinStation">详情</a></span>
|
|
|
|
|
|
|
+ <span class="popup-title">{{ poiData.name }} <a href="#" class="popup-detail" @click.prevent="goToTwinStation">详情</a></span>
|
|
|
<button class="popup-close" @click="closePopup">×</button>
|
|
<button class="popup-close" @click="closePopup">×</button>
|
|
|
</div>
|
|
</div>
|
|
|
<div class="popup-content">
|
|
<div class="popup-content">
|
|
@@ -26,335 +23,404 @@
|
|
|
</div>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
-<script>
|
|
|
|
|
|
|
+<script setup>
|
|
|
|
|
+import { ref, onMounted, onUnmounted, computed, watch } from 'vue'
|
|
|
import * as Cesium from 'cesium'
|
|
import * as Cesium from 'cesium'
|
|
|
import 'cesium/Build/CesiumUnminified/Widgets/widgets.css'
|
|
import 'cesium/Build/CesiumUnminified/Widgets/widgets.css'
|
|
|
|
|
+import { useRouter } from 'vue-router'
|
|
|
|
|
|
|
|
-export default {
|
|
|
|
|
- name: 'CesiumMap',
|
|
|
|
|
- props: {
|
|
|
|
|
- simulationTime: {
|
|
|
|
|
- type: Number,
|
|
|
|
|
- default: 0
|
|
|
|
|
- },
|
|
|
|
|
- simulationData: {
|
|
|
|
|
- type: Object,
|
|
|
|
|
- default: () => ({})
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- data() {
|
|
|
|
|
- return {
|
|
|
|
|
- viewer: null,
|
|
|
|
|
- showPopup: false,
|
|
|
|
|
- popupPosition: { x: 0, y: 0 },
|
|
|
|
|
- poiData: {
|
|
|
|
|
- name: '黑林水文站',
|
|
|
|
|
- waterLevel: '3.25',
|
|
|
|
|
- flow: '12.5',
|
|
|
|
|
- rainfall: '0.5',
|
|
|
|
|
- historyWaterLevel: '3.10',
|
|
|
|
|
- historyFlow: '11.8',
|
|
|
|
|
- device: 'HL-Sensor-001'
|
|
|
|
|
- },
|
|
|
|
|
- blinkInterval: null,
|
|
|
|
|
- heilinEntity: null,
|
|
|
|
|
- reservoirEntity: null
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- computed: {
|
|
|
|
|
- popupStyle() {
|
|
|
|
|
- return {
|
|
|
|
|
- left: this.popupPosition.x + 'px',
|
|
|
|
|
- top: this.popupPosition.y + 'px'
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- watch: {
|
|
|
|
|
- simulationData: {
|
|
|
|
|
- handler(newData) {
|
|
|
|
|
- this.$nextTick(() => {
|
|
|
|
|
- this.updateMapMarkers(newData)
|
|
|
|
|
- })
|
|
|
|
|
- },
|
|
|
|
|
- deep: true,
|
|
|
|
|
- immediate: true
|
|
|
|
|
- }
|
|
|
|
|
|
|
+const emit = defineEmits(['toggleMap'])
|
|
|
|
|
+
|
|
|
|
|
+const viewer = ref(null)
|
|
|
|
|
+const showPopup = ref(false)
|
|
|
|
|
+const popupPosition = ref({ x: 0, y: 0 })
|
|
|
|
|
+
|
|
|
|
|
+const props = defineProps({
|
|
|
|
|
+ simulationTime: {
|
|
|
|
|
+ type: Number,
|
|
|
|
|
+ default: 0
|
|
|
},
|
|
},
|
|
|
- methods: {
|
|
|
|
|
- addPoiMarker() {
|
|
|
|
|
- const heilinPosition = Cesium.Cartesian3.fromDegrees(118.8770798, 35.0320627)
|
|
|
|
|
-
|
|
|
|
|
- const clickEntity = this.viewer.entities.add({
|
|
|
|
|
- id: 'heilin-station-click',
|
|
|
|
|
- position: heilinPosition,
|
|
|
|
|
- point: {
|
|
|
|
|
- pixelSize: 30,
|
|
|
|
|
- color: Cesium.Color.TRANSPARENT,
|
|
|
|
|
- outlineColor: Cesium.Color.TRANSPARENT,
|
|
|
|
|
- outlineWidth: 0
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
|
|
+ simulationData: {
|
|
|
|
|
+ type: Object,
|
|
|
|
|
+ default: () => ({})
|
|
|
|
|
+ }
|
|
|
|
|
+})
|
|
|
|
|
|
|
|
- this.heilinEntity = this.viewer.entities.add({
|
|
|
|
|
- id: 'heilin-station',
|
|
|
|
|
- position: heilinPosition,
|
|
|
|
|
- billboard: {
|
|
|
|
|
- image: '/src/assets/images/水文站.png',
|
|
|
|
|
- verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
|
|
|
|
- horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
|
|
|
|
|
- pixelOffset: new Cesium.Cartesian2(0, -15),
|
|
|
|
|
- scale: 0.8,
|
|
|
|
|
- width: 30,
|
|
|
|
|
- height: 37.5,
|
|
|
|
|
- disableDepthTestDistance: Number.POSITIVE_INFINITY
|
|
|
|
|
- },
|
|
|
|
|
- label: {
|
|
|
|
|
- text: `水位 ${this.simulationData.heilinStation?.waterLevel || '3.25'}m\n流量 ${this.simulationData.heilinStation?.flow || '12.5'}m³/s\n降雨量 ${this.simulationData.heilinStation?.rainfall || '0.5'}mm/h`,
|
|
|
|
|
- font: 'bold 14px 黑体',
|
|
|
|
|
- fillColor: Cesium.Color.WHITE,
|
|
|
|
|
- outlineColor: Cesium.Color.fromCssColorString('#003860'),
|
|
|
|
|
- outlineWidth: 2,
|
|
|
|
|
- style: Cesium.LabelStyle.FILL_AND_OUTLINE,
|
|
|
|
|
- verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
|
|
|
|
- horizontalOrigin: Cesium.HorizontalOrigin.LEFT,
|
|
|
|
|
- pixelOffset: new Cesium.Cartesian2(0, -30),
|
|
|
|
|
- showBackground: true,
|
|
|
|
|
- backgroundColor: new Cesium.Color(0, 0.3, 0.5, 0.7),
|
|
|
|
|
- backgroundPadding: new Cesium.Cartesian2(8, 4),
|
|
|
|
|
- disableDepthTestDistance: Number.POSITIVE_INFINITY
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
|
|
+const router = useRouter()
|
|
|
|
|
|
|
|
- this.viewer.entities.add({
|
|
|
|
|
- id: 'heilin-station-title',
|
|
|
|
|
- position: heilinPosition,
|
|
|
|
|
- label: {
|
|
|
|
|
- text: '黑林水文站',
|
|
|
|
|
- font: 'bold 14px sans-serif',
|
|
|
|
|
- fillColor: Cesium.Color.WHITE,
|
|
|
|
|
- outlineColor: Cesium.Color.fromCssColorString('#003860'),
|
|
|
|
|
- outlineWidth: 2,
|
|
|
|
|
- style: Cesium.LabelStyle.FILL_AND_OUTLINE,
|
|
|
|
|
- verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
|
|
|
|
- horizontalOrigin: Cesium.HorizontalOrigin.LEFT,
|
|
|
|
|
- pixelOffset: new Cesium.Cartesian2(0, -85),
|
|
|
|
|
- showBackground: true,
|
|
|
|
|
- backgroundColor: new Cesium.Color(0, 0.2, 0.4, 0.9),
|
|
|
|
|
- backgroundPadding: new Cesium.Cartesian2(22, 4),
|
|
|
|
|
- disableDepthTestDistance: Number.POSITIVE_INFINITY
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
|
|
+const DESIGN_WIDTH = 1920
|
|
|
|
|
+const DESIGN_HEIGHT = 1080
|
|
|
|
|
|
|
|
- const reservoirPosition = Cesium.Cartesian3.fromDegrees(118.9540555, 34.9355329)
|
|
|
|
|
-
|
|
|
|
|
- this.reservoirEntity = this.viewer.entities.add({
|
|
|
|
|
- id: 'tashan-reservoir',
|
|
|
|
|
- position: reservoirPosition,
|
|
|
|
|
- billboard: {
|
|
|
|
|
- image: '/src/assets/images/水库蓝.png',
|
|
|
|
|
- verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
|
|
|
|
- horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
|
|
|
|
|
- pixelOffset: new Cesium.Cartesian2(0, -15),
|
|
|
|
|
- scale: 0.8,
|
|
|
|
|
- width: 30,
|
|
|
|
|
- height: 30,
|
|
|
|
|
- disableDepthTestDistance: Number.POSITIVE_INFINITY
|
|
|
|
|
- },
|
|
|
|
|
- label: {
|
|
|
|
|
- text: `库水位 ${this.simulationData.reservoir?.waterLevel || '18.5'}m\n库容 ${this.simulationData.reservoir?.storage || '2350.8'}万m³\n汛限 20.0m`,
|
|
|
|
|
- font: 'bold 14px 黑体',
|
|
|
|
|
- fillColor: Cesium.Color.WHITE,
|
|
|
|
|
- outlineColor: Cesium.Color.fromCssColorString('#003860'),
|
|
|
|
|
- outlineWidth: 2,
|
|
|
|
|
- style: Cesium.LabelStyle.FILL_AND_OUTLINE,
|
|
|
|
|
- verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
|
|
|
|
- horizontalOrigin: Cesium.HorizontalOrigin.LEFT,
|
|
|
|
|
- pixelOffset: new Cesium.Cartesian2(0, -30),
|
|
|
|
|
- showBackground: true,
|
|
|
|
|
- backgroundColor: new Cesium.Color(0, 0.3, 0.5, 0.7),
|
|
|
|
|
- backgroundPadding: new Cesium.Cartesian2(8, 4),
|
|
|
|
|
- disableDepthTestDistance: Number.POSITIVE_INFINITY
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
|
|
+const getScaleRatio = () => {
|
|
|
|
|
+ return Math.min(window.innerWidth / DESIGN_WIDTH, window.innerHeight / DESIGN_HEIGHT)
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
- this.viewer.entities.add({
|
|
|
|
|
- id: 'tashan-reservoir-title',
|
|
|
|
|
- position: reservoirPosition,
|
|
|
|
|
- label: {
|
|
|
|
|
- text: '小塔山水库',
|
|
|
|
|
- font: 'bold 14px sans-serif',
|
|
|
|
|
- fillColor: Cesium.Color.WHITE,
|
|
|
|
|
- outlineColor: Cesium.Color.fromCssColorString('#003860'),
|
|
|
|
|
- outlineWidth: 2,
|
|
|
|
|
- style: Cesium.LabelStyle.FILL_AND_OUTLINE,
|
|
|
|
|
- verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
|
|
|
|
- horizontalOrigin: Cesium.HorizontalOrigin.LEFT,
|
|
|
|
|
- pixelOffset: new Cesium.Cartesian2(0, -85),
|
|
|
|
|
- showBackground: true,
|
|
|
|
|
- backgroundColor: new Cesium.Color(0, 0.2, 0.4, 0.9),
|
|
|
|
|
- backgroundPadding: new Cesium.Cartesian2(22, 4),
|
|
|
|
|
- disableDepthTestDistance: Number.POSITIVE_INFINITY
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
|
|
+const poiData = ref({
|
|
|
|
|
+ name: '黑林水文站',
|
|
|
|
|
+ waterLevel: '3.25',
|
|
|
|
|
+ flow: '12.5',
|
|
|
|
|
+ rainfall: '0.5',
|
|
|
|
|
+ historyWaterLevel: '3.10',
|
|
|
|
|
+ historyFlow: '11.8',
|
|
|
|
|
+ device: 'HL-Sensor-001'
|
|
|
|
|
+})
|
|
|
|
|
|
|
|
- const handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas)
|
|
|
|
|
|
|
+const popupStyle = computed(() => {
|
|
|
|
|
+ return {
|
|
|
|
|
+ left: popupPosition.value.x + 'px',
|
|
|
|
|
+ top: popupPosition.value.y + 'px'
|
|
|
|
|
+ }
|
|
|
|
|
+})
|
|
|
|
|
|
|
|
- handler.setInputAction((movement) => {
|
|
|
|
|
- const pickedObjects = this.viewer.scene.drillPick(movement.position)
|
|
|
|
|
- for (let i = 0; i < pickedObjects.length; i++) {
|
|
|
|
|
- const pickedObj = pickedObjects[i]
|
|
|
|
|
- if (Cesium.defined(pickedObj) && pickedObj.id) {
|
|
|
|
|
- if (pickedObj.id.id === 'heilin-station' || pickedObj.id.id === 'heilin-station-click') {
|
|
|
|
|
- this.showPopup = true
|
|
|
|
|
- this.popupPosition = { x: movement.position.x - 120, y: movement.position.y - 150 }
|
|
|
|
|
|
|
+let blinkInterval = null
|
|
|
|
|
+
|
|
|
|
|
+const addPoiMarker = () => {
|
|
|
|
|
+ // 添加黑林水文站POI
|
|
|
|
|
+ const heilinPosition = Cesium.Cartesian3.fromDegrees(118.8770798, 35.0320627)
|
|
|
|
|
+
|
|
|
|
|
+ // 创建一个不可见的点实体,用于扩大点击区域
|
|
|
|
|
+ const clickEntity = viewer.value.entities.add({
|
|
|
|
|
+ id: 'heilin-station-click',
|
|
|
|
|
+ position: heilinPosition,
|
|
|
|
|
+ point: {
|
|
|
|
|
+ pixelSize: 30,
|
|
|
|
|
+ color: Cesium.Color.TRANSPARENT,
|
|
|
|
|
+ outlineColor: Cesium.Color.TRANSPARENT,
|
|
|
|
|
+ outlineWidth: 0
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ const entity = viewer.value.entities.add({
|
|
|
|
|
+ id: 'heilin-station',
|
|
|
|
|
+ position: heilinPosition,
|
|
|
|
|
+ billboard: {
|
|
|
|
|
+ image: '/src/assets/images/水文站.png',
|
|
|
|
|
+ verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
|
|
|
|
+ horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
|
|
|
|
|
+ pixelOffset: new Cesium.Cartesian2(0, -15),
|
|
|
|
|
+ scale: 0.8,
|
|
|
|
|
+ width: 30,
|
|
|
|
|
+ height: 37.5,
|
|
|
|
|
+ disableDepthTestDistance: Number.POSITIVE_INFINITY
|
|
|
|
|
+ },
|
|
|
|
|
+ label: {
|
|
|
|
|
+ text: '水位 3.25m\n流量 12.5m³/s\n降雨量 0.5mm/h',
|
|
|
|
|
+ font: 'bold 14px 黑体',
|
|
|
|
|
+ fillColor: Cesium.Color.WHITE,
|
|
|
|
|
+ outlineColor: Cesium.Color.fromCssColorString('#003860'),
|
|
|
|
|
+ outlineWidth: 2,
|
|
|
|
|
+ style: Cesium.LabelStyle.FILL_AND_OUTLINE,
|
|
|
|
|
+ verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
|
|
|
|
+ horizontalOrigin: Cesium.HorizontalOrigin.LEFT,
|
|
|
|
|
+ pixelOffset: new Cesium.Cartesian2(0, -30),
|
|
|
|
|
+ showBackground: true,
|
|
|
|
|
+ backgroundColor: new Cesium.Color(0, 0.3, 0.5, 0.7),
|
|
|
|
|
+ backgroundPadding: new Cesium.Cartesian2(8, 4),
|
|
|
|
|
+ disableDepthTestDistance: Number.POSITIVE_INFINITY
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ // 添加标题标签
|
|
|
|
|
+ viewer.value.entities.add({
|
|
|
|
|
+ id: 'heilin-station-title',
|
|
|
|
|
+ position: heilinPosition,
|
|
|
|
|
+ label: {
|
|
|
|
|
+ text: '黑林水文站',
|
|
|
|
|
+ font: 'bold 14px sans-serif',
|
|
|
|
|
+ fillColor: Cesium.Color.WHITE,
|
|
|
|
|
+ outlineColor: Cesium.Color.fromCssColorString('#003860'),
|
|
|
|
|
+ outlineWidth: 2,
|
|
|
|
|
+ style: Cesium.LabelStyle.FILL_AND_OUTLINE,
|
|
|
|
|
+ verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
|
|
|
|
+ horizontalOrigin: Cesium.HorizontalOrigin.LEFT,
|
|
|
|
|
+ pixelOffset: new Cesium.Cartesian2(0, -85),
|
|
|
|
|
+ showBackground: true,
|
|
|
|
|
+ backgroundColor: new Cesium.Color(0, 0.2, 0.4, 0.9),
|
|
|
|
|
+ backgroundPadding: new Cesium.Cartesian2(22, 4),
|
|
|
|
|
+ disableDepthTestDistance: Number.POSITIVE_INFINITY
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
|
|
|
- break
|
|
|
|
|
|
|
+ // 添加塔山小水库POI
|
|
|
|
|
+ const reservoirPosition = Cesium.Cartesian3.fromDegrees(118.9540555, 34.9355329)
|
|
|
|
|
+
|
|
|
|
|
+ const reservoirEntity = viewer.value.entities.add({
|
|
|
|
|
+ id: 'tashan-reservoir',
|
|
|
|
|
+ position: reservoirPosition,
|
|
|
|
|
+ billboard: {
|
|
|
|
|
+ image: '/src/assets/images/水库蓝.png',
|
|
|
|
|
+ verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
|
|
|
|
+ horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
|
|
|
|
|
+ pixelOffset: new Cesium.Cartesian2(0, -15),
|
|
|
|
|
+ scale: 0.8,
|
|
|
|
|
+ width: 30,
|
|
|
|
|
+ height: 30,
|
|
|
|
|
+ disableDepthTestDistance: Number.POSITIVE_INFINITY
|
|
|
|
|
+ },
|
|
|
|
|
+ label: {
|
|
|
|
|
+ text: '库水位 18.5m\n库容 2350.8万m³\n汛限 20.0m',
|
|
|
|
|
+ font: 'bold 14px 黑体',
|
|
|
|
|
+ fillColor: Cesium.Color.WHITE,
|
|
|
|
|
+ outlineColor: Cesium.Color.fromCssColorString('#003860'),
|
|
|
|
|
+ outlineWidth: 2,
|
|
|
|
|
+ style: Cesium.LabelStyle.FILL_AND_OUTLINE,
|
|
|
|
|
+ verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
|
|
|
|
+ horizontalOrigin: Cesium.HorizontalOrigin.LEFT,
|
|
|
|
|
+ pixelOffset: new Cesium.Cartesian2(0, -30),
|
|
|
|
|
+ showBackground: true,
|
|
|
|
|
+ backgroundColor: new Cesium.Color(0, 0.3, 0.5, 0.7),
|
|
|
|
|
+ backgroundPadding: new Cesium.Cartesian2(8, 4),
|
|
|
|
|
+ disableDepthTestDistance: Number.POSITIVE_INFINITY
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ // 添加标题标签
|
|
|
|
|
+ viewer.value.entities.add({
|
|
|
|
|
+ id: 'tashan-reservoir-title',
|
|
|
|
|
+ position: reservoirPosition,
|
|
|
|
|
+ label: {
|
|
|
|
|
+ text: '小塔山水库',
|
|
|
|
|
+ font: 'bold 14px sans-serif',
|
|
|
|
|
+ fillColor: Cesium.Color.WHITE,
|
|
|
|
|
+ outlineColor: Cesium.Color.fromCssColorString('#003860'),
|
|
|
|
|
+ outlineWidth: 2,
|
|
|
|
|
+ style: Cesium.LabelStyle.FILL_AND_OUTLINE,
|
|
|
|
|
+ verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
|
|
|
|
+ horizontalOrigin: Cesium.HorizontalOrigin.LEFT,
|
|
|
|
|
+ pixelOffset: new Cesium.Cartesian2(0, -85),
|
|
|
|
|
+ showBackground: true,
|
|
|
|
|
+ backgroundColor: new Cesium.Color(0, 0.2, 0.4, 0.9),
|
|
|
|
|
+ backgroundPadding: new Cesium.Cartesian2(22, 4),
|
|
|
|
|
+ disableDepthTestDistance: Number.POSITIVE_INFINITY
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ const handler = new Cesium.ScreenSpaceEventHandler(viewer.value.scene.canvas)
|
|
|
|
|
+
|
|
|
|
|
+ handler.setInputAction((movement) => {
|
|
|
|
|
+ const scaleRatio = getScaleRatio()
|
|
|
|
|
+ const correctedX = movement.position.x / scaleRatio
|
|
|
|
|
+ const correctedY = movement.position.y / scaleRatio
|
|
|
|
|
+
|
|
|
|
|
+ const pickedObjects = viewer.value.scene.drillPick(new Cesium.Cartesian2(correctedX, correctedY))
|
|
|
|
|
+ for (let i = 0; i < pickedObjects.length; i++) {
|
|
|
|
|
+ const pickedObj = pickedObjects[i]
|
|
|
|
|
+ if (Cesium.defined(pickedObj) && pickedObj.id) {
|
|
|
|
|
+ if (pickedObj.id.id === 'heilin-station' || pickedObj.id.id === 'heilin-station-click') {
|
|
|
|
|
+ showPopup.value = true
|
|
|
|
|
+ popupPosition.value = {
|
|
|
|
|
+ x: movement.position.x - 120,
|
|
|
|
|
+ y: movement.position.y - 150
|
|
|
}
|
|
}
|
|
|
|
|
+ break
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- }, Cesium.ScreenSpaceEventType.LEFT_CLICK)
|
|
|
|
|
- },
|
|
|
|
|
- updateMapMarkers(data) {
|
|
|
|
|
- if (!data) return
|
|
|
|
|
-
|
|
|
|
|
- if (this.heilinEntity && data.heilinStation) {
|
|
|
|
|
- const waterLevel = data.heilinStation.waterLevel || '3.25'
|
|
|
|
|
- const flow = data.heilinStation.flow || '12.5'
|
|
|
|
|
- const rainfall = data.heilinStation.rainfall || '0.5'
|
|
|
|
|
-
|
|
|
|
|
- this.heilinEntity.label.text = `水位 ${waterLevel}m\n流量 ${flow}m³/s\n降雨量 ${rainfall}mm/h`
|
|
|
|
|
- this.poiData.waterLevel = waterLevel
|
|
|
|
|
- this.poiData.flow = flow
|
|
|
|
|
- this.poiData.rainfall = rainfall
|
|
|
|
|
}
|
|
}
|
|
|
|
|
+ }, Cesium.ScreenSpaceEventType.LEFT_CLICK)
|
|
|
|
|
+
|
|
|
|
|
+ // 移除闪烁效果
|
|
|
|
|
+ entity.billboard.scale = 0.8
|
|
|
|
|
+ reservoirEntity.billboard.scale = 0.8
|
|
|
|
|
+
|
|
|
|
|
+ // 立即更新一次标记数据
|
|
|
|
|
+ updateMapMarkers()
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- if (this.reservoirEntity && data.reservoir) {
|
|
|
|
|
- const waterLevel = data.reservoir.waterLevel || '18.5'
|
|
|
|
|
- const storage = data.reservoir.storage || '2350.8'
|
|
|
|
|
-
|
|
|
|
|
- this.reservoirEntity.label.text = `库水位 ${waterLevel}m\n库容 ${storage}万m³\n汛限 20.0m`
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- closePopup() {
|
|
|
|
|
- this.showPopup = false
|
|
|
|
|
- },
|
|
|
|
|
- goToTwinStation() {
|
|
|
|
|
- const heilinPosition = Cesium.Cartesian3.fromDegrees(118.8770798, 35.0320627, 10000)
|
|
|
|
|
- this.viewer.camera.flyTo({
|
|
|
|
|
- destination: heilinPosition,
|
|
|
|
|
- duration: 2.0,
|
|
|
|
|
- easingFunction: Cesium.EasingFunction.SINE_IN_OUT
|
|
|
|
|
- })
|
|
|
|
|
- this.closePopup()
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- async mounted() {
|
|
|
|
|
- this.viewer = new Cesium.Viewer('cesiumContainer', {
|
|
|
|
|
- terrainProvider: await Cesium.createWorldTerrainAsync(),
|
|
|
|
|
- animation: false,
|
|
|
|
|
- baseLayerPicker: false,
|
|
|
|
|
- fullscreenButton: false,
|
|
|
|
|
- geocoder: false,
|
|
|
|
|
- homeButton: false,
|
|
|
|
|
- infoBox: false,
|
|
|
|
|
- sceneModePicker: false,
|
|
|
|
|
- selectionIndicator: false,
|
|
|
|
|
- timeline: false,
|
|
|
|
|
- navigationHelpButton: false,
|
|
|
|
|
- navigationInstructionsInitiallyVisible: false,
|
|
|
|
|
- shouldAnimate: false,
|
|
|
|
|
- showRenderLoopErrors: false
|
|
|
|
|
- })
|
|
|
|
|
|
|
+const updateMapMarkers = () => {
|
|
|
|
|
+ if (!viewer.value) return
|
|
|
|
|
+
|
|
|
|
|
+ const heilinData = props.simulationData?.heilinStation || {
|
|
|
|
|
+ waterLevel: '3.25',
|
|
|
|
|
+ flow: '12.5',
|
|
|
|
|
+ rainfall: '0.5'
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const reservoirData = props.simulationData?.reservoir || {
|
|
|
|
|
+ waterLevel: '18.5',
|
|
|
|
|
+ storage: '2350.8'
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 更新黑林水文站标签
|
|
|
|
|
+ const heilinEntity = viewer.value.entities.getById('heilin-station')
|
|
|
|
|
+ if (heilinEntity && heilinEntity.label) {
|
|
|
|
|
+ heilinEntity.label.text = `水位 ${heilinData.waterLevel}m\n流量 ${heilinData.flow}m³/s\n降雨量 ${heilinData.rainfall}mm/h`
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 更新小塔山水库标签
|
|
|
|
|
+ const reservoirEntity = viewer.value.entities.getById('tashan-reservoir')
|
|
|
|
|
+ if (reservoirEntity && reservoirEntity.label) {
|
|
|
|
|
+ reservoirEntity.label.text = `库水位 ${reservoirData.waterLevel}m\n库容 ${reservoirData.storage}万m³\n汛限 20.0m`
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 更新弹窗数据
|
|
|
|
|
+ if (poiData.value.name === '黑林水文站') {
|
|
|
|
|
+ poiData.value.waterLevel = heilinData.waterLevel
|
|
|
|
|
+ poiData.value.flow = heilinData.flow
|
|
|
|
|
+ poiData.value.rainfall = heilinData.rainfall
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
- if (this.viewer.cesiumWidget.creditContainer) {
|
|
|
|
|
- this.viewer.cesiumWidget.creditContainer.style.display = 'none'
|
|
|
|
|
- }
|
|
|
|
|
|
|
+watch(() => props.simulationData, () => {
|
|
|
|
|
+ updateMapMarkers()
|
|
|
|
|
+}, { deep: true, immediate: true })
|
|
|
|
|
|
|
|
- const toolbar = this.viewer._element.querySelector('.cesium-viewer-toolbar')
|
|
|
|
|
- if (toolbar) toolbar.style.display = 'none'
|
|
|
|
|
|
|
+const createPoiImage = () => {
|
|
|
|
|
+ const canvas = document.createElement('canvas')
|
|
|
|
|
+ canvas.width = 40
|
|
|
|
|
+ canvas.height = 50
|
|
|
|
|
+ const ctx = canvas.getContext('2d')
|
|
|
|
|
+
|
|
|
|
|
+ ctx.beginPath()
|
|
|
|
|
+ ctx.moveTo(20, 0)
|
|
|
|
|
+ ctx.lineTo(40, 30)
|
|
|
|
|
+ ctx.lineTo(20, 50)
|
|
|
|
|
+ ctx.lineTo(0, 30)
|
|
|
|
|
+ ctx.closePath()
|
|
|
|
|
+
|
|
|
|
|
+ const gradient = ctx.createRadialGradient(20, 25, 5, 20, 25, 25)
|
|
|
|
|
+ gradient.addColorStop(0, '#00ffff')
|
|
|
|
|
+ gradient.addColorStop(1, '#0066ff')
|
|
|
|
|
+ ctx.fillStyle = gradient
|
|
|
|
|
+ ctx.fill()
|
|
|
|
|
+
|
|
|
|
|
+ ctx.strokeStyle = '#ffffff'
|
|
|
|
|
+ ctx.lineWidth = 2
|
|
|
|
|
+ ctx.stroke()
|
|
|
|
|
+
|
|
|
|
|
+ return canvas
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
- const animationContainer = this.viewer._element.querySelector('.cesium-viewer-animationContainer')
|
|
|
|
|
- if (animationContainer) animationContainer.style.display = 'none'
|
|
|
|
|
|
|
+const closePopup = () => {
|
|
|
|
|
+ showPopup.value = false
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
- const timelineContainer = this.viewer._element.querySelector('.cesium-viewer-timelineContainer')
|
|
|
|
|
- if (timelineContainer) timelineContainer.style.display = 'none'
|
|
|
|
|
|
|
+const goToTwinStation = () => {
|
|
|
|
|
+ // 执行平滑flyto动画到黑林水文站位置
|
|
|
|
|
+ const heilinPosition = Cesium.Cartesian3.fromDegrees(118.8770798, 35.0320627, 10000)
|
|
|
|
|
+ viewer.value.camera.flyTo({
|
|
|
|
|
+ destination: heilinPosition,
|
|
|
|
|
+ duration: 2.0,
|
|
|
|
|
+ easingFunction: Cesium.EasingFunction.SINE_IN_OUT
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ // 延迟跳转到水文站详情页面
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ router.push('/heilin-station')
|
|
|
|
|
+ }, 2000)
|
|
|
|
|
+
|
|
|
|
|
+ closePopup()
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
|
|
+onMounted(async () => {
|
|
|
|
|
+ viewer.value = new Cesium.Viewer('cesiumContainer', {
|
|
|
|
|
+ terrainProvider: await Cesium.createWorldTerrainAsync(),
|
|
|
|
|
+ animation: false,
|
|
|
|
|
+ baseLayerPicker: false,
|
|
|
|
|
+ fullscreenButton: false,
|
|
|
|
|
+ geocoder: false,
|
|
|
|
|
+ homeButton: false,
|
|
|
|
|
+ infoBox: false,
|
|
|
|
|
+ sceneModePicker: false,
|
|
|
|
|
+ selectionIndicator: false,
|
|
|
|
|
+ timeline: false,
|
|
|
|
|
+ navigationHelpButton: false,
|
|
|
|
|
+ navigationInstructionsInitiallyVisible: false,
|
|
|
|
|
+ shouldAnimate: false,
|
|
|
|
|
+ showRenderLoopErrors: false
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ if (viewer.value.cesiumWidget.creditContainer) {
|
|
|
|
|
+ viewer.value.cesiumWidget.creditContainer.style.display = 'none'
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const toolbar = viewer.value._element.querySelector('.cesium-viewer-toolbar')
|
|
|
|
|
+ if (toolbar) toolbar.style.display = 'none'
|
|
|
|
|
+
|
|
|
|
|
+ const animationContainer = viewer.value._element.querySelector('.cesium-viewer-animationContainer')
|
|
|
|
|
+ if (animationContainer) animationContainer.style.display = 'none'
|
|
|
|
|
+
|
|
|
|
|
+ const timelineContainer = viewer.value._element.querySelector('.cesium-viewer-timelineContainer')
|
|
|
|
|
+ if (timelineContainer) timelineContainer.style.display = 'none'
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 加载流域边界GeoJSON
|
|
|
|
|
+ const response = await fetch('/Heilin.geojson')
|
|
|
|
|
+ const geojson = await response.json()
|
|
|
|
|
+
|
|
|
|
|
+ const dataSource = await Cesium.GeoJsonDataSource.load(geojson, {
|
|
|
|
|
+ stroke: Cesium.Color.fromCssColorString('#00d5ff'),
|
|
|
|
|
+ strokeWidth: 3,
|
|
|
|
|
+ fill: Cesium.Color.fromCssColorString('#00d5ff').withAlpha(0.1)
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ viewer.value.dataSources.add(dataSource)
|
|
|
|
|
+
|
|
|
|
|
+ // 加载水系GeoJSON
|
|
|
try {
|
|
try {
|
|
|
- const response = await fetch('/Heilin.geojson')
|
|
|
|
|
- const geojson = await response.json()
|
|
|
|
|
-
|
|
|
|
|
- const dataSource = await Cesium.GeoJsonDataSource.load(geojson, {
|
|
|
|
|
- stroke: Cesium.Color.fromCssColorString('#00d5ff'),
|
|
|
|
|
- strokeWidth: 3,
|
|
|
|
|
- fill: Cesium.Color.fromCssColorString('#00d5ff').withAlpha(0.1)
|
|
|
|
|
|
|
+ const waterResponse = await fetch('/黑林水系.geojson')
|
|
|
|
|
+ const waterGeojson = await waterResponse.json()
|
|
|
|
|
+
|
|
|
|
|
+ const waterDataSource = await Cesium.GeoJsonDataSource.load(waterGeojson, {
|
|
|
|
|
+ stroke: Cesium.Color.fromCssColorString('#62f6fb'),
|
|
|
|
|
+ strokeWidth: 2,
|
|
|
|
|
+ fill: Cesium.Color.fromCssColorString('#62f6fb').withAlpha(0.2)
|
|
|
})
|
|
})
|
|
|
-
|
|
|
|
|
- this.viewer.dataSources.add(dataSource)
|
|
|
|
|
-
|
|
|
|
|
- try {
|
|
|
|
|
- const waterResponse = await fetch('/黑林水系.geojson')
|
|
|
|
|
- const waterGeojson = await waterResponse.json()
|
|
|
|
|
-
|
|
|
|
|
- const waterDataSource = await Cesium.GeoJsonDataSource.load(waterGeojson, {
|
|
|
|
|
- stroke: Cesium.Color.fromCssColorString('#62f6fb'),
|
|
|
|
|
- strokeWidth: 2,
|
|
|
|
|
- fill: Cesium.Color.fromCssColorString('#62f6fb').withAlpha(0.2)
|
|
|
|
|
- })
|
|
|
|
|
-
|
|
|
|
|
- this.viewer.dataSources.add(waterDataSource)
|
|
|
|
|
- } catch (waterError) {
|
|
|
|
|
- console.error('加载水系GeoJSON失败:', waterError)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- try {
|
|
|
|
|
- const reservoirResponse = await fetch('/小塔山水库.geojson')
|
|
|
|
|
- const reservoirGeojson = await reservoirResponse.json()
|
|
|
|
|
-
|
|
|
|
|
- const reservoirDataSource = await Cesium.GeoJsonDataSource.load(reservoirGeojson, {
|
|
|
|
|
- stroke: Cesium.Color.fromCssColorString('#00d4ff'),
|
|
|
|
|
- strokeWidth: 2,
|
|
|
|
|
- fill: Cesium.Color.fromCssColorString('#00d4ff').withAlpha(0.3)
|
|
|
|
|
- })
|
|
|
|
|
-
|
|
|
|
|
- this.viewer.dataSources.add(reservoirDataSource)
|
|
|
|
|
- } catch (reservoirError) {
|
|
|
|
|
- console.error('加载小塔山水库GeoJSON失败:', reservoirError)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- const entities = dataSource.entities.values
|
|
|
|
|
- if (entities.length > 0) {
|
|
|
|
|
- let minLat = 90, maxLat = -90, minLon = 180, maxLon = -180
|
|
|
|
|
-
|
|
|
|
|
- for (const entity of entities) {
|
|
|
|
|
- if (entity.polygon && entity.polygon.hierarchy) {
|
|
|
|
|
- const hierarchy = entity.polygon.hierarchy.getValue()
|
|
|
|
|
- const positions = hierarchy.positions
|
|
|
|
|
-
|
|
|
|
|
- for (const pos of positions) {
|
|
|
|
|
- const cartographic = Cesium.Cartographic.fromCartesian(pos)
|
|
|
|
|
- const lat = Cesium.Math.toDegrees(cartographic.latitude)
|
|
|
|
|
- const lon = Cesium.Math.toDegrees(cartographic.longitude)
|
|
|
|
|
-
|
|
|
|
|
- minLat = Math.min(minLat, lat)
|
|
|
|
|
- maxLat = Math.max(maxLat, lat)
|
|
|
|
|
- minLon = Math.min(minLon, lon)
|
|
|
|
|
- maxLon = Math.max(maxLon, lon)
|
|
|
|
|
- }
|
|
|
|
|
|
|
+
|
|
|
|
|
+ viewer.value.dataSources.add(waterDataSource)
|
|
|
|
|
+ } catch (waterError) {
|
|
|
|
|
+ console.error('加载水系GeoJSON失败:', waterError)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 加载小塔山水库GeoJSON
|
|
|
|
|
+ try {
|
|
|
|
|
+ const reservoirResponse = await fetch('/小塔山水库.geojson')
|
|
|
|
|
+ const reservoirGeojson = await reservoirResponse.json()
|
|
|
|
|
+
|
|
|
|
|
+ const reservoirDataSource = await Cesium.GeoJsonDataSource.load(reservoirGeojson, {
|
|
|
|
|
+ stroke: Cesium.Color.fromCssColorString('#00d4ff'),
|
|
|
|
|
+ strokeWidth: 2,
|
|
|
|
|
+ fill: Cesium.Color.fromCssColorString('#00d4ff').withAlpha(0.3)
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ viewer.value.dataSources.add(reservoirDataSource)
|
|
|
|
|
+ } catch (reservoirError) {
|
|
|
|
|
+ console.error('加载小塔山水库GeoJSON失败:', reservoirError)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const entities = dataSource.entities.values
|
|
|
|
|
+ if (entities.length > 0) {
|
|
|
|
|
+ let minLat = 90, maxLat = -90, minLon = 180, maxLon = -180
|
|
|
|
|
+
|
|
|
|
|
+ for (const entity of entities) {
|
|
|
|
|
+ if (entity.polygon && entity.polygon.hierarchy) {
|
|
|
|
|
+ const hierarchy = entity.polygon.hierarchy.getValue()
|
|
|
|
|
+ const positions = hierarchy.positions
|
|
|
|
|
+
|
|
|
|
|
+ for (const pos of positions) {
|
|
|
|
|
+ const cartographic = Cesium.Cartographic.fromCartesian(pos)
|
|
|
|
|
+ const lat = Cesium.Math.toDegrees(cartographic.latitude)
|
|
|
|
|
+ const lon = Cesium.Math.toDegrees(cartographic.longitude)
|
|
|
|
|
+
|
|
|
|
|
+ minLat = Math.min(minLat, lat)
|
|
|
|
|
+ maxLat = Math.max(maxLat, lat)
|
|
|
|
|
+ minLon = Math.min(minLon, lon)
|
|
|
|
|
+ maxLon = Math.max(maxLon, lon)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- this.viewer.camera.flyTo({
|
|
|
|
|
- destination: Cesium.Cartesian3.fromDegrees(118.9019, 34.985, 33000),
|
|
|
|
|
- duration: 0
|
|
|
|
|
- })
|
|
|
|
|
}
|
|
}
|
|
|
- } catch (error) {
|
|
|
|
|
- console.error('加载GeoJSON失败:', error)
|
|
|
|
|
- this.viewer.camera.flyTo({
|
|
|
|
|
|
|
+
|
|
|
|
|
+ viewer.value.camera.flyTo({
|
|
|
destination: Cesium.Cartesian3.fromDegrees(118.9019, 34.985, 33000),
|
|
destination: Cesium.Cartesian3.fromDegrees(118.9019, 34.985, 33000),
|
|
|
duration: 0
|
|
duration: 0
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('加载GeoJSON失败:', error)
|
|
|
|
|
+ viewer.value.camera.flyTo({
|
|
|
|
|
+ destination: Cesium.Cartesian3.fromDegrees(118.9019, 34.985, 33000),
|
|
|
|
|
+ duration: 0
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
addPoiMarker()
|
|
addPoiMarker()
|
|
|
|
|
|
|
@@ -372,7 +438,10 @@ onUnmounted(() => {
|
|
|
if (blinkInterval) {
|
|
if (blinkInterval) {
|
|
|
clearInterval(blinkInterval)
|
|
clearInterval(blinkInterval)
|
|
|
}
|
|
}
|
|
|
-}
|
|
|
|
|
|
|
+ if (viewer.value) {
|
|
|
|
|
+ viewer.value.destroy()
|
|
|
|
|
+ }
|
|
|
|
|
+})
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
|
<style scoped>
|
|
<style scoped>
|
|
@@ -459,5 +528,4 @@ onUnmounted(() => {
|
|
|
margin-bottom: 5px;
|
|
margin-bottom: 5px;
|
|
|
line-height: 1.5;
|
|
line-height: 1.5;
|
|
|
}
|
|
}
|
|
|
-</script>
|
|
|
|
|
</style>
|
|
</style>
|