|
|
@@ -0,0 +1,1355 @@
|
|
|
+<template>
|
|
|
+ <div class="mapBox">
|
|
|
+ <div id="cesiumContainer" style="width: 100%; height: 100vh"></div>
|
|
|
+ <div :class="!menuComTStyB ? 'menuComTSty' : 'menuComT'" v-if="0">
|
|
|
+ <menuCom
|
|
|
+ :showbasemap="false"
|
|
|
+ :showwindspeed="false"
|
|
|
+ :showcloud="false"
|
|
|
+ :showrainfall="false"
|
|
|
+ :showtemperature="false"
|
|
|
+ :showcity="false"
|
|
|
+ :showwind="false"
|
|
|
+ :showexit="true"
|
|
|
+ :showwindModel="true"
|
|
|
+ @handleWindModel="showwindmodel"
|
|
|
+ @handleInit="handleInitView"
|
|
|
+ @handleExit="switchLayer"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <el-drawer
|
|
|
+ v-model="windDrawer"
|
|
|
+ direction="rtl"
|
|
|
+ class="windModelDrawer"
|
|
|
+ :before-close="handleClose"
|
|
|
+ >
|
|
|
+ <template #header>
|
|
|
+ <h3 style="font-weight: bold">{{ windDrawerHeader }}</h3>
|
|
|
+ </template>
|
|
|
+ <template #default>
|
|
|
+ <div class="windDrawerCla">
|
|
|
+ <div class="line" v-if="!showModelMsg">
|
|
|
+ <div class="leftContent">
|
|
|
+ <span>{{ windDrawerTitle }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="jcxx" v-if="showBasicMsg">
|
|
|
+ <windHome :modelValItem="modelVal" />
|
|
|
+ </div>
|
|
|
+ <div class="spjk" v-if="showVideoMsg">
|
|
|
+ <!-- <iframe
|
|
|
+ src="/public/static/windVideo.mp4"
|
|
|
+ frameborder="0"
|
|
|
+ style="width: 100%; height: 100%"
|
|
|
+ ></iframe> -->
|
|
|
+ <video ref="videoPlayer" controls width="95%">
|
|
|
+ <source src="/public/static/windVideo.mp4" type="video/mp4" />
|
|
|
+ </video>
|
|
|
+ </div>
|
|
|
+ <div class="gzck" v-if="showProblemMsg">
|
|
|
+ <windPro />
|
|
|
+ </div>
|
|
|
+ <div class="third" v-if="showModelMsg">
|
|
|
+ <ModelUnpack
|
|
|
+ :modelUnpackType="modelUnpackType"
|
|
|
+ :showModelMsg="showModelMsg"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-drawer>
|
|
|
+ <comModelDialog
|
|
|
+ :showcomModelDia="showcomModelDia"
|
|
|
+ :modelVal="modelVal"
|
|
|
+ @showDia="showComDia"
|
|
|
+ />
|
|
|
+ <windView v-if="showWindDetail" @coverOnChange="coverOnChange" :currentHeight="currentHeight"
|
|
|
+ @showDetail="menuComTSty" @backStations="backStations" />
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import * as Cesium from "../../Cesium";
|
|
|
+import "../../Cesium/Widgets/widgets.css";
|
|
|
+import fjLonLatJson from "./fjLonLat.json";
|
|
|
+import allStationJson from "./allStationJson.json"
|
|
|
+import basicGeoJson from "../../assets/geoJson/basic.json";
|
|
|
+import comModelDialog from "@/components/comModelDialog.vue";
|
|
|
+import windView from "./windView.vue";
|
|
|
+import menuCom from "../menuCom.vue";
|
|
|
+
|
|
|
+import cloudJson from "/public/static/exportData/cloud/layer.json";
|
|
|
+import rainJson from "/public/static/exportData/rain/layer.json";
|
|
|
+import tempJson from "/public/static/exportData/tmp/layer.json";
|
|
|
+
|
|
|
+// import bw from "@/assets/windimgs/fanSvg/bw.svg"
|
|
|
+//风场展示图标
|
|
|
+import fc from "@/assets/windimgs/fanSvg/fc.png"
|
|
|
+//火电展示图标
|
|
|
+import hd from "@/assets/windimgs/fanSvg/hd.png"
|
|
|
+//光伏电站展示图标
|
|
|
+import gf from "@/assets/windimgs/fanSvg/gf.png"
|
|
|
+
|
|
|
+//故障
|
|
|
+import gz from "@/assets/windimgs/fanSvg/gz.svg"
|
|
|
+//待机
|
|
|
+import dj from "@/assets/windimgs/fanSvg/dj.svg"
|
|
|
+//检修
|
|
|
+import jx from "@/assets/windimgs/fanSvg/jx.svg"
|
|
|
+//限电
|
|
|
+import xd from "@/assets/windimgs/fanSvg/xd.svg"
|
|
|
+//离线
|
|
|
+import lx from "@/assets/windimgs/fanSvg/lx.svg"
|
|
|
+//受累
|
|
|
+import sl from "@/assets/windimgs/fanSvg/sl.svg"
|
|
|
+//动图使用柱子和扇叶
|
|
|
+import bwzhu from "@/assets/windimgs/fanSvg/bwzhu.svg"
|
|
|
+import bwshan from "@/assets/windimgs/fanSvg/bwshan.png"
|
|
|
+
|
|
|
+import windHome from "@/components/windHome/index.vue";
|
|
|
+import windPro from "@/components/windProDetail/windProblem.vue";
|
|
|
+import ModelUnpack from "@/components/modelUnpack.vue";
|
|
|
+
|
|
|
+import { WindLayer } from "cesium-wind";
|
|
|
+import windGridData from "./windGridData.json";
|
|
|
+export default {
|
|
|
+ name: "windMap3D",
|
|
|
+ components: {
|
|
|
+ comModelDialog,
|
|
|
+ windView,
|
|
|
+ menuCom,
|
|
|
+ windHome,
|
|
|
+ windPro,
|
|
|
+ ModelUnpack,
|
|
|
+ },
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ viewer: null,
|
|
|
+ windLayer: null, // 风场图
|
|
|
+ windLayerTimmer: null, // 风场图计时器
|
|
|
+ cloudImagesLayer: [], // 卫星云图
|
|
|
+ cloudLayer: null, // 卫星云图
|
|
|
+ cloudintervalId: null,
|
|
|
+ rainImagesLayer: [], // 降雨图
|
|
|
+ rainLayer: null, // 降雨图
|
|
|
+ rainintervalId: null, // 降雨图
|
|
|
+ tempImagesLayer: [], //温度图
|
|
|
+ temperatureLayer: null, //温度图
|
|
|
+ tempintervalId: null, //温度图
|
|
|
+ showcomModelDia: false,
|
|
|
+ currentHeight: 0,
|
|
|
+ modelVal: null,
|
|
|
+ menuComTStyB: false,
|
|
|
+ modelUnpackType: "fengji",
|
|
|
+ windDrawer: false,
|
|
|
+ windDrawerTitle: "",
|
|
|
+ windDrawerHeader: "",
|
|
|
+ showBasicMsg: false,
|
|
|
+ showVideoMsg: false,
|
|
|
+ showProblemMsg: false,
|
|
|
+ showModelMsg: false,
|
|
|
+ showWindDetail: true,
|
|
|
+ allStationentitys: [],
|
|
|
+ allWindEntitys: [],
|
|
|
+ urlTiles: "/public/static/tiles/{z}/{x}/{y}.jpg"
|
|
|
+ };
|
|
|
+ },
|
|
|
+ mounted() {
|
|
|
+ this.initCesium();
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ async initCesium() {
|
|
|
+ const box = document.getElementById("cesiumContainer");
|
|
|
+ const viewer = new Cesium.Viewer(box, {
|
|
|
+ geocoder: false, // 地址搜索控件
|
|
|
+ homeButton: false, // 返回地图初始位置控件
|
|
|
+ infoBox: false, // 地图默认的信息控件
|
|
|
+ sceneModePicker: false, // 场景模式切换控件
|
|
|
+ baseLayerPicker: false, // 底图切换控件
|
|
|
+ navigationHelpButton: false, // 帮助控件
|
|
|
+ animation: false, // 动画控制控件
|
|
|
+ timeline: false, // 时间线控件
|
|
|
+ fullscreenButton: false, // 全屏按钮控件
|
|
|
+ // imageryProvider: true, // 是否显示 Cesium 默认地图的底图
|
|
|
+ vrButton: false,
|
|
|
+ selectionIndicator: false,
|
|
|
+ shouldAnimate: true,
|
|
|
+ });
|
|
|
+
|
|
|
+ const imageryProvider = new Cesium.UrlTemplateImageryProvider({
|
|
|
+ // minimumLevel: 11,
|
|
|
+ // maximumLevel: 18,
|
|
|
+ // url: this.urlTiles,
|
|
|
+ url: "https://webst02.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}",
|
|
|
+ // url: "http://192.168.2.180:3007/tiles/map/{z}/{x}/{y}",
|
|
|
+ credit: "影像地图",
|
|
|
+ });
|
|
|
+ imageryProvider.alpha = 0.55; // 透明度
|
|
|
+ imageryProvider.brightness = 1; // 亮度
|
|
|
+ imageryProvider.contrast = 1; // 对比度
|
|
|
+
|
|
|
+ viewer.imageryLayers.addImageryProvider(imageryProvider);
|
|
|
+ viewer._cesiumWidget._creditContainer.style.display = "none";
|
|
|
+ // this.csceneElliposid(viewer)
|
|
|
+ this.viewer = viewer;
|
|
|
+ // 展示场站
|
|
|
+ // this.showAllStation(viewer)
|
|
|
+ // 展示风机
|
|
|
+ this.showWindFromStation(viewer)
|
|
|
+ this.initGeoJsonData()
|
|
|
+ },
|
|
|
+ // 初始化 geoJson 数据
|
|
|
+ async initGeoJsonData() {
|
|
|
+ // 创建GeoJSON数据源
|
|
|
+ await new Cesium.GeoJsonDataSource.load(basicGeoJson, {
|
|
|
+ stroke: Cesium.Color.GRAY, // 边界线颜色
|
|
|
+ fill: Cesium.Color.BLACK.withAlpha(0), // 填充颜色
|
|
|
+ strokeWidth: 1, // 边界线宽度
|
|
|
+ markerSymbol: "?", // 点要素的符号
|
|
|
+ clampToGround: true, // 贴地
|
|
|
+ }).then((dataSource) => {
|
|
|
+ // 添加到视图
|
|
|
+ this.viewer.dataSources.add(dataSource);
|
|
|
+ var entities = dataSource.entities.values;
|
|
|
+ for (let i = 0; i < entities.length; i++) {
|
|
|
+ let entity = entities[i];
|
|
|
+
|
|
|
+ entity.polygon.hierarchy.getValue(Cesium.JulianDate.now()).positions;
|
|
|
+ //单独设置线条样式
|
|
|
+ var positions = entity.polygon.hierarchy._value.positions;
|
|
|
+
|
|
|
+ entity.polyline = {
|
|
|
+ positions: positions,
|
|
|
+ width: 1,
|
|
|
+ outline: false,
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ // 添加中文标签图层
|
|
|
+ const labelLayer = new Cesium.LabelCollection();
|
|
|
+ this.viewer.scene.primitives.add(labelLayer);
|
|
|
+ const cities = [];
|
|
|
+ basicGeoJson?.features?.forEach((ele) => {
|
|
|
+ if (Array.isArray(ele.properties.centroid)) {
|
|
|
+ const name = ele.properties.name;
|
|
|
+ const lon = ele.properties.centroid[0];
|
|
|
+ const lat = ele.properties.centroid[1];
|
|
|
+ cities.push({ name, lon, lat });
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ cities.forEach((city, index) => {
|
|
|
+ labelLayer.add({
|
|
|
+ id: index,
|
|
|
+ name: "cityLabel",
|
|
|
+ position: Cesium.Cartesian3.fromDegrees(city.lon, city.lat, 10),
|
|
|
+ text: city.name,
|
|
|
+ font: 'bold 14px "Microsoft YaHei", sans-serif',
|
|
|
+ fillColor: Cesium.Color.fromCssColorString("#000"),
|
|
|
+ outlineColor: Cesium.Color.WHITE,
|
|
|
+ outlineWidth: 2,
|
|
|
+ style: Cesium.LabelStyle.FILL_AND_OUTLINE,
|
|
|
+ pixelOffset: new Cesium.Cartesian2(0, 0), // 设置为0
|
|
|
+ horizontalOrigin: Cesium.HorizontalOrigin.CENTER, // 水平居中
|
|
|
+ verticalOrigin: Cesium.VerticalOrigin.CENTER, // 垂直居中
|
|
|
+ });
|
|
|
+ });
|
|
|
+ });
|
|
|
+ },
|
|
|
+ backStations() {
|
|
|
+ this.showWindDetail = false
|
|
|
+ this.allWindEntitys.forEach(({ entity, handler }) => {
|
|
|
+ this.viewer.entities.remove(entity); // 移除实体
|
|
|
+ if (!handler.isDestroyed()) {
|
|
|
+ handler.destroy(); // 销毁事件处理器(关键!)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ this.allWindEntitys = []
|
|
|
+ // if (this.viewer && this.viewer.destroy) {
|
|
|
+ // this.viewer.destroy();
|
|
|
+ // this.viewer = null
|
|
|
+ // }
|
|
|
+ // this.initCesium();
|
|
|
+ this.showAllStation(this.viewer)
|
|
|
+ },
|
|
|
+ // 展示所有风场
|
|
|
+ showAllStation(viewer) {
|
|
|
+ allStationJson.station.forEach((e, index) => {
|
|
|
+ if (e.energytype === "Wind") {
|
|
|
+ this.showStationFn(viewer, e, index, fc)
|
|
|
+ } else if(e.energytype === "Fire") {
|
|
|
+ this.showStationFn(viewer, e, index, hd)
|
|
|
+ } else {
|
|
|
+ this.showStationFn(viewer, e, index, gf)
|
|
|
+ }
|
|
|
+ });
|
|
|
+ this.resetAllStationViewport()
|
|
|
+ },
|
|
|
+ // 根据状态展示不同颜色风机贴图
|
|
|
+ showStationFn(viewer, e, index, images) {
|
|
|
+ const position = Cesium.Cartesian3.fromDegrees(e.longitude, e.latitude);
|
|
|
+ const entity = viewer.entities.add({
|
|
|
+ id: index,
|
|
|
+ position, // 模型位置
|
|
|
+ billboard: {
|
|
|
+ image: images, // 也可以是 SVG 路径,如 'icon.svg'
|
|
|
+ scale: 0.5,
|
|
|
+ verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
|
|
+ horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
|
|
|
+ // 模型贴地
|
|
|
+ heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
|
|
+ },
|
|
|
+ label: {
|
|
|
+ text: e.plantname,
|
|
|
+ font: '14px sans-serif',
|
|
|
+ fillColor: Cesium.Color.fromBytes(255, 255, 255),
|
|
|
+ heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
|
|
+ }
|
|
|
+ });
|
|
|
+ let that = this
|
|
|
+ const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
|
|
|
+ handler.setInputAction(function (movement) {
|
|
|
+ var position = movement.position;
|
|
|
+ var pickedObject = viewer.scene.pick(position);
|
|
|
+ if (pickedObject && pickedObject.id.id === index) {
|
|
|
+ console.log("你点击了标签或模型!", entity);
|
|
|
+ console.log("选中风场或新能源场", e.plantname);
|
|
|
+ that.showWindDetail = true
|
|
|
+ that.allStationentitys.forEach(({ entity, handler }) => {
|
|
|
+ viewer.entities.remove(entity); // 移除实体
|
|
|
+ if (!handler.isDestroyed()) {
|
|
|
+ handler.destroy(); // 销毁事件处理器(关键!)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ that.allStationentitys = []
|
|
|
+ that.showWindFromStation(viewer)
|
|
|
+ }
|
|
|
+ }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
|
|
|
+ that.allStationentitys.push({entity, handler})
|
|
|
+ },
|
|
|
+ // 展示所选风场的风机
|
|
|
+ showWindFromStation(viewer) {
|
|
|
+ fjLonLatJson.data.forEach((e, index) => {
|
|
|
+ if (e.status) {
|
|
|
+ if (e.status === 1) {
|
|
|
+ this.showStatuswind(viewer, e, dj)
|
|
|
+ } else if (e.status === 2) {
|
|
|
+ this.showStatuswind(viewer, e, gz)
|
|
|
+ } else if (e.status === 3) {
|
|
|
+ this.showStatuswind(viewer, e, jx)
|
|
|
+ } else if (e.status === 4) {
|
|
|
+ this.showStatuswind(viewer, e, xd)
|
|
|
+ } else if (e.status === 5) {
|
|
|
+ this.showStatuswind(viewer, e, lx)
|
|
|
+ } else if (e.status === 6) {
|
|
|
+ this.showStatuswind(viewer, e, sl)
|
|
|
+ } else {
|
|
|
+ //并网风机
|
|
|
+ this.showAnimatewind(viewer, e)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ this.resetWindViewport()
|
|
|
+ },
|
|
|
+ // 根据状态展示不同颜色风机贴图
|
|
|
+ showStatuswind(viewer, e, type, index) {
|
|
|
+ this.addSvg(
|
|
|
+ viewer,
|
|
|
+ type,
|
|
|
+ e.longitude,
|
|
|
+ e.latitude,
|
|
|
+ e,
|
|
|
+ Math.random().toFixed(4)*10000
|
|
|
+ );
|
|
|
+ },
|
|
|
+ // 风机柱与扇叶分开展示转动效果贴图
|
|
|
+ showAnimatewind(viewer, e, index) {
|
|
|
+ this.addSvgs(
|
|
|
+ viewer,
|
|
|
+ bwshan,
|
|
|
+ e.longitude,
|
|
|
+ e.latitude,
|
|
|
+ e,
|
|
|
+ Math.random().toFixed(3)*1000
|
|
|
+ );
|
|
|
+ this.addSvg(
|
|
|
+ viewer,
|
|
|
+ bwzhu,
|
|
|
+ e.longitude,
|
|
|
+ e.latitude,
|
|
|
+ e,
|
|
|
+ Math.random().toFixed(3)*2000
|
|
|
+ );
|
|
|
+ },
|
|
|
+
|
|
|
+ // 生成随机数
|
|
|
+ randomNum(minNum, maxNum) {
|
|
|
+ switch (arguments.length) {
|
|
|
+ case 1:
|
|
|
+ return parseInt(Math.random() * minNum + 1, 10);
|
|
|
+ case 2:
|
|
|
+ return parseInt(Math.random() * (maxNum - minNum + 1) + minNum, 10);
|
|
|
+ default:
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ //给模型上描边
|
|
|
+ getWtStatue(code) {
|
|
|
+ if (code === 1) {
|
|
|
+ return "#fff";
|
|
|
+ } else if (code === 2) {
|
|
|
+ return "#1890ff";
|
|
|
+ } else {
|
|
|
+ return "#f25656";
|
|
|
+ }
|
|
|
+ },
|
|
|
+ //添加模型
|
|
|
+ addModel(viewer, uri, name, lon, lat, scale, val) {
|
|
|
+ const hpRoll = new Cesium.HeadingPitchRoll(90.0, 0.0, 0.0);
|
|
|
+ const position = Cesium.Cartesian3.fromDegrees(lon, lat);
|
|
|
+ const orientation = Cesium.Transforms.headingPitchRollQuaternion(
|
|
|
+ position,
|
|
|
+ hpRoll
|
|
|
+ );
|
|
|
+
|
|
|
+ const wtStatue = this.randomNum(1, 3);
|
|
|
+ const statueColor = this.getWtStatue(wtStatue);
|
|
|
+
|
|
|
+ const entity = viewer.entities.add({
|
|
|
+ name, // 模型名称
|
|
|
+ position, // 模型位置
|
|
|
+ orientation, // 模型朝向
|
|
|
+ animation: false,
|
|
|
+ model: {
|
|
|
+ uri,
|
|
|
+ scale,
|
|
|
+ // 模型贴地
|
|
|
+ heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
|
|
+ silhouetteSize: 2,
|
|
|
+ silhouetteColor: Cesium.Color.fromCssColorString(statueColor),
|
|
|
+ runAnimations: wtStatue === 3 ? false : true,
|
|
|
+ },
|
|
|
+ //添加标签
|
|
|
+ label: {
|
|
|
+ text: val ? val.name : name, // 标签文字
|
|
|
+ font: "14px sans-serif", // 字体
|
|
|
+ fillColor: Cesium.Color.fromBytes(255, 255, 255), // 填充颜色
|
|
|
+ outlineColor: Cesium.Color.BLACK, // 描边颜色
|
|
|
+ outlineWidth: 2, // 描边宽度
|
|
|
+ style: Cesium.LabelStyle.FILL_AND_OUTLINE, // 填充+描边
|
|
|
+ verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // 文本在模型上方
|
|
|
+ horizontalOrigin: Cesium.HorizontalOrigin.CENTER, // 水平居中
|
|
|
+ eyeOffset: new Cesium.Cartesian3(-100, 1100, 5), // 向上 15 米
|
|
|
+ translucencyByDistance: new Cesium.NearFarScalar(
|
|
|
+ 1.0e6,
|
|
|
+ 1.0,
|
|
|
+ 6.0e6,
|
|
|
+ 0.0
|
|
|
+ ),
|
|
|
+ },
|
|
|
+ });
|
|
|
+ let that = this;
|
|
|
+ // 创建事件处理器
|
|
|
+ const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
|
|
|
+ // handler.setInputAction(function(click) {
|
|
|
+ // const picked = viewer.scene.pick(click.position);
|
|
|
+ // if (picked && picked.id === entity) {
|
|
|
+ // console.log('你点击了标签或模型!', entity);
|
|
|
+ // console.log('标签或模型数据!', val);
|
|
|
+ // // alert('你点击了: ' + entity.label.text.getValue());
|
|
|
+ // // this.$refs.comModelDialog.init(val)
|
|
|
+ // if (name !== '光伏') {
|
|
|
+ // that.showcomModelDia = true
|
|
|
+ // that.modelVal = val
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+ // }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
|
|
|
+ handler.setInputAction(function (movement) {
|
|
|
+ var position = movement.position;
|
|
|
+ var pickedObject = viewer.scene.pick(position);
|
|
|
+ if (pickedObject && pickedObject.id === entity) {
|
|
|
+ console.log("你点击了标签或模型!", entity);
|
|
|
+ console.log("标签或模型数据!", val);
|
|
|
+ that.modelVal = val;
|
|
|
+ // 找到实体,显示包含实体信息的弹框
|
|
|
+ that.showRightClickPopup(position, val);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
|
|
|
+ },
|
|
|
+ //添加svg或png贴图
|
|
|
+ addSvg(viewer, uri, lon, lat, val, index) {
|
|
|
+ let that = this;
|
|
|
+ let ids = that.generateUniqueId(val.id)
|
|
|
+ const position = Cesium.Cartesian3.fromDegrees(lon, lat);
|
|
|
+ const entity = viewer.entities.add({
|
|
|
+ id: ids,
|
|
|
+ position, // 模型位置
|
|
|
+ billboard: {
|
|
|
+ image: uri, // 也可以是 SVG 路径,如 'icon.svg'
|
|
|
+ scale: 0.5,
|
|
|
+ verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
|
|
+ horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
|
|
|
+ // 模型贴地
|
|
|
+ heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
|
|
+ },
|
|
|
+ label: {
|
|
|
+ text: val.name ? val.name : val.plantname,
|
|
|
+ font: '14px sans-serif',
|
|
|
+ fillColor: Cesium.Color.fromBytes(255, 255, 255),
|
|
|
+ heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
|
|
+ pixelOffset: val.name === 'F10号风机' ? new Cesium.Cartesian2(40, -20) : new Cesium.Cartesian2(0, 0)
|
|
|
+ }
|
|
|
+ });
|
|
|
+ // 创建事件处理器
|
|
|
+ const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
|
|
|
+ handler.setInputAction(function (movement) {
|
|
|
+ var position = movement.position;
|
|
|
+ var pickedObject = viewer.scene.pick(position);
|
|
|
+ if (pickedObject && pickedObject.id.id === ids) {
|
|
|
+ console.log("你点击了标签或模型!", entity);
|
|
|
+ console.log("标签或模型数据!", val);
|
|
|
+ that.modelVal = val;
|
|
|
+ // 找到实体,显示包含实体信息的弹框
|
|
|
+ that.showRightClickPopup(position, val);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
|
|
|
+ this.allWindEntitys.push({entity, handler})
|
|
|
+ },
|
|
|
+ //添加svg或png贴图
|
|
|
+ addSvgs(viewer, uri, lon, lat, val, index) {
|
|
|
+ let that = this;
|
|
|
+ let ids = that.generateUniqueId(val.id)
|
|
|
+ const position = Cesium.Cartesian3.fromDegrees(lon, lat);
|
|
|
+ const entity = viewer.entities.add({
|
|
|
+ id: ids,
|
|
|
+ position, // 模型位置
|
|
|
+ billboard: {
|
|
|
+ image: uri, // 也可以是 SVG 路径,如 'icon.svg'
|
|
|
+ scale: 0.5,
|
|
|
+ pixelOffset: new Cesium.Cartesian2(0, -15),
|
|
|
+ verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
|
|
+ horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
|
|
|
+ // 模型贴地
|
|
|
+ heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
|
|
+ // 核心:使用 CallbackProperty 实现旋转动画
|
|
|
+ rotation: new Cesium.CallbackProperty(function(time) {
|
|
|
+ // 每 3 秒转一圈(可调速度)
|
|
|
+ const seconds = time.secondsOfDay;
|
|
|
+ const angle = Cesium.Math.toRadians((seconds % 3) * -360 / 3); // 每3秒一圈
|
|
|
+ return angle;
|
|
|
+ }, false),
|
|
|
+ // 确保绕中心旋转
|
|
|
+ rotationAlignment: Cesium.HeightReference.CENTER,
|
|
|
+ alignedAxis: Cesium.Cartesian3.UNIT_Z
|
|
|
+ }
|
|
|
+ });
|
|
|
+ // 创建事件处理器
|
|
|
+ const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
|
|
|
+ handler.setInputAction(function (movement) {
|
|
|
+ var position = movement.position;
|
|
|
+ var pickedObject = viewer.scene.pick(position);
|
|
|
+ if (pickedObject && pickedObject.id.id === ids) {
|
|
|
+ console.log("你点击了标签或模型!", entity);
|
|
|
+ console.log("标签或模型数据!", val);
|
|
|
+ that.modelVal = val;
|
|
|
+ // 找到实体,显示包含实体信息的弹框
|
|
|
+ that.showRightClickPopup(position, val);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }, Cesium.ScreenSpaceEventType.RIGHT_CLICK)
|
|
|
+ this.allWindEntitys.push({entity, handler})
|
|
|
+ },
|
|
|
+ // 右键展示元素
|
|
|
+ showRightClickPopup(screenPosition, val) {
|
|
|
+ // 创建或获取弹框元素
|
|
|
+ var popup = document.getElementById("rightClickPopup");
|
|
|
+ if (!popup) {
|
|
|
+ popup = document.createElement("div");
|
|
|
+ popup.id = "rightClickPopup";
|
|
|
+ popup.style.position = "absolute";
|
|
|
+ // popup.style.backgroundColor = 'white';
|
|
|
+ popup.style.border = "1px solid rgba(255, 255, 255, 0.2)";
|
|
|
+ popup.style.borderRadius = "4px";
|
|
|
+ popup.style.padding = "10px";
|
|
|
+ popup.style.boxShadow = "0 2px 10px rgba(0,0,0,0.2)";
|
|
|
+ popup.style.backdropFilter = `blur(10px)`;
|
|
|
+ popup.style.zIndex = "1000";
|
|
|
+ popup.style.pointerEvents = "auto"; // 确保弹框可交互
|
|
|
+ // 添加一个最小宽度,避免内容过短
|
|
|
+ popup.style.minWidth = "100px";
|
|
|
+ // 可选:添加箭头指向点击点
|
|
|
+ // 这需要更复杂的 CSS 或额外的 DOM 元素
|
|
|
+ document.body.appendChild(popup);
|
|
|
+ }
|
|
|
+ // <span class="popup-menu-item" data-message-type="model" style="cursor:pointer">模型解构</span>
|
|
|
+ let content = `
|
|
|
+ <div style="display: flex;gap: 10px;flex-direction: column;text-align: center;color: #fff">
|
|
|
+ <span class="popup-menu-item" data-message-type="basic" style="cursor:pointer">基本信息</span>
|
|
|
+ <span class="popup-menu-item" data-message-type="video" style="cursor:pointer">视频监控</span>
|
|
|
+ <span class="popup-menu-item" data-message-type="problem" style="cursor:pointer">故障详情</span>
|
|
|
+ </div>
|
|
|
+ `;
|
|
|
+
|
|
|
+ // 设置弹框内容
|
|
|
+ popup.innerHTML = content;
|
|
|
+
|
|
|
+ // --- 关键修正:准确计算弹框位置 ---
|
|
|
+
|
|
|
+ // 1. 获取 Cesium 画布 (Canvas) 相对于视口 (viewport) 的位置
|
|
|
+ var canvas = this.viewer.scene.canvas;
|
|
|
+ var canvasRect = canvas.getBoundingClientRect();
|
|
|
+ // console.log('Canvas Rect:', canvasRect); // 调试用
|
|
|
+
|
|
|
+ // 2. 计算弹框左上角在页面中的绝对 X 坐标
|
|
|
+ // screenPosition.x 是相对于画布左上角的 X 偏移
|
|
|
+ // canvasRect.left 是画布左上角相对于浏览器视口左上角的 X 偏移
|
|
|
+ // window.pageXOffset 是页面水平滚动的距离
|
|
|
+ var popupLeft = canvasRect.left + screenPosition.x + window.pageXOffset;
|
|
|
+
|
|
|
+ // 3. 计算弹框左上角在页面中的绝对 Y 坐标
|
|
|
+ var popupTop = canvasRect.top + screenPosition.y + window.pageYOffset;
|
|
|
+
|
|
|
+ // --- 可选:添加偏移量,避免完全覆盖鼠标指针 ---
|
|
|
+ // 例如,向下和向右偏移 10px
|
|
|
+ var offsetX = 10;
|
|
|
+ var offsetY = 10;
|
|
|
+ popupLeft += offsetX;
|
|
|
+ popupTop += offsetY;
|
|
|
+
|
|
|
+ // --- 可选:边界检查,防止弹框超出屏幕 ---
|
|
|
+ var popupWidth = popup.offsetWidth || 150; // 确保有宽度
|
|
|
+ var popupHeight = popup.offsetHeight || 50; // 确保有高度
|
|
|
+ var windowWidth = window.innerWidth;
|
|
|
+ var windowHeight = window.innerHeight;
|
|
|
+
|
|
|
+ // 如果弹框右边会超出窗口,则左移
|
|
|
+ if (popupLeft + popupWidth > windowWidth) {
|
|
|
+ popupLeft = windowWidth - popupWidth - 10; // 靠右留 10px 边距
|
|
|
+ }
|
|
|
+ // 如果弹框底边会超出窗口,则上移
|
|
|
+ if (popupTop + popupHeight > windowHeight) {
|
|
|
+ popupTop = windowHeight - popupHeight - 10; // 靠下留 10px 边距
|
|
|
+ }
|
|
|
+ // 如果左边超出 (不太可能,但安全起见)
|
|
|
+ if (popupLeft < 0) popupLeft = 10;
|
|
|
+ // 如果顶边超出
|
|
|
+ if (popupTop < 0) popupTop = 10;
|
|
|
+
|
|
|
+ // --- 设置最终位置 ---
|
|
|
+ popup.style.left = popupLeft + "px";
|
|
|
+ popup.style.top = popupTop + "px";
|
|
|
+
|
|
|
+ // console.log('Popup Position:', { left: popupLeft, top: popupTop }); // 调试用
|
|
|
+
|
|
|
+ // 显示弹框
|
|
|
+ popup.style.display = "block";
|
|
|
+
|
|
|
+ // --- 隐藏弹框的逻辑 (保持不变) ---
|
|
|
+ function hidePopup(event) {
|
|
|
+ // 检查点击是否发生在弹框内部,如果是,则不隐藏
|
|
|
+ if (event && popup.contains(event.target)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ popup.style.display = "none";
|
|
|
+ document.removeEventListener("click", hidePopup);
|
|
|
+ document.removeEventListener("keydown", keyDownHandler);
|
|
|
+ }
|
|
|
+
|
|
|
+ function keyDownHandler(event) {
|
|
|
+ if (event.key === "Escape") {
|
|
|
+ hidePopup();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ let that = this;
|
|
|
+ that.showBasicMsg = false;
|
|
|
+ that.showVideoMsg = false;
|
|
|
+ that.showProblemMsg = false;
|
|
|
+ that.showModelMsg = false;
|
|
|
+ function popupMenuItemClick(event) {
|
|
|
+ // 检查点击的是否是菜单项
|
|
|
+ if (event.target.classList.contains("popup-menu-item")) {
|
|
|
+ event.stopPropagation(); // 阻止冒泡,防止弹框立即关闭
|
|
|
+
|
|
|
+ var messageType = event.target.dataset.messageType; // 获取 data-message-type
|
|
|
+ console.log("点击了菜单项:", messageType);
|
|
|
+
|
|
|
+ // 调用您的 showMessage 函数
|
|
|
+ that.showMessage(messageType, val);
|
|
|
+ hidePopup();
|
|
|
+
|
|
|
+ // 可选:执行后关闭弹框
|
|
|
+ // hideRightClickPopup();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 移除旧的监听器(避免重复绑定)
|
|
|
+ document.removeEventListener("click", hidePopup);
|
|
|
+ document.removeEventListener("click", popupMenuItemClick);
|
|
|
+ document.removeEventListener("keydown", keyDownHandler);
|
|
|
+
|
|
|
+ // 添加新的监听器
|
|
|
+ document.addEventListener("click", hidePopup);
|
|
|
+ document.addEventListener("click", popupMenuItemClick);
|
|
|
+ document.addEventListener("keydown", keyDownHandler);
|
|
|
+ },
|
|
|
+ showMessage(type, val) {
|
|
|
+ console.log("type===>>>", type);
|
|
|
+ this.windDrawer = true;
|
|
|
+ this.windDrawerHeader = val.name + "数据详情";
|
|
|
+ if (type === "basic") {
|
|
|
+ this.windDrawerTitle = "基础信息";
|
|
|
+ this.showBasicMsg = true;
|
|
|
+ } else if (type === "video") {
|
|
|
+ this.windDrawerTitle = "视频监控";
|
|
|
+ this.showVideoMsg = true;
|
|
|
+ } else if (type === "problem") {
|
|
|
+ this.windDrawerTitle = "故障查看";
|
|
|
+ this.showProblemMsg = true;
|
|
|
+ }
|
|
|
+ // else {
|
|
|
+ // this.windDrawerTitle = '模型解构'
|
|
|
+ // this.showModelMsg = true
|
|
|
+ // }
|
|
|
+ },
|
|
|
+ showwindmodel() {
|
|
|
+ this.windDrawer = true;
|
|
|
+ this.showBasicMsg = false;
|
|
|
+ this.showVideoMsg = false;
|
|
|
+ this.showProblemMsg = false;
|
|
|
+ this.windDrawerHeader = "风机模型可视化解构说明";
|
|
|
+ this.showModelMsg = true;
|
|
|
+ },
|
|
|
+ handleClose() {
|
|
|
+ this.windDrawer = false;
|
|
|
+ this.showBasicMsg = false;
|
|
|
+ this.showVideoMsg = false;
|
|
|
+ this.showProblemMsg = false;
|
|
|
+ this.showModelMsg = false;
|
|
|
+ },
|
|
|
+ showComDia(val) {
|
|
|
+ this.showcomModelDia = val;
|
|
|
+ this.modelVal = null;
|
|
|
+ },
|
|
|
+ csceneElliposid(viewer) {
|
|
|
+ let that = this;
|
|
|
+ // 获取 scene 和 ellipsoid
|
|
|
+ var scene = viewer.scene;
|
|
|
+ var labels = viewer.scene.primitives.add(new Cesium.LabelCollection());
|
|
|
+
|
|
|
+ // 创建 ScreenSpaceEventHandler
|
|
|
+ var handler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
|
|
|
+
|
|
|
+ // 监听左键点击事件
|
|
|
+ handler.setInputAction(async (click) => {
|
|
|
+ // 获取点击位置的笛卡尔坐标
|
|
|
+ var position = click.position;
|
|
|
+ if (!position) return;
|
|
|
+
|
|
|
+ // 使用 globe.pick 获取包含地形高度的坐标
|
|
|
+ var ray = viewer.camera.getPickRay(position);
|
|
|
+ var cartesian = viewer.scene.globe.pick(ray, viewer.scene);
|
|
|
+
|
|
|
+ if (cartesian) {
|
|
|
+ // 转换为地理坐标
|
|
|
+ var cartographic = Cesium.Cartographic.fromCartesian(cartesian);
|
|
|
+ var longitude = Cesium.Math.toDegrees(cartographic.longitude);
|
|
|
+ var latitude = Cesium.Math.toDegrees(cartographic.latitude);
|
|
|
+ var height = cartographic.height;
|
|
|
+
|
|
|
+ // 格式化坐标
|
|
|
+ var text = `经度: ${longitude.toFixed(6)}°\n纬度: ${latitude.toFixed(
|
|
|
+ 6
|
|
|
+ )}°`;
|
|
|
+
|
|
|
+ // 创建一个标签
|
|
|
+ var label = labels.add({
|
|
|
+ position: cartesian,
|
|
|
+ text: text,
|
|
|
+ font: "14px monospace",
|
|
|
+ fillColor: Cesium.Color.fromCssColorString("#1d70df"),
|
|
|
+ // style: Cesium.LabelStyle.FILL_AND_OUTLINE,
|
|
|
+ // outlineColor: Cesium.Color.BLACK,
|
|
|
+ outlineWidth: 2,
|
|
|
+ verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // 标签在点的上方
|
|
|
+ pixelOffset: new Cesium.Cartesian2(0, -20), // 向上偏移一点
|
|
|
+ disableDepthTest: true, // 让标签始终可见(即使在地球背面)
|
|
|
+ scale: 0.8,
|
|
|
+ });
|
|
|
+
|
|
|
+ // 5秒后移除标签
|
|
|
+ setTimeout(function () {
|
|
|
+ labels.remove(label);
|
|
|
+ }, 5000);
|
|
|
+
|
|
|
+ console.log(
|
|
|
+ `点击坐标: ${longitude.toFixed(6)}, ${latitude.toFixed(
|
|
|
+ 6
|
|
|
+ )}, ${height.toFixed(2)}m`
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
|
|
|
+ },
|
|
|
+ generateUniqueId(prefix = 'id') {
|
|
|
+ let idCounter = 0;
|
|
|
+ return `${prefix}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}-${idCounter++}`;
|
|
|
+ },
|
|
|
+ handleInitView() {
|
|
|
+ if (this.showWindDetail) {
|
|
|
+ this.resetWindViewport()
|
|
|
+ } else {
|
|
|
+ this.resetAllStationViewport()
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 重置所有风场视角
|
|
|
+ resetAllStationViewport() {
|
|
|
+ this.viewer.camera.flyTo({
|
|
|
+ destination: Cesium.Cartesian3.fromDegrees(114.502778, 35.326667, 3000000),
|
|
|
+ orientation: {
|
|
|
+ heading: Cesium.Math.toRadians(0),
|
|
|
+ pitch: Cesium.Math.toRadians(-90),
|
|
|
+ roll: 0.0,
|
|
|
+ },
|
|
|
+ duration: 3.0,
|
|
|
+ });
|
|
|
+ },
|
|
|
+ // 重置风场中所有风机视角
|
|
|
+ resetWindViewport() {
|
|
|
+ let fromLon = this.$route.query.longitude*1
|
|
|
+ let fromLat = this.$route.query.latitude*1
|
|
|
+ let fromheight = this.$route.query.height*1
|
|
|
+ // 设置镜头到指定的经纬度(度)、高度(米)
|
|
|
+ this.viewer.camera.setView({
|
|
|
+ destination: Cesium.Cartesian3.fromDegrees(
|
|
|
+ fromLon, // 经度 (degrees)
|
|
|
+ fromLat, // 纬度 (degrees)
|
|
|
+ fromheight // 高度 (meters)
|
|
|
+ ),
|
|
|
+ orientation: {
|
|
|
+ heading : Cesium.Math.toRadians(0.0), // 偏航角 (方向,0 指向北方)
|
|
|
+ pitch : Cesium.Math.toRadians(-90.0), // 俯仰角 (-90 是垂直向下)
|
|
|
+ roll : 0.0 // 翻滚角
|
|
|
+ }
|
|
|
+ });
|
|
|
+ // 目标位置:经度、纬度、高度
|
|
|
+ const targetLon = 114.502778;
|
|
|
+ const targetLat = 35.326667;
|
|
|
+ const targetHeight = 20000;
|
|
|
+
|
|
|
+ const draggableHeightTolerance = 10000; // 允许拖拽的高度范围:20,000 ~ 30,000
|
|
|
+ const minHeight = 10000; // 最低高度
|
|
|
+ const maxHeight = 800000; // 最高高度
|
|
|
+
|
|
|
+ const allowedOffsetDegrees = 0.2; // 允许拖拽的最大偏移(经纬度)
|
|
|
+
|
|
|
+ const minLon = targetLon - allowedOffsetDegrees;
|
|
|
+ const maxLon = targetLon + allowedOffsetDegrees;
|
|
|
+ const minLat = targetLat - allowedOffsetDegrees;
|
|
|
+ const maxLat = targetLat + allowedOffsetDegrees;
|
|
|
+
|
|
|
+ let that = this
|
|
|
+ that.viewer.camera.flyTo({
|
|
|
+ destination: Cesium.Cartesian3.fromDegrees(targetLon,targetLat, targetHeight),
|
|
|
+ // orientation: {
|
|
|
+ // heading: Cesium.Math.toRadians(0),
|
|
|
+ // pitch: Cesium.Math.toRadians(-90),
|
|
|
+ // roll: 0.0,
|
|
|
+ // },
|
|
|
+ orientation: {
|
|
|
+ heading: 0, //北京天安门角度 0 上海角度 1
|
|
|
+ pitch: -0.2,
|
|
|
+ roll: 0,
|
|
|
+ },
|
|
|
+ duration: 3.0,
|
|
|
+ complete: function () {
|
|
|
+ console.log('飞入完成,启用拖拽限制逻辑');
|
|
|
+ enableHeightBasedDragControl();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ // ===== 控制逻辑:根据高度决定是否允许拖拽 =====
|
|
|
+ function enableHeightBasedDragControl() {
|
|
|
+ that.viewer.scene.preUpdate.addEventListener(function (scene, time) {
|
|
|
+ const camera = that.viewer.camera;
|
|
|
+ const posCartographic = camera.positionCartographic;
|
|
|
+
|
|
|
+ if (!posCartographic) return;
|
|
|
+
|
|
|
+ const currentHeight = posCartographic.height;
|
|
|
+ that.currentHeight = currentHeight
|
|
|
+ const currentLon = Cesium.Math.toDegrees(posCartographic.longitude);
|
|
|
+ const currentLat = Cesium.Math.toDegrees(posCartographic.latitude);
|
|
|
+
|
|
|
+ // === 第一步:限制相机高度不能超出 [10000, 100000] ===
|
|
|
+ if (currentHeight < minHeight) {
|
|
|
+ // 强制拉高到 minHeight,但保持当前水平视角
|
|
|
+ const newPos = Cesium.Cartesian3.fromDegrees(currentLon, currentLat, minHeight);
|
|
|
+ camera.setView({
|
|
|
+ destination: newPos,
|
|
|
+ orientation: {
|
|
|
+ heading: camera.heading,
|
|
|
+ pitch: camera.pitch,
|
|
|
+ roll: camera.roll
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return; // 避免后续逻辑冲突
|
|
|
+ }
|
|
|
+
|
|
|
+ if (currentHeight > maxHeight) {
|
|
|
+ const newPos = Cesium.Cartesian3.fromDegrees(currentLon, currentLat, maxHeight);
|
|
|
+ camera.setView({
|
|
|
+ destination: newPos,
|
|
|
+ orientation: {
|
|
|
+ heading: camera.heading,
|
|
|
+ pitch: camera.pitch,
|
|
|
+ roll: camera.roll
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // if (currentHeight < (maxHeight-700000)) {
|
|
|
+ // that.cancleAllLayer();
|
|
|
+ // }
|
|
|
+
|
|
|
+ // === 第二步:判断是否在“可拖拽高度区间” ===
|
|
|
+ const isInDraggableRange =
|
|
|
+ currentHeight >= (targetHeight - draggableHeightTolerance) &&
|
|
|
+ currentHeight <= (targetHeight + draggableHeightTolerance);
|
|
|
+
|
|
|
+ if (isInDraggableRange) {
|
|
|
+ // ✅ 允许拖拽,但限制在指定范围内
|
|
|
+ const correctedLon = Cesium.Math.clamp(currentLon, minLon, maxLon);
|
|
|
+ const correctedLat = Cesium.Math.clamp(currentLat, minLat, maxLat);
|
|
|
+
|
|
|
+ if (Math.abs(correctedLon - currentLon) > 1e-8 ||
|
|
|
+ Math.abs(correctedLat - currentLat) > 1e-8) {
|
|
|
+ // 越界了,纠正位置,保留当前高度和视角
|
|
|
+ camera.setView({
|
|
|
+ destination: Cesium.Cartesian3.fromDegrees(correctedLon, correctedLat, currentHeight),
|
|
|
+ orientation: {
|
|
|
+ heading: camera.heading,
|
|
|
+ pitch: camera.pitch,
|
|
|
+ roll: camera.roll
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // ❌ 不在可拖拽高度:禁止平移,强制回正到目标点
|
|
|
+ const correctedPosition = Cesium.Cartesian3.fromDegrees(
|
|
|
+ targetLon,
|
|
|
+ targetLat,
|
|
|
+ currentHeight // 保留当前缩放高度
|
|
|
+ );
|
|
|
+
|
|
|
+ camera.setView({
|
|
|
+ destination: correctedPosition,
|
|
|
+ orientation: {
|
|
|
+ heading: camera.heading,
|
|
|
+ pitch: camera.pitch,
|
|
|
+ roll: camera.roll
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ coverOnChange(val) {
|
|
|
+ if (val.value === "风场") {
|
|
|
+ this.switchWindLayer(val.check);
|
|
|
+ } else if (val.value === "云层") {
|
|
|
+ this.switchCloudLayer(val.check);
|
|
|
+ } else if (val.value === "降雨") {
|
|
|
+ this.switchRainLayer(val.check);
|
|
|
+ } else if (val.value === "温度") {
|
|
|
+ this.switchTemperatureLayerr(val.check);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 提供控制函数以便在需要时停止循环
|
|
|
+ stopCycling(intervalId) {
|
|
|
+ if (intervalId) {
|
|
|
+ clearInterval(intervalId);
|
|
|
+ intervalId = null;
|
|
|
+ console.log("循环已停止");
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 切换风场图显隐
|
|
|
+ switchWindLayer() {
|
|
|
+ this.viewer.scene.screenSpaceCameraController.enableZoom = true;
|
|
|
+ if (this.rainLayer || this.rainImagesLayer.length > 0) {
|
|
|
+ this.removeRainLayer();
|
|
|
+ this.stopCycling(this.rainintervalId);
|
|
|
+ }
|
|
|
+ if (this.cloudLayer || this.cloudImagesLayer.length > 0) {
|
|
|
+ this.removeCloudLayer();
|
|
|
+ this.stopCycling(this.cloudintervalId);
|
|
|
+ }
|
|
|
+ if (this.temperatureLayer || this.tempImagesLayer.length > 0) {
|
|
|
+ this.removeTemperatureLayer();
|
|
|
+ this.stopCycling(this.tempintervalId);
|
|
|
+ }
|
|
|
+ if (this.windLayer) {
|
|
|
+ this.removeWindLayer();
|
|
|
+ } else {
|
|
|
+ this.showWindLayer();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 添加风场图
|
|
|
+ async showWindLayer() {
|
|
|
+ if (!this.windLayer) {
|
|
|
+ this.windLayer = new WindLayer(windGridData, {
|
|
|
+ particleSize: 2.0,
|
|
|
+ particleOpacity: 0.6,
|
|
|
+ particleSpeed: 0.01,
|
|
|
+ maxVelocity: 25,// 风速最大值(用于颜色映射和速度缩放)
|
|
|
+ minVelocity: 0, // 风速最小值阈值(低于此值不显示粒子)
|
|
|
+ colorScale: [
|
|
|
+ "rgb(36,104, 180)",
|
|
|
+ "rgb(60,157, 194)",
|
|
|
+ "rgb(128,205,193)",
|
|
|
+ "rgb(151,218,168)",
|
|
|
+ "rgb(198,231,181)",
|
|
|
+ "rgb(238,247,217)",
|
|
|
+ "rgb(255,238,159)",
|
|
|
+ "rgb(252,217,125)",
|
|
|
+ "rgb(255,182,100)",
|
|
|
+ "rgb(252,150,75)",
|
|
|
+ "rgb(250,112,52)",
|
|
|
+ "rgb(245,64,32)",
|
|
|
+ "rgb(237,45,28)",
|
|
|
+ "rgb(220,24,32)",
|
|
|
+ "rgb(180,0,35)",
|
|
|
+ ],// 颜色强度缩放
|
|
|
+ frameRate: 15,
|
|
|
+ fadeOpacity: 0.995,
|
|
|
+ particleAge: 150,
|
|
|
+ maxAge: 60,
|
|
|
+ globalAlpha: 0.8,
|
|
|
+ velocityScale: 1 / 30,// 粒子移动速度缩放因子(控制动画快慢)
|
|
|
+ paths: 2000,
|
|
|
+ lineWidth: 2,
|
|
|
+ });
|
|
|
+ this.windLayer.addTo(this.viewer);
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 切换卫星云图显隐
|
|
|
+ switchCloudLayer(val) {
|
|
|
+ if (this.windLayer) {
|
|
|
+ this.removeWindLayer();
|
|
|
+ }
|
|
|
+ if (this.rainLayer || this.rainImagesLayer.length > 0) {
|
|
|
+ this.removeRainLayer();
|
|
|
+ this.stopCycling(this.rainintervalId);
|
|
|
+ }
|
|
|
+ if (this.temperatureLayer || this.tempImagesLayer.length > 0) {
|
|
|
+ this.removeTemperatureLayer();
|
|
|
+ this.stopCycling(this.tempintervalId);
|
|
|
+ }
|
|
|
+ if (!val || this.cloudLayer || this.cloudImagesLayer.length > 0) {
|
|
|
+ this.removeCloudLayer();
|
|
|
+ this.stopCycling(this.cloudintervalId);
|
|
|
+ } else {
|
|
|
+ this.showCloudLayer();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 切换降雨图显隐
|
|
|
+ switchRainLayer() {
|
|
|
+ this.viewer.scene.screenSpaceCameraController.enableZoom = true;
|
|
|
+ if (this.windLayer) {
|
|
|
+ this.removeWindLayer();
|
|
|
+ }
|
|
|
+ if (this.cloudLayer || this.cloudImagesLayer.length > 0) {
|
|
|
+ this.removeCloudLayer();
|
|
|
+ this.stopCycling(this.cloudintervalId);
|
|
|
+ }
|
|
|
+ if (this.temperatureLayer || this.tempImagesLayer.length > 0) {
|
|
|
+ this.removeTemperatureLayer();
|
|
|
+ this.stopCycling(this.tempintervalId);
|
|
|
+ }
|
|
|
+ if (this.rainLayer || this.rainImagesLayer.length > 0) {
|
|
|
+ this.removeRainLayer();
|
|
|
+ this.stopCycling(this.rainintervalId);
|
|
|
+ } else {
|
|
|
+ this.showRainLayer();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 切换温度图显隐
|
|
|
+ switchTemperatureLayerr() {
|
|
|
+ this.viewer.scene.screenSpaceCameraController.enableZoom = true;
|
|
|
+ if (this.windLayer) {
|
|
|
+ this.removeWindLayer();
|
|
|
+ }
|
|
|
+ if (this.cloudLayer || this.cloudImagesLayer.length > 0) {
|
|
|
+ this.removeCloudLayer();
|
|
|
+ this.stopCycling(this.cloudintervalId);
|
|
|
+ }
|
|
|
+ if (this.rainLayer || this.rainImagesLayer.length > 0) {
|
|
|
+ this.removeRainLayer();
|
|
|
+ this.stopCycling(this.rainintervalId);
|
|
|
+ }
|
|
|
+ if (this.temperatureLayer || this.tempImagesLayer.length > 0) {
|
|
|
+ this.removeTemperatureLayer();
|
|
|
+ this.stopCycling(this.tempintervalId);
|
|
|
+ } else {
|
|
|
+ this.showTemperatureLayer();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 显示云图
|
|
|
+ showCloudLayer() {
|
|
|
+ const imageUrls = [];
|
|
|
+ cloudJson.forEach((it) => {
|
|
|
+ imageUrls.push("/public/static" + it.path);
|
|
|
+ });
|
|
|
+ this.showeveryTypeImagesLayer(
|
|
|
+ imageUrls,
|
|
|
+ this.cloudintervalId,
|
|
|
+ this.cloudImagesLayer
|
|
|
+ );
|
|
|
+ },
|
|
|
+ //显示降雨图
|
|
|
+ showRainLayer() {
|
|
|
+ const imageUrls = [];
|
|
|
+ rainJson.forEach((it) => {
|
|
|
+ imageUrls.push("/public/static" + it.path);
|
|
|
+ });
|
|
|
+ this.showeveryTypeImagesLayer(
|
|
|
+ imageUrls,
|
|
|
+ this.rainintervalId,
|
|
|
+ this.rainImagesLayer
|
|
|
+ );
|
|
|
+ this.csceneElliposid(this.viewer, "rain");
|
|
|
+ },
|
|
|
+ //显示温度图
|
|
|
+ showTemperatureLayer() {
|
|
|
+ const imageUrls = [];
|
|
|
+ tempJson.forEach((it) => {
|
|
|
+ imageUrls.push("/public/static" + it.path);
|
|
|
+ });
|
|
|
+ this.showeveryTypeImagesLayer(
|
|
|
+ imageUrls,
|
|
|
+ this.tempintervalId,
|
|
|
+ this.tempImagesLayer
|
|
|
+ );
|
|
|
+ this.csceneElliposid(this.viewer, "temp");
|
|
|
+ },
|
|
|
+ async showeveryTypeImagesLayer(imageUrls, intervalId, ImagesLayers) {
|
|
|
+ // 存储所有图片图层的数组
|
|
|
+ let imageLayers = [];
|
|
|
+ // 当前显示的图片索引
|
|
|
+ let currentImageIndex = -1; // 初始为-1,表示没有图片显示
|
|
|
+
|
|
|
+ // 创建所有图片图层并添加到Viewer,初始时全部隐藏
|
|
|
+ await imageUrls.forEach((url) => {
|
|
|
+ const provider = new Cesium.SingleTileImageryProvider({
|
|
|
+ url: url,
|
|
|
+ // url: URL.createObjectURL(url),
|
|
|
+ rectangle: Cesium.Rectangle.fromDegrees(-180.0, -90.0, 180.0, 90.0), // 全球覆盖
|
|
|
+ tileWidth: 1440, // 根据你的图片实际宽度修改
|
|
|
+ tileHeight: 721,
|
|
|
+ // 如果你的图片只覆盖特定区域,请修改rectangle参数
|
|
|
+ });
|
|
|
+ const Layer = this.viewer.imageryLayers.addImageryProvider(provider);
|
|
|
+ Layer.alpha = 0.8; // 透明度
|
|
|
+ Layer.brightness = 1; // 亮度
|
|
|
+ Layer.contrast = 1; // 对比度
|
|
|
+ Layer.show = false; // 初始隐藏
|
|
|
+ imageLayers.push(Layer);
|
|
|
+ ImagesLayers.push(Layer);
|
|
|
+ });
|
|
|
+ function showNextImage() {
|
|
|
+ // 隐藏当前图片
|
|
|
+ if (currentImageIndex >= 0 && currentImageIndex < imageLayers.length) {
|
|
|
+ imageLayers[currentImageIndex].show = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算下一张图片的索引
|
|
|
+ currentImageIndex = (currentImageIndex + 1) % imageLayers.length;
|
|
|
+
|
|
|
+ // 显示下一张图片
|
|
|
+ imageLayers[currentImageIndex].show = true;
|
|
|
+ // imageLayers[currentImageIndex + 1].show = true;
|
|
|
+ console.log("当前显示图片: " + imageUrls[currentImageIndex]);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置切换间隔(毫秒),例如每5秒切换一次
|
|
|
+ const intervalMs = 5000;
|
|
|
+ intervalId = setInterval(showNextImage, intervalMs);
|
|
|
+
|
|
|
+ // 初始显示第一张图片
|
|
|
+ showNextImage();
|
|
|
+ },
|
|
|
+ // 移除风场图
|
|
|
+ removeWindLayer() {
|
|
|
+ if (this.windLayer) {
|
|
|
+ // this.windLayer.destroy();
|
|
|
+ this.windLayer.remove();
|
|
|
+ this.windLayer = null;
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 移除卫星云图
|
|
|
+ removeCloudLayer() {
|
|
|
+ if (this.cloudLayer) {
|
|
|
+ this.tagMsg = null;
|
|
|
+ this.viewer.imageryLayers.remove(this.cloudLayer);
|
|
|
+ this.cloudLayer = null;
|
|
|
+ }
|
|
|
+ if (this.cloudImagesLayer.length > 0) {
|
|
|
+ this.cloudImagesLayer.forEach((it) => {
|
|
|
+ this.viewer.imageryLayers.remove(it);
|
|
|
+ });
|
|
|
+ this.cloudImagesLayer = [];
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this.imageryProviderV) {
|
|
|
+ this.viewer.imageryLayers.remove(this.imageryProviderV);
|
|
|
+ this.imageryProviderV = null;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 移除降雨图
|
|
|
+ removeRainLayer() {
|
|
|
+ if (this.rainLayer) {
|
|
|
+ this.tagMsg = null;
|
|
|
+ this.viewer.imageryLayers.remove(this.rainLayer);
|
|
|
+ this.rainLayer = null;
|
|
|
+ this.setMapImageryProvider();
|
|
|
+ this.handlerAction.removeInputAction(
|
|
|
+ Cesium.ScreenSpaceEventType.LEFT_CLICK
|
|
|
+ );
|
|
|
+ }
|
|
|
+ if (this.rainImagesLayer.length > 0) {
|
|
|
+ this.rainImagesLayer.forEach((it) => {
|
|
|
+ this.viewer.imageryLayers.remove(it);
|
|
|
+ });
|
|
|
+ this.rainImagesLayer = [];
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this.imageryProviderV) {
|
|
|
+ this.viewer.imageryLayers.remove(this.imageryProviderV);
|
|
|
+ this.imageryProviderV = null;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 移除温度图
|
|
|
+ removeTemperatureLayer() {
|
|
|
+ if (this.temperatureLayer) {
|
|
|
+ this.tagMsg = null;
|
|
|
+ this.viewer.imageryLayers.remove(this.temperatureLayer);
|
|
|
+ this.temperatureLayer = null;
|
|
|
+ this.setMapImageryProvider();
|
|
|
+ this.handlerAction.removeInputAction(
|
|
|
+ Cesium.ScreenSpaceEventType.LEFT_CLICK
|
|
|
+ );
|
|
|
+ }
|
|
|
+ if (this.tempImagesLayer.length > 0) {
|
|
|
+ this.tempImagesLayer.forEach((it) => {
|
|
|
+ this.viewer.imageryLayers.remove(it);
|
|
|
+ });
|
|
|
+ this.tempImagesLayer = [];
|
|
|
+ }
|
|
|
+ if (this.imageryProviderV) {
|
|
|
+ this.viewer.imageryLayers.remove(this.imageryProviderV);
|
|
|
+ this.imageryProviderV = null;
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ //取消所有图层加载
|
|
|
+ cancleAllLayer() {
|
|
|
+ if (this.windLayer) {
|
|
|
+ this.removeWindLayer();
|
|
|
+ }
|
|
|
+ if (this.rainLayer || this.rainImagesLayer.length > 0) {
|
|
|
+ this.removeRainLayer();
|
|
|
+ this.stopCycling(this.rainintervalId);
|
|
|
+ }
|
|
|
+ if (this.cloudLayer || this.cloudImagesLayer.length > 0) {
|
|
|
+ this.removeCloudLayer();
|
|
|
+ this.stopCycling(this.cloudintervalId);
|
|
|
+ }
|
|
|
+ if (this.temperatureLayer || this.tempImagesLayer.length > 0) {
|
|
|
+ this.removeTemperatureLayer();
|
|
|
+ this.stopCycling(this.tempintervalId);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ switchLayer() {
|
|
|
+ this.$router.push({
|
|
|
+ path: "/",
|
|
|
+ });
|
|
|
+ },
|
|
|
+ menuComTSty(val) {
|
|
|
+ this.menuComTStyB = val;
|
|
|
+ },
|
|
|
+
|
|
|
+ },
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="less" scoped>
|
|
|
+.mapBox {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ position: relative;
|
|
|
+ box-sizing: content-box;
|
|
|
+ overflow: hidden;
|
|
|
+
|
|
|
+ .menuComT {
|
|
|
+ position: fixed;
|
|
|
+ bottom: 400px;
|
|
|
+ left: 20px;
|
|
|
+ }
|
|
|
+ .menuComTSty {
|
|
|
+ position: fixed;
|
|
|
+ bottom: 20px;
|
|
|
+ left: 20px;
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|
|
|
+<style lang="less">
|
|
|
+.el-overlay {
|
|
|
+ background-color: transparent !important;
|
|
|
+ .windModelDrawer {
|
|
|
+ width: 80% !important;
|
|
|
+ backdrop-filter: blur(15px) !important;
|
|
|
+ background: rgba(255, 255, 255, .8) !important;
|
|
|
+ border-radius: 10px 0 0 10px !important;
|
|
|
+ .el-drawer__body {
|
|
|
+ overflow: hidden;
|
|
|
+ padding-top: 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+.windDrawerCla {
|
|
|
+ .line {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: row;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ width: 100%;
|
|
|
+ margin-bottom: 10px;
|
|
|
+
|
|
|
+ .leftContent {
|
|
|
+ width: 242px;
|
|
|
+ height: 41px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ background: url("@/assets/cesiumImg/title_left_bg.png") no-repeat;
|
|
|
+
|
|
|
+ span {
|
|
|
+ font-size: 16px;
|
|
|
+ font-family: Microsoft YaHei;
|
|
|
+ font-weight: 400;
|
|
|
+ color: #ffffff;
|
|
|
+ margin-left: 25px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .jcxx,
|
|
|
+ .gzck {
|
|
|
+ height: 100%;
|
|
|
+ }
|
|
|
+ .spjk,
|
|
|
+ .third {
|
|
|
+ height: 80vh;
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|