windMap3D.vue 68 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063
  1. <template>
  2. <div id="loading" class="dataLoading" v-if="loading">
  3. <span class="loadText">数据加载中...</span>
  4. </div>
  5. <div class="mapBox">
  6. <div id="cesiumContainer" style="width: 100%; height: 100vh"></div>
  7. <div :class="!menuComTStyB ? 'menuComTSty' : 'menuComT'" v-if="0">
  8. <menuCom
  9. :showbasemap="false"
  10. :showwindspeed="false"
  11. :showcloud="false"
  12. :showrainfall="false"
  13. :showtemperature="false"
  14. :showcity="false"
  15. :showwind="false"
  16. :showexit="true"
  17. :showwindModel="true"
  18. @handleWindModel="showwindmodel"
  19. @handleInit="handleInitView"
  20. @handleExit="switchLayer"
  21. />
  22. </div>
  23. <el-drawer
  24. v-model="windDrawer"
  25. direction="rtl"
  26. class="windModelDrawer"
  27. :before-close="handleClose"
  28. >
  29. <template #header>
  30. <h3 style="font-weight: bold;color: #fff">{{ windDrawerHeader }}</h3>
  31. </template>
  32. <template #default>
  33. <div class="windDrawerCla">
  34. <div class="line" v-if="!showModelMsg">
  35. <div class="leftContent">
  36. <span>{{ windDrawerTitle }}</span>
  37. </div>
  38. </div>
  39. <div class="jcxx" v-if="showBasicMsg">
  40. <windHome :modelValItem="modelVal" />
  41. </div>
  42. <div class="spjk" v-if="showVideoMsg">
  43. <windVideo />
  44. </div>
  45. <div class="gzck" v-if="showProblemMsg">
  46. <windPro />
  47. </div>
  48. <div class="third" v-if="showModelMsg">
  49. <ModelUnpack
  50. :modelUnpackType="modelUnpackType"
  51. :showModelMsg="showModelMsg"
  52. />
  53. </div>
  54. </div>
  55. </template>
  56. </el-drawer>
  57. <comModelDialog
  58. :showcomModelDia="showcomModelDia"
  59. :modelVal="modelVal"
  60. @showDia="showComDia"
  61. />
  62. <!-- <windView
  63. v-if="showWindDetail"
  64. :cesiumViewer="viewer"
  65. @coverOnChange="coverOnChange"
  66. :currentHeight="currentHeight"
  67. :fjLonLatJsonArr="fjLonLatJsonArr.data"
  68. @showDetail="menuComTSty"
  69. @backStations="backStations"
  70. @resetChangeWind="resetChangeWind"
  71. @initView="resetWindViewport"
  72. /> -->
  73. <windView2
  74. v-if="showWindDetail"
  75. :cesiumViewer="viewer"
  76. @coverOnChange="coverOnChange"
  77. :currentHeight="currentHeight"
  78. :fjLonLatJsonArr="fjLonLatJsonArr.data"
  79. @showDetail="menuComTSty"
  80. @resetChangeWind="resetChangeWind"
  81. />
  82. </div>
  83. </template>
  84. <script>
  85. import * as Cesium from "../../Cesium";
  86. import "../../Cesium/Widgets/widgets.css";
  87. import fjMYLonLatJson from "../fjLonLatJson/fj_MY.json";
  88. import fjWHZLonLatJson from "../fjLonLatJson/fj_WHZ.json"; //京能旺海庄
  89. import fjYPLLonLatJson from "../fjLonLatJson/fj_YPL.json"; //京能营盘梁
  90. import fjSMSLonLatJson from "../fjLonLatJson/fj_SMS.json"; //京能苏木山
  91. import wsRes from "../fjLonLatJson/wsRes.json"; // 模拟 ws 返回数据
  92. import mountainPosJson from "../fjLonLatJson/mountainPos.json";
  93. import allStationJson from "./allStationJson.json";
  94. import basicGeoJson from "../../assets/geoJson/basic.json";
  95. import comModelDialog from "@/components/comModelDialog.vue";
  96. import windView from "./windView.vue";
  97. import windView2 from "./windView2.vue";
  98. import menuCom from "../menuCom.vue";
  99. import dayjs from "dayjs";
  100. import cloudJson from "/public/static/exportData/cloud/layer.json";
  101. import rainJson from "/public/static/exportData/rain/layer.json";
  102. import tempJson from "/public/static/exportData/tmp/layer.json";
  103. //风场展示图标
  104. import fc from "@/assets/windimgs/fanSvg/fc.png";
  105. //火电展示图标
  106. import hd from "@/assets/windimgs/fanSvg/hd.png";
  107. //光伏电站展示图标
  108. import gf from "@/assets/windimgs/fanSvg/gf.png";
  109. import benchmark from "@/assets/cesiumImg/benchmark.png";
  110. import windHome from "@/components/windHome/index.vue";
  111. import windVideo from "@/components/windVideo/index.vue"
  112. import windPro from "@/components/windProDetail/windProblem.vue";
  113. import ModelUnpack from "@/components/modelUnpack.vue";
  114. import { WindLayer } from "cesium-wind";
  115. import windGridData from "./windGridData.json";
  116. import { HeightReference } from "cesium";
  117. import AdvancedBillboardGenerator from "@/tools/lightsign.js";
  118. import webSocketService from "@/tools/ws.js";
  119. // const advancedGenerator = new AdvancedBillboardGenerator();
  120. export default {
  121. name: "windMap3D",
  122. components: {
  123. comModelDialog,
  124. windView,
  125. menuCom,
  126. windHome,
  127. windVideo,
  128. windPro,
  129. ModelUnpack,
  130. windView2
  131. },
  132. data() {
  133. return {
  134. benchmark,
  135. restLatLon: [
  136. {
  137. longitude: 114.48789,
  138. latitude: 35.32916,
  139. name: "MYFDC",
  140. },
  141. {
  142. longitude: 112.88355172,
  143. latitude: 40.46617836,
  144. name: "JNWHZ",
  145. },
  146. {
  147. longitude: 112.5270545,
  148. latitude: 40.35920334,
  149. name: "JNYPL",
  150. },
  151. {
  152. longitude: 112.69922452,
  153. latitude: 40.31857399,
  154. name: "JNSMS",
  155. },
  156. ],
  157. fjLonLatJsonArr: [],
  158. loading: true,
  159. viewer: null,
  160. windLayer: null, // 风场图
  161. windLayerTimmer: null, // 风场图计时器
  162. cloudImagesLayer: [], // 卫星云图
  163. cloudLayer: null, // 卫星云图
  164. cloudintervalId: null,
  165. rainImagesLayer: [], // 降雨图
  166. rainLayer: null, // 降雨图
  167. rainintervalId: null, // 降雨图
  168. tempImagesLayer: [], //温度图
  169. temperatureLayer: null, //温度图
  170. tempintervalId: null, //温度图
  171. showcomModelDia: false,
  172. currentHeight: 0,
  173. modelVal: null,
  174. menuComTStyB: false,
  175. modelUnpackType: "fengji",
  176. windDrawer: false,
  177. windDrawerTitle: "",
  178. windDrawerHeader: "",
  179. showBasicMsg: false,
  180. showVideoMsg: false,
  181. showProblemMsg: false,
  182. showModelMsg: true,
  183. showWindDetail: true,
  184. allStationentitys: [],
  185. allWindEntitys: [],
  186. urlTiles: "/static/tiles/{z}/{x}/{y}.jpg",
  187. verticalExaggeration: 1.0,
  188. infoBoxes: [],
  189. entityxy: {},
  190. entityxyArray: [],
  191. isFakeData: false,
  192. fakeDataTimmer: null,
  193. isFirstAdd: true,
  194. zbLabelList: [],
  195. };
  196. },
  197. mounted() {
  198. this.initCesium();
  199. setTimeout(() => {
  200. this.loading = false;
  201. }, 1000);
  202. },
  203. unmounted() {
  204. clearInterval(this.fakeDataTimmer);
  205. this.fakeDataTimmer = null;
  206. this.viewer.clock.onTick.removeEventListener(this.clockTickHandler);
  207. },
  208. methods: {
  209. clockTickHandler() {
  210. if (
  211. (this.viewer.clock.currentTime.secondsOfDay > 0 &&
  212. this.viewer.clock.currentTime.secondsOfDay < 45000) ||
  213. this.viewer.clock.currentTime.secondsOfDay > 78000
  214. ) {
  215. if (!this.viewer.scene.light) {
  216. this.viewer.scene.light = new Cesium.DirectionalLight({
  217. direction: new Cesium.Cartesian3(0, -1, 0),
  218. intensity: 0.5, // 增加光源强度,默认1.0
  219. });
  220. }
  221. } else {
  222. if (this.viewer.scene.light) {
  223. this.viewer.scene.light = null;
  224. }
  225. }
  226. },
  227. async initCesium() {
  228. const box = document.getElementById("cesiumContainer");
  229. const viewer = new Cesium.Viewer(box, {
  230. // terrainProvider: Cesium.createWorldTerrain(),
  231. baseLayerPicker: false, //是否显示底图切换按钮
  232. animation: false, //是否显示动画控制按钮
  233. vrButton: false,
  234. geocoder: false, //是否显示地理编码按钮
  235. homeButton: false, //是否显示地图导航按钮
  236. infoBox: false,
  237. sceneModePicker: false, //是否显示场景模式切换按钮
  238. selectionIndicator: false,
  239. timeline: false, //是否显示时间轴
  240. fullscreenButton: false, //是否显示全屏按钮
  241. navigationHelpButton: false,
  242. shouldAnimate: true,
  243. imageryProvider: false, //控制默认底图的显示
  244. // terrain: Cesium.Terrain.fromWorldTerrain(),
  245. });
  246. viewer._cesiumWidget._creditContainer.style.display = "none";
  247. // this.csceneElliposid(viewer)
  248. const utcStart = dayjs(
  249. `${dayjs().format("YYYY-MM-DD")} 00:00:00`
  250. ).subtract(4, "hour");
  251. const startTime = Cesium.JulianDate.fromDate(new Date(utcStart));
  252. const utcStop = dayjs(
  253. `${dayjs().format("YYYY-MM-DD")} 23:59:59`
  254. ).subtract(4, "hour");
  255. const stopTime = Cesium.JulianDate.fromDate(new Date(utcStop));
  256. const utcNow = dayjs().subtract(4, "hour");
  257. const nowTime = Cesium.JulianDate.fromDate(new Date(utcNow));
  258. // dayjs().format("YYYY-MM-DD")} 00:00:00
  259. viewer.clock.startTime = startTime;
  260. viewer.clock.stopTime = stopTime;
  261. viewer.clock.currentTime = nowTime;
  262. viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP; // 循环模式:到达结束时跳回开始
  263. // viewer.clock.multiplier = 10000; // 时间流逝速度(加快模拟)
  264. viewer.clock.shouldAnimate = true;
  265. this.viewer = viewer;
  266. this.viewer.clock.onTick.addEventListener(this.clockTickHandler);
  267. // 展示场站
  268. // this.showAllStation(viewer)
  269. // 展示风机
  270. this.initCesiumTerrain();
  271. this.initCesiumBaseMapImage();
  272. // this.initGeoJsonData();
  273. this.showWindFromStation(viewer);
  274. },
  275. // 初始化底图
  276. async initCesiumBaseMapImage() {
  277. const imageryProvider = await new Cesium.UrlTemplateImageryProvider({
  278. // url: "http://localhost:3007/tiles/map/{z}/{x}/{y}",
  279. url:
  280. import.meta.env.VITE_APP_ENV_NAME === "dev"
  281. ? "https://webst02.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}"
  282. : "/static/ditu/{z}/{x}/{y}.png",
  283. // minimumLevel: 11,
  284. maximumLevel: 24,
  285. credit: "影像地图",
  286. });
  287. imageryProvider.alpha = 0.55; // 透明度
  288. imageryProvider.brightness = 1; // 亮度
  289. imageryProvider.contrast = 1; // 对比度
  290. this.viewer.imageryLayers.addImageryProvider(imageryProvider);
  291. },
  292. // 初始化 geoJson 数据
  293. async initGeoJsonData() {
  294. // 创建GeoJSON数据源
  295. await new Cesium.GeoJsonDataSource.load(basicGeoJson, {
  296. stroke: Cesium.Color.GRAY, // 边界线颜色
  297. fill: Cesium.Color.BLACK.withAlpha(0), // 填充颜色
  298. strokeWidth: 1, // 边界线宽度
  299. markerSymbol: "?", // 点要素的符号
  300. clampToGround: true, // 贴地
  301. }).then((dataSource) => {
  302. // 添加到视图
  303. this.viewer.dataSources.add(dataSource);
  304. var entities = dataSource.entities.values;
  305. for (let i = 0; i < entities.length; i++) {
  306. let entity = entities[i];
  307. entity.polygon.hierarchy.getValue(Cesium.JulianDate.now()).positions;
  308. //单独设置线条样式
  309. var positions = entity.polygon.hierarchy._value.positions;
  310. entity.polyline = {
  311. positions: positions,
  312. width: 2,
  313. outline: false,
  314. // clampToGround: true,
  315. show: true,
  316. };
  317. entity.wall = {
  318. positions: entity.polyline.positions.getValue(
  319. Cesium.JulianDate.now()
  320. ),
  321. // maximumHeights: new Array(
  322. // entity.polyline.positions.getValue(Cesium.JulianDate.now()).length
  323. // ).fill(-3000), // 向下30米
  324. minimumHeights: new Array(
  325. entity.polyline.positions.getValue(Cesium.JulianDate.now()).length
  326. ).fill(-50000), // 向下50米
  327. material: Cesium.Color.BLACK.withAlpha(0.1),
  328. outline: false,
  329. // outlineColor: Cesium.Color.WHITE,
  330. };
  331. }
  332. // 添加中文标签图层
  333. const labelLayer = new Cesium.LabelCollection();
  334. this.viewer.scene.primitives.add(labelLayer);
  335. const cities = [];
  336. basicGeoJson?.features?.forEach((ele) => {
  337. if (Array.isArray(ele.properties.centroid)) {
  338. const name = ele.properties.name;
  339. const lon = ele.properties.centroid[0];
  340. const lat = ele.properties.centroid[1];
  341. cities.push({ name, lon, lat });
  342. }
  343. });
  344. cities.forEach((city, index) => {
  345. labelLayer.add({
  346. id: index,
  347. name: "cityLabel",
  348. position: Cesium.Cartesian3.fromDegrees(city.lon, city.lat, 10),
  349. text: city.name,
  350. font: 'bold 14px "Microsoft YaHei", sans-serif',
  351. fillColor: Cesium.Color.fromCssColorString("#000"),
  352. outlineColor: Cesium.Color.WHITE,
  353. outlineWidth: 2,
  354. style: Cesium.LabelStyle.FILL_AND_OUTLINE,
  355. pixelOffset: new Cesium.Cartesian2(0, 0), // 设置为0
  356. horizontalOrigin: Cesium.HorizontalOrigin.CENTER, // 水平居中
  357. verticalOrigin: Cesium.VerticalOrigin.CENTER, // 垂直居中
  358. });
  359. });
  360. this.labelLayer = labelLayer;
  361. // this.csceneElliposid(this.viewer, null)
  362. // this.resetViewport1();
  363. });
  364. },
  365. // 初始化地形
  366. async initCesiumTerrain() {
  367. const terrainProvider = await Cesium.CesiumTerrainProvider.fromUrl(
  368. // "http://localhost:3007/tiles/dixing",
  369. "/static/dixing",
  370. {
  371. requestWaterMask: true, // 如果需要水效果,设置为true
  372. requestVertexNormals: true, // 如果需要光照效果,设置为true
  373. requestMetadata: true, // 请求元数据
  374. minimumLevel: 4,
  375. maximumLevel: 12,
  376. }
  377. );
  378. this.viewer.scene.verticalExaggeration = this.verticalExaggeration;
  379. // this.viewer.scene.verticalExaggerationRelativeHeight = 2400.0;
  380. this.viewer.terrainProvider = terrainProvider;
  381. // const terrainProvider = new Cesium.CesiumTerrainProvider({
  382. // url: "http://localhost:3007/tiles/dixing", // 地形数据所在目录的URL
  383. // requestWaterMask: true, // 如果需要水效果,设置为true
  384. // requestVertexNormals: true, // 如果需要光照效果,设置为true
  385. // minimumLevel: 10,
  386. // maximumLevel: 15,
  387. // });
  388. // this.viewer.terrainProvider = terrainProvider;
  389. // console.log(111, terrainProvider);
  390. // const imageryProvider = new Cesium.UrlTemplateImageryProvider({
  391. // url: "http://localhost:3007/tiles/dixing/{z}/{x}/{y}",
  392. // // url: "/static/dixing/{z}/{x}/{y}.terrain",
  393. // credit: "地形数据",
  394. // });
  395. // this.viewer.imageryLayers.addImageryProvider(imageryProvider);
  396. // this.viewer.scene.globe.depthTestAgainstTerrain = true; //地形遮挡效果开关,打开后 地形会遮挡看不到的区域
  397. this.viewer.scene.globe.enableLighting = true; //对大气和雾启用动态照明效
  398. },
  399. backStations() {
  400. this.showWindDetail = false;
  401. this.allWindEntitys.forEach(({ entity, handler }) => {
  402. this.viewer.entities.remove(entity); // 移除实体
  403. if (!handler.isDestroyed()) {
  404. handler.destroy(); // 销毁事件处理器(关键!)
  405. }
  406. });
  407. this.allWindEntitys = [];
  408. // if (this.viewer && this.viewer.destroy) {
  409. // this.viewer.destroy();
  410. // this.viewer = null
  411. // }
  412. // this.initCesium();
  413. this.showAllStation(this.viewer);
  414. },
  415. resetChangeWind(data) {
  416. let that = this;
  417. that.viewer.camera.flyTo({
  418. destination: Cesium.Cartesian3.fromDegrees(
  419. data.longitude,
  420. data.latitude - 0.02,
  421. 1000
  422. ),
  423. orientation: {
  424. heading: Cesium.Math.toRadians(0),
  425. pitch: Cesium.Math.toRadians(-30),
  426. roll: 0.0,
  427. },
  428. duration: 3.0,
  429. });
  430. },
  431. // 展示所有风场
  432. showAllStation(viewer) {
  433. allStationJson.station.forEach((e, index) => {
  434. if (e.energytype === "Wind") {
  435. this.showStationFn(viewer, e, index, fc);
  436. } else if (e.energytype === "Fire") {
  437. this.showStationFn(viewer, e, index, hd);
  438. } else {
  439. this.showStationFn(viewer, e, index, gf);
  440. }
  441. });
  442. this.resetAllStationViewport();
  443. },
  444. // 根据状态展示不同颜色风机贴图
  445. showStationFn(viewer, e, index, images) {
  446. const position = Cesium.Cartesian3.fromDegrees(e.longitude, e.latitude);
  447. const entity = viewer.entities.add({
  448. id: index,
  449. position, // 模型位置
  450. billboard: {
  451. image: images, // 也可以是 SVG 路径,如 'icon.svg'
  452. scale: 0.5,
  453. verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
  454. horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
  455. // 模型贴地
  456. heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
  457. },
  458. label: {
  459. text: e.plantname,
  460. font: "14px sans-serif",
  461. fillColor: Cesium.Color.fromBytes(255, 255, 255),
  462. heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
  463. },
  464. });
  465. let that = this;
  466. const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
  467. handler.setInputAction(function (movement) {
  468. var position = movement.position;
  469. var pickedObject = viewer.scene.pick(position);
  470. if (pickedObject && pickedObject.id.id === index) {
  471. console.log("你点击了标签或模型!", entity);
  472. console.log("选中风场或新能源场", e.plantname);
  473. that.showWindDetail = true;
  474. that.allStationentitys.forEach(({ entity, handler }) => {
  475. viewer.entities.remove(entity); // 移除实体
  476. if (!handler.isDestroyed()) {
  477. handler.destroy(); // 销毁事件处理器(关键!)
  478. }
  479. });
  480. that.allStationentitys = [];
  481. that.showWindFromStation(viewer);
  482. }
  483. }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
  484. that.allStationentitys.push({ entity, handler });
  485. },
  486. // 展示所选风场的风机
  487. showWindFromStation(viewer) {
  488. let stationName = this.$route.query.nameEn;
  489. let fjLonLatJson = [];
  490. this.fjLonLatJsonArr = [];
  491. webSocketService.connect({
  492. url: `ws/${this.getWsUrl(stationName)}`,
  493. autoReconnect: true,
  494. maxReconnectAttempts: 5,
  495. reconnectInterval: 3000,
  496. onOpen: (event) => {
  497. console.log("✅ WebSocket 连接成功", event);
  498. },
  499. onError: (error) => {
  500. console.log("❌ WebSocket 错误:", error);
  501. return;
  502. if (stationName === "MYFDC") {
  503. fjLonLatJson = JSON.parse(JSON.stringify(fjMYLonLatJson));
  504. } else if (stationName === "JNWHZ") {
  505. fjLonLatJson = JSON.parse(JSON.stringify(fjWHZLonLatJson));
  506. } else if (stationName === "JNYPL") {
  507. fjLonLatJson = JSON.parse(JSON.stringify(fjYPLLonLatJson));
  508. } else if (stationName === "JNSMS") {
  509. fjLonLatJson = JSON.parse(JSON.stringify(fjSMSLonLatJson));
  510. }
  511. let wtArray = JSON.parse(JSON.stringify(wsRes.windMachineList));
  512. this.isFakeData = true;
  513. fjLonLatJson.data.forEach((e) => {
  514. let fjItem =
  515. wtArray.find((findEle) => {
  516. return findEle.fjbh === e.fjbh;
  517. }) || {};
  518. const fjMix = Object.assign({}, e, fjItem);
  519. fjMix.status = this.getStatus(fjMix.fjzt);
  520. e = fjMix;
  521. this.showStatuswind(viewer, e);
  522. });
  523. this.fjLonLatJsonArr = fjLonLatJson;
  524. this.fakeDataTimmer = setInterval(() => {
  525. wtArray.forEach((fjzbItem) => {
  526. const jwdItem =
  527. fjLonLatJson.data.find((findEle) => {
  528. return findEle.fjbh === fjzbItem.fjbh;
  529. }) || {};
  530. this.changeZb(Object.assign({}, jwdItem, fjzbItem));
  531. });
  532. }, 1000);
  533. },
  534. });
  535. webSocketService.on("message", (res) => {
  536. if (stationName === "MYFDC") {
  537. fjLonLatJson = JSON.parse(JSON.stringify(fjMYLonLatJson));
  538. } else if (stationName === "JNWHZ") {
  539. fjLonLatJson = JSON.parse(JSON.stringify(fjWHZLonLatJson));
  540. } else if (stationName === "JNYPL") {
  541. fjLonLatJson = JSON.parse(JSON.stringify(fjYPLLonLatJson));
  542. } else if (stationName === "JNSMS") {
  543. fjLonLatJson = JSON.parse(JSON.stringify(fjSMSLonLatJson));
  544. }
  545. let wtArray = JSON.parse(JSON.stringify(res.windMachineList));
  546. if (this.isFirstAdd) {
  547. fjLonLatJson.data.forEach((e) => {
  548. let fjItem =
  549. wtArray.find((findEle) => {
  550. return findEle.fjbh === e.fjbh;
  551. }) || {};
  552. const fjMix = Object.assign({}, e, fjItem);
  553. fjMix.status = this.getStatus(fjMix.fjzt);
  554. e = fjMix;
  555. this.showStatuswind(viewer, e);
  556. });
  557. }
  558. this.fjLonLatJsonArr = fjLonLatJson;
  559. this.isFirstAdd = false;
  560. wtArray.forEach((fjzbItem) => {
  561. const jwdItem =
  562. fjLonLatJson.data.find((findEle) => {
  563. return findEle.fjbh === fjzbItem.fjbh;
  564. }) || {};
  565. this.changeZb(Object.assign({}, jwdItem, fjzbItem));
  566. });
  567. });
  568. this.resetWindViewport();
  569. },
  570. getWsUrl(stationName) {
  571. let wsurl = "";
  572. if (stationName === "MYFDC") {
  573. wsurl = "HZMY";
  574. } else if (stationName === "JNWHZ") {
  575. wsurl = "DHWH";
  576. } else if (stationName === "JNYPL") {
  577. wsurl = "DHYP";
  578. } else if (stationName === "JNSMS") {
  579. wsurl = "DHSM";
  580. }
  581. return wsurl;
  582. },
  583. getStatus(sName) {
  584. let status = 0;
  585. if (/待机/.test(sName)) {
  586. status = 1;
  587. } else if (/故障/.test(sName)) {
  588. status = 2;
  589. } else if (/检修/.test(sName)) {
  590. status = 3;
  591. } else if (/限电/.test(sName)) {
  592. status = 4;
  593. } else if (/离线/.test(sName)) {
  594. status = 5;
  595. } else if (/受累/.test(sName)) {
  596. status = 6;
  597. } else if (/并网/.test(sName)) {
  598. status = 7;
  599. }
  600. return status;
  601. },
  602. // 根据状态展示不同颜色风机贴图
  603. showStatuswind(viewer, e) {
  604. this.addModel(
  605. viewer,
  606. e.fjbh,
  607. e.longitude,
  608. e.latitude,
  609. e.status,
  610. e.height || 0,
  611. e
  612. );
  613. },
  614. //给模型上描边
  615. getWtStatue(code, type) {
  616. if (code === 1) {
  617. //待机
  618. // return "#05bb4c";
  619. return type === "color" ? "#05bb4c" : type === "en" ? "dj" : "待机";
  620. } else if (code === 2) {
  621. //故障
  622. // return "#d83238";
  623. return type === "color" ? "#d83238" : type === "en" ? "gz" : "故障";
  624. } else if (code === 3) {
  625. //检修
  626. // return "#ff8300";
  627. return type === "color" ? "#ff8300" : type === "en" ? "jx" : "检修";
  628. } else if (code === 4) {
  629. //限电
  630. // return "#c732ca";
  631. return type === "color" ? "#c732ca" : type === "en" ? "xd" : "限电";
  632. } else if (code === 5) {
  633. //离线
  634. // return "#6f7881";
  635. return type === "color" ? "#6f7881" : type === "en" ? "lx" : "离线";
  636. } else if (code === 6) {
  637. //受累
  638. // return "#cbd1d7";
  639. return type === "color" ? "#cbd1d7" : type === "en" ? "sl" : "受累";
  640. } else {
  641. //并网
  642. // return "#42a7f9";
  643. return type === "color" ? "#42a7f9" : type === "en" ? "bw" : "并网";
  644. }
  645. },
  646. addTreeain(viewer, mountainMsg) {
  647. const hpRoll = new Cesium.HeadingPitchRoll(90.0, 0.0, 0.0);
  648. const position = Cesium.Cartesian3.fromDegrees(
  649. mountainMsg.longitude,
  650. mountainMsg.latitude,
  651. mountainMsg.height || 0
  652. );
  653. const orientation = Cesium.Transforms.headingPitchRollQuaternion(
  654. position,
  655. hpRoll
  656. );
  657. const offsetLocal = new Cesium.Cartesian3(
  658. mountainMsg.east,
  659. mountainMsg.north,
  660. mountainMsg.up
  661. ); // E, N, U
  662. const transform = Cesium.Transforms.eastNorthUpToFixedFrame(position);
  663. const offsetGlobal = new Cesium.Cartesian3();
  664. Cesium.Matrix4.multiplyByPointAsVector(
  665. transform,
  666. offsetLocal,
  667. offsetGlobal
  668. );
  669. const finalPos = Cesium.Cartesian3.add(
  670. position,
  671. offsetGlobal,
  672. new Cesium.Cartesian3()
  673. );
  674. const terrain = viewer.entities.add({
  675. name: mountainMsg.modelName, // 模型名称
  676. position: finalPos, // 模型位置
  677. orientation, // 模型朝向
  678. animation: false,
  679. model: {
  680. uri: `/static/model/dixing/${mountainMsg.modelNameEn}.glb`,
  681. // uri: "/static/model/dixing/terrain.glb",
  682. scale: mountainMsg.scale,
  683. // 模型贴地
  684. heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
  685. },
  686. });
  687. },
  688. //添加模型
  689. addModel(viewer, name, lon, lat, status, height, model) {
  690. let that = this;
  691. const hpRoll = new Cesium.HeadingPitchRoll(45.0, 0.0, 0.0);
  692. const position = Cesium.Cartesian3.fromDegrees(lon, lat, height || 0);
  693. const orientation = Cesium.Transforms.headingPitchRollQuaternion(
  694. position,
  695. hpRoll
  696. );
  697. const statueColor = this.getWtStatue(status, "color");
  698. const statueFJ = this.getWtStatue(status, "en");
  699. const statueZh = this.getWtStatue(status, "zh");
  700. if (model.bgFj === "1") {
  701. const benchmark = viewer.entities.add({
  702. position,
  703. billboard: {
  704. image: that.benchmark,
  705. scale: 0.8,
  706. pixelOffset: new Cesium.Cartesian2(50, -25),
  707. heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
  708. }, // 偏移避免重叠
  709. });
  710. }
  711. const entity = viewer.entities.add({
  712. name, // 模型名称
  713. position, // 模型位置
  714. orientation, // 模型朝向
  715. animation: false,
  716. model: {
  717. uri: `/static/model/fjStatus/fj_${statueFJ}.glb`,
  718. scale: 0.5,
  719. // 模型贴地
  720. heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
  721. // heightReference: Cesium.HeightReference.NONE,
  722. silhouetteSize: 1,
  723. silhouetteColor: Cesium.Color.fromCssColorString(statueColor),
  724. minimumPixelSize: 75,
  725. // maximumScale:0.5
  726. // runAnimations: wtStatue !== 7 ? false : true,
  727. },
  728. label: {
  729. text: name,
  730. font: "14px sans-serif",
  731. fillColor: Cesium.Color.fromBytes(255, 255, 255),
  732. heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
  733. },
  734. });
  735. const btn = document.getElementById("windBtn");
  736. btn.addEventListener("click", function (event) {
  737. // entityxy.show = !entityxy.show;
  738. that.zbLabelList.forEach(iv => {
  739. iv.zb.forEach(it => {
  740. it.show = !it.show;
  741. })
  742. })
  743. });
  744. // viewer.camera.moveEnd.addEventListener(() => {
  745. // // 获取相机的笛卡尔3D坐标
  746. // const position = viewer.camera.position;
  747. // // 将笛卡尔坐标转换为地理坐标(弧度)
  748. // const cartographic = Cesium.Cartographic.fromCartesian(position);
  749. // // 高度(以米为单位)
  750. // // 注意:这个高度是相对于WGS84椭球体的高度,不是海拔高度(MSL)
  751. // const height = Cesium.Math.toDegrees(cartographic.height);
  752. // if (height > 3000000) {
  753. // entityxy.show = false;
  754. // console.log("entityxy false", entityxy.show);
  755. // }
  756. // if (height < 3000000) {
  757. // entityxy.show = true;
  758. // console.log("entityxy true", entityxy.show);
  759. // }
  760. // // 强制请求一次场景重绘
  761. // if (viewer && viewer.scene) {
  762. // viewer.scene.requestRender();
  763. // }
  764. // console.log("相机高度 (椭球体高):", height, "米");
  765. // });
  766. // 创建事件处理器
  767. const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
  768. handler.setInputAction(function (movement) {
  769. var position = movement.position;
  770. var pickedObject = viewer.scene.pick(position);
  771. if (pickedObject && pickedObject.id === entity) {
  772. console.log("你点击了标签或模型!", entity);
  773. // console.log("标签或模型数据!", val);
  774. that.modelVal = model;
  775. // 找到实体,显示包含实体信息的弹框
  776. that.showRightClickPopup(position, model);
  777. return;
  778. }
  779. }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
  780. let statusItems = [
  781. {
  782. label: "功率",
  783. value: `${
  784. this.isFakeData
  785. ? this.randomNum(100, 1000)
  786. : model.actualPower.toFixed(2)
  787. } MW`,
  788. },
  789. {
  790. label: "风速",
  791. value: `${
  792. this.isFakeData
  793. ? this.randomNum(100, 1000)
  794. : model.windSpeed.toFixed(1)
  795. } m/s`,
  796. },
  797. {
  798. label: "转速",
  799. value: `${
  800. this.isFakeData
  801. ? this.randomNum(100, 1000)
  802. : model.rotateSpeed.toFixed(1)
  803. } p`,
  804. },
  805. ];
  806. // const billboardImage = advancedGenerator.generateAdvancedBillboard({
  807. // title: name,
  808. // statusItems,
  809. // borderGradient: ["#ccc", "#ccc"],
  810. // backgroundColor: "rgba(13, 38, 77, 0.5)",
  811. // padding: 6,
  812. // titleHeight: 22,
  813. // itemHeight: 17,
  814. // titleColor: "#1890ff",
  815. // titleStrokeStyle: "#000",
  816. // titleStrokeWidth: 1,
  817. // labelStrokeStyle: "#000",
  818. // labelStrokeWidth: 1,
  819. // valueStrokeStyle: "#000",
  820. // valueStrokeWidth: 1,
  821. // });
  822. // let entityxy =
  823. // this.entityxyArray.find((findEle) => {
  824. // return findEle.fjbh === model.fjbh;
  825. // }) || null;
  826. // entityxy = viewer.entities.add({
  827. // name,
  828. // position,
  829. // billboard: {
  830. // image: billboardImage,
  831. // scale: 1,
  832. // verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // 原来已经是CENTER,现在改为BOTTOM
  833. // pixelOffset: new Cesium.Cartesian2(80, -50), // 原来是-20,现在改为30,向上移动
  834. // eyeOffset: new Cesium.Cartesian3(0, 0, 0), // 保持固定大小
  835. // heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
  836. // scaleByDistance: new Cesium.NearFarScalar(
  837. // 6000, // 5010米内
  838. // 1.2, // 1倍大小
  839. // 9000, // 10000米外
  840. // 1 // 0.1倍大小
  841. // ),
  842. // // 透明度渐变
  843. // // translucencyByDistance: new Cesium.NearFarScalar(
  844. // // 6000, // 500米内
  845. // // 1.0, // 完全不透明
  846. // // 9000, // 3000米外
  847. // // 0.5 // 完全透明
  848. // // ),
  849. // },
  850. // });
  851. // entityxy.fjbh = model.fjbh;
  852. // this.entityxyArray.push(entityxy);
  853. },
  854. changeZb(model) {
  855. // 使用高级灯牌生成器
  856. // let statusItems = [
  857. // {
  858. // label: "功率",
  859. // value: `${
  860. // this.isFakeData
  861. // ? this.randomNum(100, 1000)
  862. // : model.actualPower.toFixed(2)
  863. // } MW`,
  864. // },
  865. // {
  866. // label: "风速",
  867. // value: `${
  868. // this.isFakeData
  869. // ? this.randomNum(100, 1000)
  870. // : model.windSpeed.toFixed(1)
  871. // } m/s`,
  872. // },
  873. // {
  874. // label: "转速",
  875. // value: `${
  876. // this.isFakeData
  877. // ? this.randomNum(100, 1000)
  878. // : model.rotateSpeed.toFixed(1)
  879. // } p`,
  880. // },
  881. // ];
  882. const glText = this.isFakeData
  883. ? this.randomNum(100, 1000)
  884. : model.actualPower.toFixed(2);
  885. const fsText = this.isFakeData
  886. ? this.randomNum(100, 1000)
  887. : model.rotateSpeed.toFixed(2);
  888. const zsText = this.isFakeData
  889. ? this.randomNum(100, 1000)
  890. : model.windSpeed.toFixed(2);
  891. const zbMap = {
  892. glText: `${glText} MW`,
  893. fsText: `${fsText} m/s`,
  894. zsText: `${zsText} p`,
  895. };
  896. const position = Cesium.Cartesian3.fromDegrees(
  897. model.longitude,
  898. model.latitude,
  899. 0
  900. );
  901. const zbhj =
  902. this.zbLabelList.find((ele) => {
  903. return ele.fjbh === model.fjbh;
  904. }) || null;
  905. if (zbhj?.fjbh) {
  906. zbhj.zb.forEach((ele) => {
  907. if (ele.zb) {
  908. const spText = ele.label.text._value.split(": ");
  909. ele.label.text._value = `${spText[0]}: ${zbMap[ele.zb]}`;
  910. }
  911. });
  912. } else {
  913. const wtName = this.viewer.entities.add({
  914. position,
  915. label: {
  916. text: model.fjbh,
  917. font: "14pt sans-serif",
  918. fillColor: new Cesium.Color.fromCssColorString("#4fc3f7"), // 填充颜色
  919. outlineColor: Cesium.Color.BLACK, // 轮廓颜色
  920. style: Cesium.LabelStyle.FILL_AND_OUTLINE,
  921. outlineWidth: 2,
  922. verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
  923. showBackground: false, // 显示背景
  924. backgroundColor: new Cesium.Color.fromCssColorString(
  925. "rgba(13, 38, 77, 0.5)"
  926. ), // 背景颜色
  927. backgroundPadding: new Cesium.Cartesian2(10, 10), // 背景内边距
  928. verticalOrigin: Cesium.VerticalOrigin.CENTER, // 垂直对齐方式
  929. horizontalOrigin: Cesium.HorizontalOrigin.LEFT, // 水平对齐方式
  930. pixelOffset: new Cesium.Cartesian2(80, -60), // 原来是-20,现在改为30,向上移动
  931. eyeOffset: new Cesium.Cartesian3(0, 0, 0), // 保持固定大小
  932. heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
  933. scaleByDistance: new Cesium.NearFarScalar(
  934. 6000, // 6000米内
  935. 1,
  936. 9000, // 9000米外
  937. 0.8
  938. ),
  939. },
  940. });
  941. const glZb = this.viewer.entities.add({
  942. position,
  943. label: {
  944. text: `功率: ${glText} MW`,
  945. // text: `功率: ${glText} MW\n风速: ${fsText}\n转速: ${zsText}`,
  946. font: "14pt sans-serif",
  947. style: Cesium.LabelStyle.FILL_AND_OUTLINE,
  948. outlineWidth: 2,
  949. verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
  950. showBackground: false, // 显示背景
  951. backgroundColor: new Cesium.Color.fromCssColorString(
  952. "rgba(13, 38, 77, 0.5)"
  953. ), // 背景颜色
  954. backgroundPadding: new Cesium.Cartesian2(10, 10), // 背景内边距
  955. verticalOrigin: Cesium.VerticalOrigin.CENTER, // 垂直对齐方式
  956. horizontalOrigin: Cesium.HorizontalOrigin.LEFT, // 水平对齐方式
  957. pixelOffset: new Cesium.Cartesian2(80, -40), // 原来是-20,现在改为30,向上移动
  958. eyeOffset: new Cesium.Cartesian3(0, 0, 0), // 保持固定大小
  959. heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
  960. scaleByDistance: new Cesium.NearFarScalar(
  961. 6000, // 6000米内
  962. 1,
  963. 9000, // 9000米外
  964. 0.8
  965. ),
  966. },
  967. });
  968. glZb.zb = "glText";
  969. const fsZb = this.viewer.entities.add({
  970. position,
  971. label: {
  972. text: `风速: ${fsText}`,
  973. font: "14pt sans-serif",
  974. style: Cesium.LabelStyle.FILL_AND_OUTLINE,
  975. outlineWidth: 2,
  976. verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
  977. showBackground: false, // 显示背景
  978. backgroundColor: new Cesium.Color.fromCssColorString(
  979. "rgba(13, 38, 77, 0.5)"
  980. ), // 背景颜色
  981. backgroundPadding: new Cesium.Cartesian2(10, 10), // 背景内边距
  982. verticalOrigin: Cesium.VerticalOrigin.CENTER, // 垂直对齐方式
  983. horizontalOrigin: Cesium.HorizontalOrigin.LEFT, // 水平对齐方式
  984. pixelOffset: new Cesium.Cartesian2(80, -20), // 原来是-20,现在改为30,向上移动
  985. eyeOffset: new Cesium.Cartesian3(0, 0, 0), // 保持固定大小
  986. heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
  987. scaleByDistance: new Cesium.NearFarScalar(
  988. 6000, // 6000米内
  989. 1,
  990. 9000, // 9000米外
  991. 0.8
  992. ),
  993. },
  994. });
  995. fsZb.zb = "fsText";
  996. const zsZb = this.viewer.entities.add({
  997. position,
  998. label: {
  999. text: `转速: ${zsText}`,
  1000. font: "14pt sans-serif",
  1001. style: Cesium.LabelStyle.FILL_AND_OUTLINE,
  1002. outlineWidth: 2,
  1003. verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
  1004. showBackground: false, // 显示背景
  1005. backgroundColor: new Cesium.Color.fromCssColorString(
  1006. "rgba(13, 38, 77, 0.5)"
  1007. ), // 背景颜色
  1008. backgroundPadding: new Cesium.Cartesian2(10, 10), // 背景内边距
  1009. verticalOrigin: Cesium.VerticalOrigin.CENTER, // 垂直对齐方式
  1010. horizontalOrigin: Cesium.HorizontalOrigin.LEFT, // 水平对齐方式
  1011. pixelOffset: new Cesium.Cartesian2(80, 0), // 原来是-20,现在改为30,向上移动
  1012. eyeOffset: new Cesium.Cartesian3(0, 0, 0), // 保持固定大小
  1013. heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
  1014. scaleByDistance: new Cesium.NearFarScalar(
  1015. 6000, // 6000米内
  1016. 1,
  1017. 9000, // 9000米外
  1018. 0.8
  1019. ),
  1020. },
  1021. });
  1022. zsZb.zb = "zsText";
  1023. this.zbLabelList.push({
  1024. fjbh: model.fjbh,
  1025. zb: [wtName, glZb, fsZb, zsZb],
  1026. // zb: [wtName, glZb],
  1027. });
  1028. }
  1029. // entity.label.text._value
  1030. // const billboardImage = advancedGenerator.generateAdvancedBillboard({
  1031. // title: model.fjbh,
  1032. // statusItems,
  1033. // borderGradient: ["#ccc", "#ccc"],
  1034. // backgroundColor: "rgba(13, 38, 77, 0.5)",
  1035. // padding: 6,
  1036. // titleHeight: 22,
  1037. // itemHeight: 17,
  1038. // titleColor: "#1890ff",
  1039. // titleStrokeStyle: "#000",
  1040. // titleStrokeWidth: 1,
  1041. // labelStrokeStyle: "#000",
  1042. // labelStrokeWidth: 1,
  1043. // valueStrokeStyle: "#000",
  1044. // valueStrokeWidth: 1,
  1045. // });
  1046. // this.billboardImageList.push(billboardImage);
  1047. // let entityxy =
  1048. // this.entityxyArray.find((findEle) => {
  1049. // return findEle.fjbh === model.fjbh;
  1050. // }) || null;
  1051. // for (let i = 0; i < this.entityxyArray.length; i++) {
  1052. // if (this.entityxyArray[i].fjbh === model.fjbh) {
  1053. // let entityxy = Cesium.clone(this.entityxyArray[i]);
  1054. // entityxy.fjbh = this.entityxyArray[i].fjbh;
  1055. // entityxy.billboard = null;
  1056. // entityxy.billboard = {
  1057. // image: billboardImage.toDataURL("image/png"),
  1058. // scale: 1,
  1059. // width: billboardImage.width,
  1060. // height: billboardImage.height,
  1061. // verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // 原来已经是CENTER,现在改为BOTTOM
  1062. // pixelOffset: new Cesium.Cartesian2(80, -50), // 原来是-20,现在改为30,向上移动
  1063. // eyeOffset: new Cesium.Cartesian3(0, 0, 0), // 保持固定大小
  1064. // heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
  1065. // scaleByDistance: new Cesium.NearFarScalar(
  1066. // 6000, // 5010米内
  1067. // 1.2, // 1.2倍大小
  1068. // 9000, // 10000米外
  1069. // 1 // 1.05倍大小
  1070. // ),
  1071. // };
  1072. // this.viewer.entities.remove(this.entityxyArray[i]);
  1073. // this.viewer.entities.add(entityxy);
  1074. // this.entityxyArray.splice(i, 1, entityxy);
  1075. // break;
  1076. // }
  1077. // }
  1078. // if (entityxy) {
  1079. // entityxy.billboard = {
  1080. // image: billboardImage.toDataURL("image/png"),
  1081. // scale: 1,
  1082. // width: billboardImage.width,
  1083. // height: billboardImage.height,
  1084. // verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // 原来已经是CENTER,现在改为BOTTOM
  1085. // pixelOffset: new Cesium.Cartesian2(80, -50), // 原来是-20,现在改为30,向上移动
  1086. // eyeOffset: new Cesium.Cartesian3(0, 0, 0), // 保持固定大小
  1087. // heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
  1088. // scaleByDistance: new Cesium.NearFarScalar(
  1089. // 6000, // 5010米内
  1090. // 1.2, // 1.2倍大小
  1091. // 9000, // 10000米外
  1092. // 1 // 1.05倍大小
  1093. // ),
  1094. // };
  1095. // }
  1096. },
  1097. // 右键展示元素
  1098. showRightClickPopup(screenPosition, val) {
  1099. // 创建或获取弹框元素
  1100. var popup = document.getElementById("rightClickPopup");
  1101. if (!popup) {
  1102. popup = document.createElement("div");
  1103. popup.id = "rightClickPopup";
  1104. popup.style.position = "absolute";
  1105. // popup.style.backgroundColor = 'white';
  1106. popup.style.border = "1px solid rgba(255, 255, 255, 0.2)";
  1107. popup.style.borderRadius = "4px";
  1108. popup.style.padding = "10px";
  1109. popup.style.boxShadow = "0 2px 10px rgba(0,0,0,0.2)";
  1110. popup.style.backdropFilter = `blur(10px)`;
  1111. popup.style.zIndex = "1000";
  1112. popup.style.pointerEvents = "auto"; // 确保弹框可交互
  1113. // 添加一个最小宽度,避免内容过短
  1114. popup.style.minWidth = "100px";
  1115. // 可选:添加箭头指向点击点
  1116. // 这需要更复杂的 CSS 或额外的 DOM 元素
  1117. document.body.appendChild(popup);
  1118. }
  1119. // <span class="popup-menu-item" data-message-type="model" style="cursor:pointer">模型解构</span>
  1120. let content = `
  1121. <div style="display: flex;gap: 10px;flex-direction: column;text-align: center;color: #fff">
  1122. <span class="popup-menu-item" data-message-type="basic" style="cursor:pointer">基本信息</span>
  1123. <span class="popup-menu-item" data-message-type="video" style="cursor:pointer">视频监控</span>
  1124. <span class="popup-menu-item" data-message-type="problem" style="cursor:pointer">故障详情</span>
  1125. <span class="popup-menu-item" data-message-type="third" style="cursor:pointer">模型解构</span>
  1126. </div>
  1127. `;
  1128. // 设置弹框内容
  1129. popup.innerHTML = content;
  1130. // --- 关键修正:准确计算弹框位置 ---
  1131. // 1. 获取 Cesium 画布 (Canvas) 相对于视口 (viewport) 的位置
  1132. var canvas = this.viewer.scene.canvas;
  1133. var canvasRect = canvas.getBoundingClientRect();
  1134. // console.log('Canvas Rect:', canvasRect); // 调试用
  1135. // 2. 计算弹框左上角在页面中的绝对 X 坐标
  1136. // screenPosition.x 是相对于画布左上角的 X 偏移
  1137. // canvasRect.left 是画布左上角相对于浏览器视口左上角的 X 偏移
  1138. // window.pageXOffset 是页面水平滚动的距离
  1139. var popupLeft = canvasRect.left + screenPosition.x + window.pageXOffset;
  1140. // 3. 计算弹框左上角在页面中的绝对 Y 坐标
  1141. var popupTop = canvasRect.top + screenPosition.y + window.pageYOffset;
  1142. // --- 可选:添加偏移量,避免完全覆盖鼠标指针 ---
  1143. // 例如,向下和向右偏移 10px
  1144. var offsetX = 10;
  1145. var offsetY = 10;
  1146. popupLeft += offsetX;
  1147. popupTop += offsetY;
  1148. // --- 可选:边界检查,防止弹框超出屏幕 ---
  1149. var popupWidth = popup.offsetWidth || 150; // 确保有宽度
  1150. var popupHeight = popup.offsetHeight || 50; // 确保有高度
  1151. var windowWidth = window.innerWidth;
  1152. var windowHeight = window.innerHeight;
  1153. // 如果弹框右边会超出窗口,则左移
  1154. if (popupLeft + popupWidth > windowWidth) {
  1155. popupLeft = windowWidth - popupWidth - 10; // 靠右留 10px 边距
  1156. }
  1157. // 如果弹框底边会超出窗口,则上移
  1158. if (popupTop + popupHeight > windowHeight) {
  1159. popupTop = windowHeight - popupHeight - 10; // 靠下留 10px 边距
  1160. }
  1161. // 如果左边超出 (不太可能,但安全起见)
  1162. if (popupLeft < 0) popupLeft = 10;
  1163. // 如果顶边超出
  1164. if (popupTop < 0) popupTop = 10;
  1165. // --- 设置最终位置 ---
  1166. popup.style.left = popupLeft + "px";
  1167. popup.style.top = popupTop + "px";
  1168. // console.log('Popup Position:', { left: popupLeft, top: popupTop }); // 调试用
  1169. // 显示弹框
  1170. popup.style.display = "block";
  1171. // --- 隐藏弹框的逻辑 (保持不变) ---
  1172. function hidePopup(event) {
  1173. // 检查点击是否发生在弹框内部,如果是,则不隐藏
  1174. if (event && popup.contains(event.target)) {
  1175. return;
  1176. }
  1177. popup.style.display = "none";
  1178. document.removeEventListener("click", hidePopup);
  1179. document.removeEventListener("keydown", keyDownHandler);
  1180. }
  1181. function keyDownHandler(event) {
  1182. if (event.key === "Escape") {
  1183. hidePopup();
  1184. }
  1185. }
  1186. let that = this;
  1187. that.showBasicMsg = false;
  1188. that.showVideoMsg = false;
  1189. that.showProblemMsg = false;
  1190. that.showModelMsg = false;
  1191. function popupMenuItemClick(event) {
  1192. // 检查点击的是否是菜单项
  1193. if (event.target.classList.contains("popup-menu-item")) {
  1194. event.stopPropagation(); // 阻止冒泡,防止弹框立即关闭
  1195. var messageType = event.target.dataset.messageType; // 获取 data-message-type
  1196. console.log("点击了菜单项:", messageType);
  1197. // 调用您的 showMessage 函数
  1198. that.showMessage(messageType, val);
  1199. hidePopup();
  1200. // 可选:执行后关闭弹框
  1201. // hideRightClickPopup();
  1202. }
  1203. }
  1204. // 移除旧的监听器(避免重复绑定)
  1205. document.removeEventListener("click", hidePopup);
  1206. document.removeEventListener("click", popupMenuItemClick);
  1207. document.removeEventListener("keydown", keyDownHandler);
  1208. // 添加新的监听器
  1209. document.addEventListener("click", hidePopup);
  1210. document.addEventListener("click", popupMenuItemClick);
  1211. document.addEventListener("keydown", keyDownHandler);
  1212. },
  1213. showMessage(type, val) {
  1214. this.windDrawer = true;
  1215. this.windDrawerHeader = val.fjbh + "数据详情";
  1216. if (type === "basic") {
  1217. this.windDrawerTitle = "基础信息";
  1218. this.showBasicMsg = true;
  1219. } else if (type === "video") {
  1220. this.windDrawerTitle = "视频监控";
  1221. this.showVideoMsg = true;
  1222. } else if (type === "problem") {
  1223. this.windDrawerTitle = "故障查看";
  1224. this.showProblemMsg = true;
  1225. } else if (type === "third") {
  1226. this.windDrawerHeader = `${val.name}部件查询解构`;
  1227. this.windDrawerTitle = "模型解构";
  1228. this.showModelMsg = true;
  1229. }
  1230. },
  1231. showwindmodel() {
  1232. this.windDrawer = true;
  1233. this.showBasicMsg = false;
  1234. this.showVideoMsg = false;
  1235. this.showProblemMsg = false;
  1236. this.windDrawerHeader = "风机模型可视化解构说明";
  1237. this.showModelMsg = true;
  1238. },
  1239. handleClose() {
  1240. this.windDrawer = false;
  1241. this.showBasicMsg = false;
  1242. this.showVideoMsg = false;
  1243. this.showProblemMsg = false;
  1244. this.showModelMsg = false;
  1245. },
  1246. showComDia(val) {
  1247. this.showcomModelDia = val;
  1248. this.modelVal = null;
  1249. },
  1250. randomNum(minNum, maxNum) {
  1251. switch (arguments.length) {
  1252. case 1:
  1253. return parseInt(Math.random() * minNum + 1, 10);
  1254. case 2:
  1255. return parseInt(Math.random() * (maxNum - minNum + 1) + minNum, 10);
  1256. default:
  1257. return 0;
  1258. }
  1259. },
  1260. csceneElliposid(viewer) {
  1261. let that = this;
  1262. // 获取 scene 和 ellipsoid
  1263. var scene = viewer.scene;
  1264. var labels = viewer.scene.primitives.add(new Cesium.LabelCollection());
  1265. // 创建 ScreenSpaceEventHandler
  1266. var handler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
  1267. // 监听左键点击事件
  1268. handler.setInputAction(async (click) => {
  1269. // 获取点击位置的笛卡尔坐标
  1270. var position = click.position;
  1271. if (!position) return;
  1272. // 使用 globe.pick 获取包含地形高度的坐标
  1273. var ray = viewer.camera.getPickRay(position);
  1274. var cartesian = viewer.scene.globe.pick(ray, viewer.scene);
  1275. if (cartesian) {
  1276. // 转换为地理坐标
  1277. var cartographic = Cesium.Cartographic.fromCartesian(cartesian);
  1278. var longitude = Cesium.Math.toDegrees(cartographic.longitude);
  1279. var latitude = Cesium.Math.toDegrees(cartographic.latitude);
  1280. var height = cartographic.height;
  1281. // 格式化坐标
  1282. var text = `经度: ${longitude.toFixed(6)}°\n纬度: ${latitude.toFixed(
  1283. 6
  1284. )}°`;
  1285. // 创建一个标签
  1286. var label = labels.add({
  1287. position: cartesian,
  1288. text: text,
  1289. font: "14px monospace",
  1290. fillColor: Cesium.Color.fromCssColorString("#1d70df"),
  1291. // style: Cesium.LabelStyle.FILL_AND_OUTLINE,
  1292. // outlineColor: Cesium.Color.BLACK,
  1293. outlineWidth: 2,
  1294. verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // 标签在点的上方
  1295. pixelOffset: new Cesium.Cartesian2(0, -20), // 向上偏移一点
  1296. disableDepthTest: true, // 让标签始终可见(即使在地球背面)
  1297. scale: 0.8,
  1298. });
  1299. // 5秒后移除标签
  1300. setTimeout(function () {
  1301. labels.remove(label);
  1302. }, 5000);
  1303. console.log(
  1304. `点击坐标: ${longitude.toFixed(6)}, ${latitude.toFixed(
  1305. 6
  1306. )}, ${height.toFixed(2)}m`
  1307. );
  1308. }
  1309. }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
  1310. },
  1311. generateUniqueId(prefix = "id") {
  1312. let idCounter = 0;
  1313. return `${prefix}-${Date.now()}-${Math.random()
  1314. .toString(36)
  1315. .substr(2, 9)}-${idCounter++}`;
  1316. },
  1317. handleInitView() {
  1318. if (this.showWindDetail) {
  1319. this.resetWindViewport();
  1320. } else {
  1321. this.resetAllStationViewport();
  1322. }
  1323. },
  1324. // 重置所有风场视角
  1325. resetAllStationViewport() {
  1326. this.viewer.camera.flyTo({
  1327. destination: Cesium.Cartesian3.fromDegrees(
  1328. 114.502778,
  1329. 35.326667,
  1330. 3000000
  1331. ),
  1332. orientation: {
  1333. heading: Cesium.Math.toRadians(0),
  1334. pitch: Cesium.Math.toRadians(-90),
  1335. roll: 0.0,
  1336. },
  1337. duration: 3.0,
  1338. });
  1339. },
  1340. // 重置风场中所有风机视角
  1341. resetWindViewport() {
  1342. let fromLon = this.$route.query.longitude * 1;
  1343. let fromLat = this.$route.query.latitude * 1;
  1344. let fromheight = this.$route.query.height * 1;
  1345. let fromname = this.$route.query.nameEn;
  1346. // 设置镜头到指定的经纬度(度)、高度(米)
  1347. this.viewer.camera.setView({
  1348. destination: Cesium.Cartesian3.fromDegrees(
  1349. fromLon, // 经度 (degrees)
  1350. fromLat, // 纬度 (degrees)
  1351. fromheight // 高度 (meters)
  1352. ),
  1353. orientation: {
  1354. heading: Cesium.Math.toRadians(0.0), // 偏航角 (方向,0 指向北方)
  1355. pitch: Cesium.Math.toRadians(-90.0), // 俯仰角 (-90 是垂直向下)
  1356. roll: 0.0, // 翻滚角
  1357. },
  1358. });
  1359. // 目标位置:经度、纬度、高度
  1360. // const targetLon = 107.034945;
  1361. // const targetLat = 37.309099;
  1362. let targetLon = null;
  1363. let targetLat = null;
  1364. this.restLatLon.forEach((it) => {
  1365. if (it.name === fromname) {
  1366. targetLon = it.longitude;
  1367. targetLat = it.latitude;
  1368. }
  1369. });
  1370. const targetHeight = 5000;
  1371. const draggableHeightTolerance = 5000; // 允许拖拽的高度范围:20,000 ~ 30,000
  1372. const minHeight = 1600; // 最低高度
  1373. const maxHeight = 10000; // 最高高度
  1374. const allowedOffsetDegrees = 2; // 允许拖拽的最大偏移(经纬度)
  1375. const Lat3d = targetLat - 0.3;
  1376. const minLon = targetLon - allowedOffsetDegrees;
  1377. const maxLon = targetLon + allowedOffsetDegrees;
  1378. const minLat = Lat3d - allowedOffsetDegrees;
  1379. const maxLat = Lat3d + allowedOffsetDegrees;
  1380. let that = this;
  1381. that.viewer.camera.flyTo({
  1382. destination: Cesium.Cartesian3.fromDegrees(
  1383. targetLon,
  1384. Lat3d,
  1385. targetHeight
  1386. ),
  1387. // orientation: {
  1388. // heading: Cesium.Math.toRadians(0),
  1389. // pitch: Cesium.Math.toRadians(-90),
  1390. // roll: 0.0,
  1391. // },
  1392. orientation: {
  1393. heading: 0, //北京天安门角度 0 上海角度 1
  1394. pitch: -0.3,
  1395. roll: 0,
  1396. },
  1397. duration: 3.0,
  1398. complete: function () {
  1399. console.log("飞入完成,启用拖拽限制逻辑");
  1400. enableHeightBasedDragControl();
  1401. },
  1402. });
  1403. // ===== 控制逻辑:根据高度决定是否允许拖拽 =====
  1404. function enableHeightBasedDragControl() {
  1405. that.viewer.scene.preUpdate.addEventListener(function (scene, time) {
  1406. const camera = that.viewer.camera;
  1407. const posCartographic = camera.positionCartographic;
  1408. if (!posCartographic) return;
  1409. const currentHeight = posCartographic.height;
  1410. that.currentHeight = currentHeight;
  1411. const currentLon = Cesium.Math.toDegrees(posCartographic.longitude);
  1412. const currentLat = Cesium.Math.toDegrees(posCartographic.latitude);
  1413. // === 第一步:限制相机高度不能超出 [10000, 100000] ===
  1414. if (currentHeight < minHeight) {
  1415. // 强制拉高到 minHeight,但保持当前水平视角
  1416. const newPos = Cesium.Cartesian3.fromDegrees(
  1417. currentLon,
  1418. currentLat,
  1419. minHeight
  1420. );
  1421. camera.setView({
  1422. destination: newPos,
  1423. orientation: {
  1424. heading: camera.heading,
  1425. pitch: camera.pitch,
  1426. roll: camera.roll,
  1427. },
  1428. });
  1429. return; // 避免后续逻辑冲突
  1430. }
  1431. if (currentHeight > maxHeight) {
  1432. const newPos = Cesium.Cartesian3.fromDegrees(
  1433. currentLon,
  1434. currentLat,
  1435. maxHeight
  1436. );
  1437. camera.setView({
  1438. destination: newPos,
  1439. orientation: {
  1440. heading: camera.heading,
  1441. pitch: camera.pitch,
  1442. roll: camera.roll,
  1443. },
  1444. });
  1445. return;
  1446. }
  1447. // if (currentHeight < (maxHeight-700000)) {
  1448. // that.cancleAllLayer();
  1449. // }
  1450. // === 第二步:判断是否在“可拖拽高度区间” ===
  1451. const isInDraggableRange =
  1452. currentHeight >= targetHeight - draggableHeightTolerance &&
  1453. currentHeight <= targetHeight + draggableHeightTolerance;
  1454. if (isInDraggableRange) {
  1455. // ✅ 允许拖拽,但限制在指定范围内
  1456. const correctedLon = Cesium.Math.clamp(currentLon, minLon, maxLon);
  1457. const correctedLat = Cesium.Math.clamp(currentLat, minLat, maxLat);
  1458. if (
  1459. Math.abs(correctedLon - currentLon) > 1e-8 ||
  1460. Math.abs(correctedLat - currentLat) > 1e-8
  1461. ) {
  1462. // 越界了,纠正位置,保留当前高度和视角
  1463. camera.setView({
  1464. destination: Cesium.Cartesian3.fromDegrees(
  1465. correctedLon,
  1466. correctedLat,
  1467. currentHeight
  1468. ),
  1469. orientation: {
  1470. heading: camera.heading,
  1471. pitch: camera.pitch,
  1472. roll: camera.roll,
  1473. },
  1474. });
  1475. }
  1476. } else {
  1477. // ❌ 不在可拖拽高度:禁止平移,强制回正到目标点
  1478. const correctedPosition = Cesium.Cartesian3.fromDegrees(
  1479. targetLon,
  1480. targetLat,
  1481. currentHeight // 保留当前缩放高度
  1482. );
  1483. camera.setView({
  1484. destination: correctedPosition,
  1485. orientation: {
  1486. heading: camera.heading,
  1487. pitch: camera.pitch,
  1488. roll: camera.roll,
  1489. },
  1490. });
  1491. }
  1492. });
  1493. }
  1494. },
  1495. coverOnChange(val) {
  1496. if (val.value === "风场") {
  1497. this.switchWindLayer(val.check);
  1498. } else if (val.value === "云层") {
  1499. this.switchCloudLayer(val.check);
  1500. } else if (val.value === "降雨") {
  1501. this.switchRainLayer(val.check);
  1502. } else if (val.value === "温度") {
  1503. this.switchTemperatureLayerr(val.check);
  1504. }
  1505. },
  1506. // 提供控制函数以便在需要时停止循环
  1507. stopCycling(intervalId) {
  1508. if (intervalId) {
  1509. clearInterval(intervalId);
  1510. intervalId = null;
  1511. console.log("循环已停止");
  1512. }
  1513. },
  1514. // 切换风场图显隐
  1515. switchWindLayer() {
  1516. this.viewer.scene.screenSpaceCameraController.enableZoom = true;
  1517. if (this.rainLayer || this.rainImagesLayer.length > 0) {
  1518. this.removeRainLayer();
  1519. this.stopCycling(this.rainintervalId);
  1520. }
  1521. if (this.cloudLayer || this.cloudImagesLayer.length > 0) {
  1522. this.removeCloudLayer();
  1523. this.stopCycling(this.cloudintervalId);
  1524. }
  1525. if (this.temperatureLayer || this.tempImagesLayer.length > 0) {
  1526. this.removeTemperatureLayer();
  1527. this.stopCycling(this.tempintervalId);
  1528. }
  1529. if (this.windLayer) {
  1530. this.removeWindLayer();
  1531. } else {
  1532. this.showWindLayer();
  1533. }
  1534. },
  1535. // 添加风场图
  1536. async showWindLayer() {
  1537. if (!this.windLayer) {
  1538. this.windLayer = new WindLayer(windGridData, {
  1539. particleSize: 2.0,
  1540. particleOpacity: 0.6,
  1541. particleSpeed: 0.01,
  1542. maxVelocity: 25, // 风速最大值(用于颜色映射和速度缩放)
  1543. minVelocity: 0, // 风速最小值阈值(低于此值不显示粒子)
  1544. colorScale: [
  1545. "rgb(36,104, 180)",
  1546. "rgb(60,157, 194)",
  1547. "rgb(128,205,193)",
  1548. "rgb(151,218,168)",
  1549. "rgb(198,231,181)",
  1550. "rgb(238,247,217)",
  1551. "rgb(255,238,159)",
  1552. "rgb(252,217,125)",
  1553. "rgb(255,182,100)",
  1554. "rgb(252,150,75)",
  1555. "rgb(250,112,52)",
  1556. "rgb(245,64,32)",
  1557. "rgb(237,45,28)",
  1558. "rgb(220,24,32)",
  1559. "rgb(180,0,35)",
  1560. ], // 颜色强度缩放
  1561. frameRate: 15,
  1562. fadeOpacity: 0.995,
  1563. particleAge: 150,
  1564. maxAge: 60,
  1565. globalAlpha: 0.8,
  1566. velocityScale: 1 / 30, // 粒子移动速度缩放因子(控制动画快慢)
  1567. paths: 500,
  1568. lineWidth: 2,
  1569. });
  1570. this.windLayer.addTo(this.viewer);
  1571. }
  1572. },
  1573. // 切换卫星云图显隐
  1574. switchCloudLayer(val) {
  1575. if (this.windLayer) {
  1576. this.removeWindLayer();
  1577. }
  1578. if (this.rainLayer || this.rainImagesLayer.length > 0) {
  1579. this.removeRainLayer();
  1580. this.stopCycling(this.rainintervalId);
  1581. }
  1582. if (this.temperatureLayer || this.tempImagesLayer.length > 0) {
  1583. this.removeTemperatureLayer();
  1584. this.stopCycling(this.tempintervalId);
  1585. }
  1586. if (!val || this.cloudLayer || this.cloudImagesLayer.length > 0) {
  1587. this.removeCloudLayer();
  1588. this.stopCycling(this.cloudintervalId);
  1589. } else {
  1590. this.showCloudLayer();
  1591. }
  1592. },
  1593. // 切换降雨图显隐
  1594. switchRainLayer() {
  1595. this.viewer.scene.screenSpaceCameraController.enableZoom = true;
  1596. if (this.windLayer) {
  1597. this.removeWindLayer();
  1598. }
  1599. if (this.cloudLayer || this.cloudImagesLayer.length > 0) {
  1600. this.removeCloudLayer();
  1601. this.stopCycling(this.cloudintervalId);
  1602. }
  1603. if (this.temperatureLayer || this.tempImagesLayer.length > 0) {
  1604. this.removeTemperatureLayer();
  1605. this.stopCycling(this.tempintervalId);
  1606. }
  1607. if (this.rainLayer || this.rainImagesLayer.length > 0) {
  1608. this.removeRainLayer();
  1609. this.stopCycling(this.rainintervalId);
  1610. } else {
  1611. this.showRainLayer();
  1612. }
  1613. },
  1614. // 切换温度图显隐
  1615. switchTemperatureLayerr() {
  1616. this.viewer.scene.screenSpaceCameraController.enableZoom = true;
  1617. if (this.windLayer) {
  1618. this.removeWindLayer();
  1619. }
  1620. if (this.cloudLayer || this.cloudImagesLayer.length > 0) {
  1621. this.removeCloudLayer();
  1622. this.stopCycling(this.cloudintervalId);
  1623. }
  1624. if (this.rainLayer || this.rainImagesLayer.length > 0) {
  1625. this.removeRainLayer();
  1626. this.stopCycling(this.rainintervalId);
  1627. }
  1628. if (this.temperatureLayer || this.tempImagesLayer.length > 0) {
  1629. this.removeTemperatureLayer();
  1630. this.stopCycling(this.tempintervalId);
  1631. } else {
  1632. this.showTemperatureLayer();
  1633. }
  1634. },
  1635. // 显示云图
  1636. showCloudLayer() {
  1637. const imageUrls = [];
  1638. cloudJson.forEach((it) => {
  1639. imageUrls.push("/static" + it.path);
  1640. });
  1641. this.showeveryTypeImagesLayer(
  1642. imageUrls,
  1643. this.cloudintervalId,
  1644. this.cloudImagesLayer
  1645. );
  1646. },
  1647. //显示降雨图
  1648. showRainLayer() {
  1649. const imageUrls = [];
  1650. rainJson.forEach((it) => {
  1651. imageUrls.push("/static" + it.path);
  1652. });
  1653. this.showeveryTypeImagesLayer(
  1654. imageUrls,
  1655. this.rainintervalId,
  1656. this.rainImagesLayer
  1657. );
  1658. this.csceneElliposid(this.viewer, "rain");
  1659. },
  1660. //显示温度图
  1661. showTemperatureLayer() {
  1662. const imageUrls = [];
  1663. tempJson.forEach((it) => {
  1664. imageUrls.push("/static" + it.path);
  1665. });
  1666. this.showeveryTypeImagesLayer(
  1667. imageUrls,
  1668. this.tempintervalId,
  1669. this.tempImagesLayer
  1670. );
  1671. this.csceneElliposid(this.viewer, "temp");
  1672. },
  1673. async showeveryTypeImagesLayer(imageUrls, intervalId, ImagesLayers) {
  1674. // 存储所有图片图层的数组
  1675. let imageLayers = [];
  1676. // 当前显示的图片索引
  1677. let currentImageIndex = -1; // 初始为-1,表示没有图片显示
  1678. // 创建所有图片图层并添加到Viewer,初始时全部隐藏
  1679. await imageUrls.forEach((url) => {
  1680. const provider = new Cesium.SingleTileImageryProvider({
  1681. url: url,
  1682. // url: URL.createObjectURL(url),
  1683. rectangle: Cesium.Rectangle.fromDegrees(-180.0, -90.0, 180.0, 90.0), // 全球覆盖
  1684. tileWidth: 1440, // 根据你的图片实际宽度修改
  1685. tileHeight: 721,
  1686. // 如果你的图片只覆盖特定区域,请修改rectangle参数
  1687. });
  1688. const Layer = this.viewer.imageryLayers.addImageryProvider(provider);
  1689. Layer.alpha = 0.8; // 透明度
  1690. Layer.brightness = 1; // 亮度
  1691. Layer.contrast = 1; // 对比度
  1692. Layer.show = false; // 初始隐藏
  1693. imageLayers.push(Layer);
  1694. ImagesLayers.push(Layer);
  1695. });
  1696. function showNextImage() {
  1697. // 隐藏当前图片
  1698. if (currentImageIndex >= 0 && currentImageIndex < imageLayers.length) {
  1699. imageLayers[currentImageIndex].show = false;
  1700. }
  1701. // 计算下一张图片的索引
  1702. currentImageIndex = (currentImageIndex + 1) % imageLayers.length;
  1703. // 显示下一张图片
  1704. imageLayers[currentImageIndex].show = true;
  1705. // imageLayers[currentImageIndex + 1].show = true;
  1706. console.log("当前显示图片: " + imageUrls[currentImageIndex]);
  1707. }
  1708. // 设置切换间隔(毫秒),例如每5秒切换一次
  1709. const intervalMs = 5000;
  1710. intervalId = setInterval(showNextImage, intervalMs);
  1711. // 初始显示第一张图片
  1712. showNextImage();
  1713. },
  1714. // 移除风场图
  1715. removeWindLayer() {
  1716. if (this.windLayer) {
  1717. // this.windLayer.destroy();
  1718. this.windLayer.remove();
  1719. this.windLayer = null;
  1720. }
  1721. },
  1722. // 移除卫星云图
  1723. removeCloudLayer() {
  1724. if (this.cloudLayer) {
  1725. this.tagMsg = null;
  1726. this.viewer.imageryLayers.remove(this.cloudLayer);
  1727. this.cloudLayer = null;
  1728. }
  1729. if (this.cloudImagesLayer.length > 0) {
  1730. this.cloudImagesLayer.forEach((it) => {
  1731. this.viewer.imageryLayers.remove(it);
  1732. });
  1733. this.cloudImagesLayer = [];
  1734. }
  1735. if (this.imageryProviderV) {
  1736. this.viewer.imageryLayers.remove(this.imageryProviderV);
  1737. this.imageryProviderV = null;
  1738. }
  1739. },
  1740. // 移除降雨图
  1741. removeRainLayer() {
  1742. if (this.rainLayer) {
  1743. this.tagMsg = null;
  1744. this.viewer.imageryLayers.remove(this.rainLayer);
  1745. this.rainLayer = null;
  1746. this.setMapImageryProvider();
  1747. this.handlerAction.removeInputAction(
  1748. Cesium.ScreenSpaceEventType.LEFT_CLICK
  1749. );
  1750. }
  1751. if (this.rainImagesLayer.length > 0) {
  1752. this.rainImagesLayer.forEach((it) => {
  1753. this.viewer.imageryLayers.remove(it);
  1754. });
  1755. this.rainImagesLayer = [];
  1756. }
  1757. if (this.imageryProviderV) {
  1758. this.viewer.imageryLayers.remove(this.imageryProviderV);
  1759. this.imageryProviderV = null;
  1760. }
  1761. },
  1762. // 移除温度图
  1763. removeTemperatureLayer() {
  1764. if (this.temperatureLayer) {
  1765. this.tagMsg = null;
  1766. this.viewer.imageryLayers.remove(this.temperatureLayer);
  1767. this.temperatureLayer = null;
  1768. this.setMapImageryProvider();
  1769. this.handlerAction.removeInputAction(
  1770. Cesium.ScreenSpaceEventType.LEFT_CLICK
  1771. );
  1772. }
  1773. if (this.tempImagesLayer.length > 0) {
  1774. this.tempImagesLayer.forEach((it) => {
  1775. this.viewer.imageryLayers.remove(it);
  1776. });
  1777. this.tempImagesLayer = [];
  1778. }
  1779. if (this.imageryProviderV) {
  1780. this.viewer.imageryLayers.remove(this.imageryProviderV);
  1781. this.imageryProviderV = null;
  1782. }
  1783. },
  1784. //取消所有图层加载
  1785. cancleAllLayer() {
  1786. if (this.windLayer) {
  1787. this.removeWindLayer();
  1788. }
  1789. if (this.rainLayer || this.rainImagesLayer.length > 0) {
  1790. this.removeRainLayer();
  1791. this.stopCycling(this.rainintervalId);
  1792. }
  1793. if (this.cloudLayer || this.cloudImagesLayer.length > 0) {
  1794. this.removeCloudLayer();
  1795. this.stopCycling(this.cloudintervalId);
  1796. }
  1797. if (this.temperatureLayer || this.tempImagesLayer.length > 0) {
  1798. this.removeTemperatureLayer();
  1799. this.stopCycling(this.tempintervalId);
  1800. }
  1801. },
  1802. switchLayer() {
  1803. this.$router.push({
  1804. path: "/",
  1805. });
  1806. },
  1807. menuComTSty(val) {
  1808. this.menuComTStyB = val;
  1809. },
  1810. },
  1811. };
  1812. </script>
  1813. <style lang="less" scoped>
  1814. .dataLoading {
  1815. width: 100vw;
  1816. height: 100vh;
  1817. background: rgba(0, 0, 0, 0.5);
  1818. z-index: 999;
  1819. position: fixed;
  1820. .loadText {
  1821. position: absolute;
  1822. top: 50%;
  1823. left: 50%;
  1824. transform: translate(-50%, -50%);
  1825. background: rgba(255, 255, 255, 0.7);
  1826. padding: 15px 20px;
  1827. border-radius: 6px;
  1828. color: black;
  1829. font-size: 14px;
  1830. font-weight: bold;
  1831. }
  1832. }
  1833. .mapBox {
  1834. width: 100%;
  1835. height: 100%;
  1836. position: relative;
  1837. box-sizing: content-box;
  1838. overflow: hidden;
  1839. .menuComT {
  1840. position: fixed;
  1841. bottom: 400px;
  1842. left: 20px;
  1843. }
  1844. .menuComTSty {
  1845. position: fixed;
  1846. bottom: 20px;
  1847. left: 20px;
  1848. }
  1849. }
  1850. </style>
  1851. <style lang="less">
  1852. .el-overlay {
  1853. background-color: transparent !important;
  1854. .windModelDrawer {
  1855. width: 100% !important;
  1856. backdrop-filter: blur(15px) !important;
  1857. // background: rgba(255, 255, 255, 0.8) !important;
  1858. // background-color: rgba(20, 29, 51, 0.3) !important;
  1859. background-color: rgba(0, 0, 0, 0.3) !important;
  1860. border-radius: 10px 0 0 10px !important;
  1861. .el-drawer__header{
  1862. .el-drawer__close-btn{
  1863. .el-icon{
  1864. color: #fff !important;
  1865. }
  1866. }
  1867. }
  1868. .el-drawer__body {
  1869. overflow: hidden;
  1870. padding-top: 0;
  1871. }
  1872. }
  1873. }
  1874. .windDrawerCla {
  1875. .line {
  1876. display: flex;
  1877. flex-direction: row;
  1878. align-items: center;
  1879. justify-content: space-between;
  1880. width: 100%;
  1881. margin-bottom: 10px;
  1882. .leftContent {
  1883. width: 242px;
  1884. height: 41px;
  1885. display: flex;
  1886. align-items: center;
  1887. background: url("@/assets/cesiumImg/title_left_bg1.png") no-repeat;
  1888. span {
  1889. font-size: 16px;
  1890. font-family: Microsoft YaHei;
  1891. font-weight: 400;
  1892. color: #ffffff;
  1893. margin-left: 25px;
  1894. }
  1895. }
  1896. }
  1897. .jcxx,
  1898. .gzck {
  1899. height: 100%;
  1900. }
  1901. .spjk,
  1902. .third {
  1903. height: 80vh;
  1904. }
  1905. }
  1906. </style>