| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559 |
- <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>
-
- <!-- POI详情弹窗 -->
- <div v-if="showPopup" class="poi-popup" :style="popupStyle">
- <div class="popup-header">
- <span class="popup-title">{{ poiData.name }} <a href="#" class="popup-detail" @click.prevent="goToTwinStation">详情</a></span>
- <button class="popup-close" @click="closePopup">×</button>
- </div>
- <div class="popup-content">
- <div class="popup-section">
- <div class="popup-label">实时数据</div>
- <div class="popup-value">水位: {{ poiData.waterLevel }}m</div>
- <div class="popup-value">流量: {{ poiData.flow }}m³/s</div>
- <div class="popup-value">降雨量: {{ poiData.rainfall }}mm/h</div>
- </div>
- <div class="popup-section">
- <div class="popup-label">历史同期值</div>
- <div class="popup-value">水位: {{ poiData.historyWaterLevel }}m</div>
- <div class="popup-value">流量: {{ poiData.historyFlow }}m³/s</div>
- </div>
- </div>
- </div>
- </template>
- <script>
- import * as Cesium from 'cesium'
- import 'cesium/Build/CesiumUnminified/Widgets/widgets.css'
- <<<<<<< HEAD
- 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'
- =======
- const emit = defineEmits(['toggleMap'])
- const viewer = ref(null)
- const showPopup = ref(false)
- const popupPosition = ref({ x: 0, y: 0 })
- const router = useRouter()
- const DESIGN_WIDTH = 1920
- const DESIGN_HEIGHT = 1080
- const getScaleRatio = () => {
- return Math.min(window.innerWidth / DESIGN_WIDTH, window.innerHeight / DESIGN_HEIGHT)
- }
- const poiData = ref({
- name: '黑林水文站',
- waterLevel: '3.25',
- flow: '12.5',
- rainfall: '0.5',
- historyWaterLevel: '3.10',
- historyFlow: '11.8',
- device: 'HL-Sensor-001'
- })
- const popupStyle = computed(() => {
- return {
- left: popupPosition.value.x + 'px',
- top: popupPosition.value.y + 'px'
- }
- })
- 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
- >>>>>>> origin/master
- },
- 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
- }
- },
- 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
- }
- })
-
- 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
- }
- })
-
- 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
- }
- })
- <<<<<<< HEAD
- 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
- }
- })
-
- 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 handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas)
-
- 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 }
- break
- }
- =======
- 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
- >>>>>>> origin/master
- }
- }
- }, 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
- }
-
- 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
- })
-
- if (this.viewer.cesiumWidget.creditContainer) {
- this.viewer.cesiumWidget.creditContainer.style.display = 'none'
- }
-
- const toolbar = this.viewer._element.querySelector('.cesium-viewer-toolbar')
- if (toolbar) toolbar.style.display = 'none'
-
- const animationContainer = this.viewer._element.querySelector('.cesium-viewer-animationContainer')
- if (animationContainer) animationContainer.style.display = 'none'
-
- const timelineContainer = this.viewer._element.querySelector('.cesium-viewer-timelineContainer')
- if (timelineContainer) timelineContainer.style.display = 'none'
- 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)
- })
-
- 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)
- }
- }
- }
-
- 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({
- destination: Cesium.Cartesian3.fromDegrees(118.9019, 34.985, 33000),
- duration: 0
- })
- }
- <<<<<<< HEAD
- this.addPoiMarker()
- this.$nextTick(() => {
- this.updateMapMarkers(this.simulationData)
- })
- },
- beforeUnmount() {
- if (this.blinkInterval) {
- clearInterval(this.blinkInterval)
- }
- if (this.viewer) {
- this.viewer.destroy()
- }
- =======
- addPoiMarker()
-
- window.addEventListener('resize', handleResize)
- })
- const handleResize = () => {
- if (viewer.value) {
- viewer.value.resize()
- }
- }
- onUnmounted(() => {
- window.removeEventListener('resize', handleResize)
- if (blinkInterval) {
- clearInterval(blinkInterval)
- >>>>>>> origin/master
- }
- }
- </script>
- <style scoped>
- .poi-popup {
- position: fixed;
- width: 240px;
- background: rgba(0, 20, 40, 0.95);
- border: 1px solid rgba(0, 213, 255, 0.6);
- border-radius: 8px;
- box-shadow: 0 0 20px rgba(0, 213, 255, 0.4);
- z-index: 10;
- pointer-events: auto;
- overflow: hidden;
- }
- .popup-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 12px 15px;
- background: linear-gradient(90deg, rgba(0, 60, 120, 0.9), rgba(0, 100, 150, 0.7));
- border-bottom: 1px solid rgba(0, 213, 255, 0.4);
- }
- .popup-title {
- font-size: 16px;
- font-weight: bold;
- color: #00ffff;
- text-shadow: 0 0 5px rgba(0, 255, 255, 0.5);
- }
- .popup-detail {
- font-size: 12px;
- color: #62f6fb;
- text-decoration: none;
- margin-left: 8px;
- cursor: pointer;
- transition: color 0.3s;
- }
- .popup-detail:hover {
- color: #ffffff;
- text-decoration: underline;
- }
- .popup-close {
- background: none;
- border: none;
- color: #7bbef6;
- font-size: 20px;
- cursor: pointer;
- padding: 0;
- line-height: 1;
- transition: color 0.3s;
- }
- .popup-close:hover {
- color: #ffffff;
- }
- .popup-content {
- padding: 15px;
- }
- .popup-section {
- margin-bottom: 15px;
- }
- .popup-section:last-child {
- margin-bottom: 0;
- }
- .popup-label {
- font-size: 14px;
- font-weight: bold;
- color: #62f6fb;
- margin-bottom: 8px;
- text-shadow: 0 0 3px rgba(0, 212, 255, 0.5);
- }
- .popup-value {
- font-size: 13px;
- color: #e0fcff;
- margin-bottom: 5px;
- line-height: 1.5;
- }
- </style>
|