SunlightAnalysis.vue 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071
  1. <template>
  2. <div
  3. class="ZTGlobal"
  4. style="width: 100%; padding: 1rem 1.1rem 0rem; color: white"
  5. v-loading="loading"
  6. element-loading-text="正在分析中...."
  7. element-loading-spinner="el-icon-loading"
  8. element-loading-background="rgba(0, 0, 0, 0.8)"
  9. >
  10. <el-row :gutter="10">
  11. <el-col :span="24">
  12. <div class="titleHeader">
  13. <h3>参数设置</h3>
  14. </div>
  15. </el-col>
  16. </el-row>
  17. <el-row :gutter="10">
  18. <el-col :span="24">
  19. <el-row :gutter="10" style="display: flex; align-items: center">
  20. <el-col :span="24">
  21. 日 期:
  22. <el-date-picker
  23. size="mini"
  24. v-model="form.selDate"
  25. type="date"
  26. placeholder="选择日期"
  27. >
  28. </el-date-picker>
  29. </el-col>
  30. </el-row>
  31. <el-row :gutter="10" style="display: flex; align-items: center">
  32. <el-col :span="6"> 日照时间范围: </el-col>
  33. <el-col :span="18">
  34. <el-slider
  35. size="mini"
  36. v-model="datavalue"
  37. range
  38. :marks="marks"
  39. :max="24"
  40. @change="datavalueChange"
  41. >
  42. </el-slider>
  43. </el-col>
  44. </el-row>
  45. <el-row :gutter="10">
  46. <el-col :span="24">
  47. <div class="SaveCenter">
  48. <el-button size="mini" type="primary" @click="statrSunlight"
  49. >执行日照效果</el-button
  50. >
  51. <el-button size="mini" type="primary" @click="dqSunlight"
  52. >当前日照</el-button
  53. >
  54. </div>
  55. </el-col>
  56. </el-row>
  57. <el-divider></el-divider>
  58. <!-- <el-row :gutter="10">
  59. <el-col :span="12">
  60. 时间间隔:
  61. <el-input-number
  62. style="width: 50%"
  63. size="mini"
  64. v-model="form.timeInterval"
  65. ></el-input-number>
  66. </el-col>
  67. <el-col :span="12">
  68. 间距(米):
  69. <el-input-number
  70. style="width: 50%"
  71. size="mini"
  72. v-model="form.spacing"
  73. ></el-input-number>
  74. </el-col>
  75. </el-row> -->
  76. <!-- <el-row :gutter="10">
  77. <el-col :span="12" style="text-align: center">
  78. X坐标(度):
  79. <el-input-number
  80. size="mini"
  81. step="0.000001"
  82. v-model="form.x"
  83. @change="XYChange"
  84. ></el-input-number>
  85. </el-col>
  86. <el-col :span="12" style="text-align: center">
  87. Y坐标(度):
  88. <el-input-number
  89. size="mini"
  90. step="0.000001"
  91. v-model="form.y"
  92. @change="XYChange"
  93. ></el-input-number>
  94. </el-col>
  95. </el-row> -->
  96. <!-- <el-row :gutter="10">
  97. <el-col :span="12">
  98. 底部高程(米):
  99. <el-input-number
  100. style="width: 50%"
  101. size="mini"
  102. v-model="form.bottomHeight"
  103. ></el-input-number>
  104. </el-col>
  105. <el-col :span="12">
  106. 拉伸高度(米):
  107. <el-input-number
  108. style="width: 50%"
  109. size="mini"
  110. v-model="form.extrudeHeight"
  111. ></el-input-number>
  112. </el-col>
  113. </el-row> -->
  114. <el-row :gutter="10">
  115. <el-col :span="24">
  116. 阴影分析时日照时间案范围重合时为时刻阴影,不重合时计算为时间范围内阴影率
  117. </el-col>
  118. </el-row>
  119. </el-col>
  120. </el-row>
  121. <el-row :gutter="10" v-if="multiViewportMode > 1">
  122. <el-col :span="24">
  123. 选择分析视口:<el-radio-group
  124. v-model="radio"
  125. v-for="item in multiViewportMode"
  126. :key="item"
  127. >
  128. <el-radio :label="item - 1">窗口{{ item }}</el-radio>
  129. </el-radio-group>
  130. </el-col>
  131. </el-row>
  132. <div class="SaveCenter">
  133. <el-row :gutter="10">
  134. <el-col :span="24">
  135. <el-button size="mini" type="primary" @click="onSubmit"
  136. >自定义日照分析</el-button
  137. >
  138. <el-button size="mini" type="primary" @click="onDHSubmit"
  139. >大寒日</el-button
  140. >
  141. <el-button size="mini" type="primary" @click="onDZSubmit"
  142. >冬至日</el-button
  143. >
  144. </el-col>
  145. </el-row>
  146. <el-row :gutter="10">
  147. <el-col :span="24">
  148. <el-tooltip
  149. effect="dark"
  150. content="日照时长结果生成后,调整地图到合适位置,然后导出"
  151. placement="top"
  152. >
  153. <el-button
  154. size="mini"
  155. type="primary"
  156. :disabled="sdh.length == 0 ? true : false"
  157. @click="ExportResult"
  158. >导出结果</el-button
  159. >
  160. </el-tooltip>
  161. <el-button size="mini" @click="resetForm">清除</el-button>
  162. </el-col>
  163. </el-row>
  164. </div>
  165. <el-row :gutter="10">
  166. <el-col :span="24">
  167. <el-descriptions border size="mini" :column="2">
  168. <el-descriptions-item
  169. v-for="item in sdh"
  170. :key="item"
  171. :label="item.scS + ' 小时'"
  172. ><div
  173. style="width: 4.5rem; height: 100%"
  174. :style="{ 'background-color': item.fill }"
  175. >
  176. &nbsp;
  177. </div>
  178. </el-descriptions-item>
  179. </el-descriptions>
  180. </el-col>
  181. </el-row>
  182. <!-- <el-row :gutter="10">
  183. <el-col :span="24">
  184. <el-table
  185. size="mini"
  186. :data="tableData"
  187. style="width: 100%"
  188. :highlight-current-row="true"
  189. @row-click="rowClick"
  190. >
  191. <el-table-column prop="heightText" label="高程"> </el-table-column>
  192. <el-table-column prop="sunDate" label="日照时间"> </el-table-column>
  193. <el-table-column
  194. prop="shadowRadioText"
  195. label="日照率"
  196. ></el-table-column>
  197. </el-table>
  198. </el-col>
  199. </el-row> -->
  200. </div>
  201. </template>
  202. <script>
  203. let handlerPolygon = null,
  204. layers = null,
  205. shadowQuery = null,
  206. markedPoints = [];
  207. import {
  208. point,
  209. buffer,
  210. bboxPolygon,
  211. bbox,
  212. square,
  213. destination,
  214. polygon,
  215. featureCollection,
  216. interpolate,
  217. isobands,
  218. isolines,
  219. pointGrid,
  220. } from "@turf/turf";
  221. import {
  222. cartesian3ToWGS84,
  223. mapQuery,
  224. flatten,
  225. mercator2lonLat,
  226. undergroundMode,
  227. } from "@/utils/MapHelper/MapHelper.js";
  228. import { v4 as uuidv4 } from "uuid";
  229. import moment from "moment";
  230. export default {
  231. data() {
  232. return {
  233. tooltip: createTooltip(document.body),
  234. form: {
  235. timeInterval: 60,
  236. spacing: 3,
  237. selDate: new Date(),
  238. startTime: 10,
  239. endTime: 14,
  240. bottomHeight: 0,
  241. extrudeHeight: 100,
  242. x: 0,
  243. y: 0,
  244. },
  245. sdh: [
  246. { scS: "0", fill: "#313695" },
  247. { scS: "1", fill: "#1E90A8" },
  248. { scS: "2", fill: "#00B457" },
  249. { scS: "3", fill: "#B7FF01" },
  250. { scS: "4", fill: "#F2A705" },
  251. { scS: ">=5", fill: "#FF0000" },
  252. ],
  253. loading: false,
  254. datavalue: [10, 14],
  255. positions: [],
  256. points: [],
  257. // layers: null,
  258. // shadowQuery: null,
  259. marks: {
  260. 8: "8点",
  261. 12: {
  262. style: {
  263. color: "#1989FA",
  264. },
  265. label: this.$createElement("strong", "12点"),
  266. },
  267. 20: "20点",
  268. },
  269. tableData: [],
  270. multiViewportMode: 0,
  271. radio: 0,
  272. eids: [],
  273. };
  274. },
  275. props: {
  276. info: {
  277. type: Object,
  278. default: () => {
  279. return {};
  280. },
  281. },
  282. layerid: {
  283. type: String,
  284. default: "",
  285. },
  286. lydata: {
  287. type: Object,
  288. default: () => {
  289. return {};
  290. },
  291. },
  292. lyoption: {
  293. type: Object,
  294. default: () => {
  295. return {};
  296. },
  297. },
  298. },
  299. computed: {},
  300. mounted() {
  301. this.multiViewportMode = scene.multiViewportMode + 1;
  302. this.init();
  303. },
  304. methods: {
  305. init() {
  306. if (viewer.shadows == false) {
  307. viewer.shadows = true; //开启场景阴影
  308. viewer.terrainShadows = true; //地形阴影
  309. }
  310. let scene = viewer.scene;
  311. layers = scene.layers.layerQueue;
  312. //图层模型设置阴影
  313. for (let i = 0; i < layers.length; i++) {
  314. // if (layers[i].shadowType !== 2) {
  315. layers[i].shadowType = 2;
  316. // layers[i].refresh();
  317. // }
  318. }
  319. this.dqSunlight();
  320. },
  321. //自定义日照
  322. onSubmit() {
  323. this.analysis();
  324. },
  325. //大寒日
  326. onDHSubmit() {
  327. debugger;
  328. this.form.selDate = new Date(new Date().getFullYear() + "-01-20");
  329. this.datavalue = [9, 15];
  330. this.analysis();
  331. },
  332. //冬至日
  333. onDZSubmit() {
  334. // 冬至日期(东八区)的计算公式:(y×d+c)-l
  335. //   公式解读:Y=年数后2位,D=0.2422,L=闰年数,21世纪C=21.94,20世纪=22.60。
  336. //   举例说明:2088年冬至日期=[88×0.2422+21.94]-[88/4]=43-22=21,12月21日冬至。
  337. //   例外:1918年和2021年的计算结果减1日。
  338. debugger;
  339. let lastTwoDigitsOfYear = moment().format("YY");
  340. let rns = lastTwoDigitsOfYear / 4;
  341. let sjs = 21.94;
  342. let dzr = Math.floor(lastTwoDigitsOfYear * 0.2422 + sjs - rns);
  343. this.form.selDate = new Date(new Date().getFullYear() + "-12-" + dzr);
  344. this.datavalue = [9, 15];
  345. this.analysis();
  346. },
  347. //清空from
  348. resetForm() {
  349. this.clear();
  350. // this.clearmarkedPoints();
  351. },
  352. /**
  353. * 开始分析
  354. */
  355. analysis1() {
  356. let that = this;
  357. that.tableData = [];
  358. if (!handlerPolygon) {
  359. handlerPolygon = new Cesium.DrawHandler(
  360. viewer,
  361. Cesium.DrawMode.Point,
  362. Cesium.ClampMode.Space
  363. );
  364. }
  365. handlerPolygon.activate();
  366. handlerPolygon.activeEvt.addEventListener(function (isActive) {
  367. if (isActive == true) {
  368. viewer.enableCursorStyle = false;
  369. viewer._element.style.cursor = "";
  370. document.body.classList.add("drawCur");
  371. } else {
  372. viewer.enableCursorStyle = true;
  373. document.body.classList.remove("drawCur");
  374. }
  375. });
  376. handlerPolygon.movingEvt.addEventListener((windowPosition) => {
  377. that.tooltip.showAt(
  378. windowPosition,
  379. "<p>点击鼠标左键开始选择分析区域</p>"
  380. );
  381. });
  382. handlerPolygon.drawEvt.addEventListener((result) => {
  383. that.resetForm();
  384. // that.clickSunDate();
  385. that.loading = true;
  386. let scene = viewer.scene;
  387. //创建阴影查询对象
  388. if (!shadowQuery) {
  389. shadowQuery = new Cesium.ShadowQueryPoints(scene);
  390. shadowQuery.setVisibleInViewport(that.radio);
  391. shadowQuery.queryPointsEvent.addEventListener(function (e) {
  392. that.tableData = [];
  393. that.loading = false;
  394. e.forEach(function (a) {
  395. var e_cartographic = Cesium.Cartographic.fromCartesian(
  396. a.position
  397. );
  398. var e_shadowRadio = shadowQuery.getShadowRadio(e_cartographic);
  399. var e_longitude = Cesium.Math.toDegrees(e_cartographic.longitude);
  400. var e_latitude = Cesium.Math.toDegrees(e_cartographic.latitude);
  401. var e_height = e_cartographic.height;
  402. let sunForm = {};
  403. sunForm.shadowRadioText =
  404. (Number(e_shadowRadio.toFixed(2)) * 100).toFixed(2) + "%";
  405. sunForm.longitudeText = Number(e_longitude.toFixed(6));
  406. sunForm.latitudeText = Number(e_latitude.toFixed(6));
  407. sunForm.heightText = Number(e_height.toFixed(2));
  408. sunForm.dateScope =
  409. that.datavalue[0] + "时--" + that.datavalue[1] + "时";
  410. if (that.datavalue[0] != that.datavalue[1]) {
  411. sunForm.sunDate =
  412. (
  413. (that.datavalue[1] + 1 - that.datavalue[0]) *
  414. e_shadowRadio
  415. ).toFixed(2) + "小时";
  416. } else {
  417. sunForm.sunDate = "";
  418. }
  419. that.tableData.push(sunForm);
  420. });
  421. });
  422. }
  423. that.tooltip.setVisible(false);
  424. var position = result.object.position;
  425. var cartographic = Cesium.Cartographic.fromCartesian(position);
  426. var longitude = Cesium.Math.toDegrees(cartographic.longitude);
  427. var latitude = Cesium.Math.toDegrees(cartographic.latitude);
  428. var clickPoint = point([longitude, latitude]); //
  429. var buffered = buffer(clickPoint, (that.form.spacing / 2 + 1) / 1000, {
  430. units: "kilometers",
  431. });
  432. that.form.x = longitude;
  433. that.form.y = latitude;
  434. that.points = buffered.geometry.coordinates[0].flat();
  435. //设置分析对象的开始结束时间
  436. var dateValue = that.form.selDate;
  437. var st = new Date(dateValue);
  438. st.setHours(Number(that.form.startTime));
  439. shadowQuery.startTime = Cesium.JulianDate.fromDate(st);
  440. var et = new Date(dateValue);
  441. et.setHours(Number(that.form.endTime));
  442. shadowQuery.endTime = Cesium.JulianDate.fromDate(et);
  443. //设置当前时间
  444. that.setCurrentTime();
  445. shadowQuery.spacing = that.form.spacing;
  446. shadowQuery.timeInterval = that.form.timeInterval;
  447. //设置分析区域、底部高程和拉伸高度
  448. var bh = Number(that.form.bottomHeight);
  449. var eh = Number(that.form.extrudeHeight);
  450. shadowQuery.qureyRegion({
  451. position: that.points,
  452. bottom: bh,
  453. extend: eh,
  454. });
  455. shadowQuery.build();
  456. });
  457. },
  458. analysis() {
  459. let that = this;
  460. that.tableData = [];
  461. if (!handlerPolygon) {
  462. handlerPolygon = new Cesium.DrawHandler(
  463. viewer,
  464. Cesium.DrawMode.Polygon,
  465. 0
  466. );
  467. }
  468. handlerPolygon.activate();
  469. handlerPolygon.activeEvt.addEventListener(function (isActive) {
  470. if (isActive == true) {
  471. // viewer.enableCursorStyle = false;
  472. // viewer._element.style.cursor = "";
  473. document.body.classList.add("drawCur");
  474. } else {
  475. // viewer.enableCursorStyle = true;
  476. document.body.classList.remove("drawCur");
  477. }
  478. });
  479. handlerPolygon.movingEvt.addEventListener((windowPosition) => {
  480. that.tooltip.showAt(
  481. windowPosition,
  482. "<p>点击鼠标左键开始绘制分析区域</p>"
  483. );
  484. });
  485. handlerPolygon.drawEvt.addEventListener((result) => {
  486. that.tooltip.setVisible(false);
  487. that.resetForm();
  488. that.loading = true;
  489. let scene = viewer.scene;
  490. let gd = 0;
  491. //创建阴影查询对象
  492. if (!shadowQuery) {
  493. shadowQuery = new Cesium.ShadowQueryPoints(scene);
  494. shadowQuery.setVisibleInViewport(that.radio);
  495. shadowQuery.queryPointsEvent.addEventListener(function (e) {
  496. that.tableData = [];
  497. that.loading = false;
  498. let sdsd = [];
  499. e.forEach(function (a) {
  500. var e_cartographic = Cesium.Cartographic.fromCartesian(
  501. a.position
  502. );
  503. var e_shadowRadio = shadowQuery.getShadowRadio(e_cartographic);
  504. var e_longitude = Cesium.Math.toDegrees(e_cartographic.longitude);
  505. var e_latitude = Cesium.Math.toDegrees(e_cartographic.latitude);
  506. var e_height = e_cartographic.height;
  507. let sd = point([e_longitude, e_latitude]);
  508. sd.properties.value = e_shadowRadio;
  509. sdsd.push(sd);
  510. });
  511. let gsd = that.setIsoline(sdsd);
  512. // that.sdh = [];
  513. gsd.features.forEach((element) => {
  514. // let qj = element.properties.value.split("-");
  515. // let scS = (
  516. // (that.datavalue[1] + 1 - that.datavalue[0]) *
  517. // qj[0]
  518. // ).toFixed(2);
  519. // let scE = (
  520. // (that.datavalue[1] + 1 - that.datavalue[0]) *
  521. // qj[1]
  522. // ).toFixed(2);
  523. // that.sdh.push({ scS, scE, fill: element.properties.fill });
  524. element.geometry.coordinates.forEach((polygon) => {
  525. debugger;
  526. let ps = polygon[0].flat();
  527. let id = uuidv4();
  528. that.eids.push(id);
  529. viewer.entities.add({
  530. id: id,
  531. polygon: {
  532. hierarchy: Cesium.Cartesian3.fromDegreesArray(ps),
  533. material: new Cesium.Color.fromCssColorString(
  534. element.properties.fill
  535. ).withAlpha(0.9),
  536. height: gd + 1,
  537. outline: false,
  538. // outlineColor: Cesium.Color.BLACK,
  539. // outlineWidth: 2.0,
  540. // classificationType: Cesium.ClassificationType.TERRAIN,
  541. },
  542. });
  543. });
  544. });
  545. if (shadowQuery) {
  546. shadowQuery.destroy();
  547. shadowQuery = null;
  548. }
  549. });
  550. }
  551. let positions = that.positions;
  552. let points = that.points;
  553. var polygons = result.object;
  554. if (!polygons) {
  555. return;
  556. }
  557. polygons.show = false;
  558. // handlerPolygon.polyline.show = false;
  559. positions = [].concat(polygons.positions);
  560. positions = Cesium.arrayRemoveDuplicates(
  561. positions,
  562. Cesium.Cartesian3.equalsEpsilon
  563. );
  564. //遍历多边形,取出所有点
  565. for (var i = 0, len = positions.length; i < len; i++) {
  566. //转化为经纬度,并加入至临时数组
  567. var cartographic = Cesium.Cartographic.fromCartesian(
  568. polygons.positions[i]
  569. );
  570. var longitude = Cesium.Math.toDegrees(cartographic.longitude);
  571. var latitude = Cesium.Math.toDegrees(cartographic.latitude);
  572. gd = cartographic.height;
  573. points.push(longitude);
  574. points.push(latitude);
  575. }
  576. //设置分析对象的开始结束时间
  577. var dateValue = that.form.selDate;
  578. var st = new Date(dateValue);
  579. st.setHours(Number(that.form.startTime));
  580. shadowQuery.startTime = Cesium.JulianDate.fromDate(st);
  581. var et = new Date(dateValue);
  582. let endTime = 0;
  583. if (that.form.startTime != that.form.endTime) {
  584. endTime = that.form.endTime - 1;
  585. } else {
  586. endTime = that.form.endTime;
  587. }
  588. et.setHours(Number(endTime));
  589. shadowQuery.endTime = Cesium.JulianDate.fromDate(et);
  590. //设置当前时间
  591. that.setCurrentTime();
  592. shadowQuery.spacing = that.form.spacing;
  593. shadowQuery.timeInterval = that.form.timeInterval;
  594. //设置分析区域、底部高程和拉伸高度
  595. var bh = Number(gd + 1);
  596. var eh = Number(2);
  597. shadowQuery.qureyRegion({
  598. position: that.points,
  599. bottom: bh,
  600. extend: eh,
  601. });
  602. shadowQuery.build();
  603. // that.resetForm();
  604. });
  605. },
  606. ExportResult() {
  607. let that = this;
  608. let entitys = [];
  609. that.eids.forEach((id) => {
  610. let entity = viewer.entities.getById(id);
  611. if (entity) {
  612. entitys.push(entity);
  613. }
  614. });
  615. //版本缺陷无法使用俯仰角
  616. // viewer.flyTo(entitys, {
  617. // options: {
  618. // offset: new Cesium.HeadingPitchRange(
  619. // 0,
  620. // Cesium.Math.toRadians(-180),
  621. // 1000
  622. // ),
  623. // },
  624. // });
  625. let box = that.squarePolygon(entitys);
  626. viewer.camera.flyTo({
  627. destination: Cesium.Rectangle.fromDegrees(
  628. box[0][0] + 0.0002,
  629. box[0][1] - 0.0002,
  630. box[2][0] - 0.0002,
  631. box[2][1] + 0.0002
  632. ),
  633. });
  634. setTimeout(function () {
  635. var promise = scene.outputSceneToFile();
  636. Cesium.when(promise, function (base64data) {
  637. that.download(base64data);
  638. });
  639. }, 4000);
  640. },
  641. /**
  642. * 根据图片生成画布
  643. */
  644. convertImageToCanvas(image) {
  645. var canvas = document.createElement("canvas");
  646. canvas.width = image.width;
  647. canvas.height = image.height;
  648. var ctx = canvas.getContext("2d");
  649. ctx.drawImage(image, 0, 0);
  650. this.drawLegends(canvas, ctx);
  651. return canvas;
  652. },
  653. // 绘制图例
  654. drawLegends(canvas, ctx) {
  655. var legends = this.sdh;
  656. var padding = 10; // 图例与边缘的间距
  657. var lineHeight = 30; // 每行图例的高度
  658. var labW = 120;
  659. var x = canvas.width - padding - labW; // 图例的起始X坐标
  660. var y = canvas.height - legends.length * lineHeight - padding; // 图例的起始Y坐标
  661. // 绘制颜色块
  662. ctx.fillStyle = "#ffffff";
  663. ctx.fillRect(
  664. x - padding,
  665. y - padding,
  666. canvas.width - x + padding,
  667. canvas.height - y + padding
  668. );
  669. legends.forEach(function (legend, index) {
  670. // 绘制文本
  671. ctx.fillStyle = "black";
  672. ctx.fillText(
  673. legend.scS + "小时",
  674. x,
  675. y + index * lineHeight + lineHeight / 2
  676. );
  677. // 绘制颜色块
  678. ctx.fillStyle = legend.fill;
  679. ctx.fillRect(
  680. x + (labW / 3) * 2,
  681. y + index * lineHeight,
  682. 30,
  683. lineHeight
  684. );
  685. });
  686. },
  687. /**
  688. * 下载图片
  689. */
  690. download(base64data) {
  691. let that = this;
  692. var image = new Image();
  693. image.src = base64data;
  694. image.onload = function () {
  695. var canvas = that.convertImageToCanvas(image);
  696. var url = canvas.toDataURL("image/jpeg");
  697. var a = document.createElement("a");
  698. var event = new MouseEvent("click");
  699. a.download = new Date().getTime() + ".jpg"; // 指定下载图片的名称
  700. a.href = url;
  701. a.dispatchEvent(event); // 触发超链接的点击事件
  702. };
  703. },
  704. clear() {
  705. // viewer.entities.removeAll();
  706. // this.form.x = 0;
  707. // this.form.y = 0;
  708. this.tableData = [];
  709. if (handlerPolygon) {
  710. handlerPolygon.clear();
  711. handlerPolygon.deactivate();
  712. }
  713. this.tooltip.setVisible(false);
  714. if (shadowQuery) {
  715. shadowQuery.destroy();
  716. shadowQuery = null;
  717. }
  718. this.positions = [];
  719. this.points = [];
  720. // this.sdh = [];
  721. this.eids = [];
  722. },
  723. //结束时日照阴影
  724. setCurrentTime() {
  725. var et = this.form.selDate;
  726. et.setHours(Number(this.form.endTime));
  727. viewer.clock.currentTime = Cesium.JulianDate.fromDate(et);
  728. viewer.clock.multiplier = 1;
  729. viewer.clock.shouldAnimate = false;
  730. },
  731. /**
  732. * 执行日照动画
  733. */
  734. statrSunlight() {
  735. var dateVal = this.form.selDate;
  736. var startTime = new Date(dateVal);
  737. var shour = Number(this.form.startTime);
  738. var ehour = Number(this.form.endTime);
  739. if (shour > ehour) {
  740. return;
  741. }
  742. var nTimer = 0.0;
  743. var nIntervId = setInterval(function () {
  744. if (shour < ehour) {
  745. startTime.setHours(shour);
  746. startTime.setMinutes(nTimer);
  747. viewer.clock.currentTime = Cesium.JulianDate.fromDate(startTime);
  748. nTimer += 10.0;
  749. if (nTimer > 60.0) {
  750. shour += 1.0;
  751. nTimer = 0.0;
  752. }
  753. } else {
  754. clearInterval(nIntervId);
  755. }
  756. }, 20);
  757. },
  758. //当前日照效果
  759. dqSunlight() {
  760. viewer.clock.currentTime = Cesium.JulianDate.fromDate(new Date());
  761. viewer.clock.multiplier = 1;
  762. viewer.clock.shouldAnimate = true;
  763. },
  764. //日照列表行被点击
  765. rowClick(row, column, event) {
  766. this.clearmarkedPoints();
  767. var markedPoint = viewer.entities.add(
  768. new Cesium.Entity({
  769. point: new Cesium.PointGraphics({
  770. color: new Cesium.Color(1, 1, 0, 0.5),
  771. pixelSize: 15,
  772. }),
  773. position: Cesium.Cartesian3.fromDegrees(
  774. row.longitudeText,
  775. row.latitudeText,
  776. row.heightText
  777. ),
  778. })
  779. );
  780. markedPoints.push(markedPoint);
  781. },
  782. //清除点击的黄色圆圈
  783. clearmarkedPoints() {
  784. for (let i = 0; i < markedPoints.length; i++) {
  785. viewer.entities.remove(markedPoints[i]);
  786. }
  787. markedPoints = [];
  788. },
  789. // x或y变化触发
  790. XYChange() {
  791. if (!shadowQuery) {
  792. return;
  793. }
  794. this.loading = true;
  795. let that = this;
  796. var clickPoint = point([that.form.x, that.form.y]); //
  797. var buffered = buffer(clickPoint, (that.form.spacing / 2 + 1) / 1000, {
  798. units: "kilometers",
  799. });
  800. that.points = buffered.geometry.coordinates[0].flat();
  801. //设置分析区域、底部高程和拉伸高度
  802. var bh = Number(that.form.bottomHeight);
  803. var eh = Number(that.form.extrudeHeight);
  804. shadowQuery.qureyRegion({
  805. position: that.points,
  806. bottom: bh,
  807. extend: eh,
  808. });
  809. shadowQuery.build();
  810. },
  811. // 时间范围变化触发
  812. datavalueChange(val) {
  813. if (val[0] == val[1]) {
  814. this.form.timeInterval = 1;
  815. } else {
  816. this.form.timeInterval = 60;
  817. }
  818. this.form.startTime = val[0];
  819. this.form.endTime = val[1];
  820. },
  821. /**
  822. * @property {Array} features 关键点集合
  823. */
  824. setIsoline(features) {
  825. // 准备工作,创建 options
  826. const breaks = [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1];
  827. let breaksProperties = [];
  828. for (let index = 0; index < breaks.length; index++) {
  829. let time1 = Math.ceil(
  830. (this.datavalue[1] - this.datavalue[0]) * breaks[index]
  831. );
  832. switch (time1) {
  833. case 0:
  834. breaksProperties.push(this.sdh[0]);
  835. break;
  836. case 1:
  837. breaksProperties.push(this.sdh[1]);
  838. break;
  839. case 2:
  840. breaksProperties.push(this.sdh[2]);
  841. break;
  842. case 3:
  843. breaksProperties.push(this.sdh[3]);
  844. break;
  845. case 4:
  846. breaksProperties.push(this.sdh[4]);
  847. break;
  848. default:
  849. breaksProperties.push(this.sdh[5]);
  850. break;
  851. }
  852. }
  853. const interpolateOptions = {
  854. gridType: "points",
  855. property: "value",
  856. units: "degrees",
  857. weight: 20,
  858. };
  859. const isobandsOptions = {
  860. zProperty: "value",
  861. // commonProperties: {
  862. // "fill-opacity": 0.5,
  863. // },
  864. breaksProperties: breaksProperties,
  865. };
  866. // 准备工作结束
  867. // 将散点合并成要素集
  868. const points = featureCollection(features);
  869. // 使用反向距离加权(IDW)方法进行运算
  870. const grid = interpolate(points, 0.00003, interpolateOptions);
  871. // 根据参与分级的属性和分级的数组计算出等值面
  872. const isobandsData = isobands(grid, breaks, isobandsOptions);
  873. const intersection = featureCollection(isobandsData.features);
  874. return intersection;
  875. },
  876. /**
  877. * 计算边界
  878. */
  879. squarePolygon(entitys) {
  880. const polygons = [
  881. /* ... 多边形数组 ... */
  882. ];
  883. entitys.forEach((entity) => {
  884. let positions = entity.polygon.hierarchy.getValue().positions;
  885. let posts = [];
  886. positions.forEach((position) => {
  887. let xy = cartesian3ToWGS84(position);
  888. posts.push([xy.lng, xy.lat]);
  889. });
  890. let thispolygon = polygon([posts]);
  891. polygons.push(thispolygon);
  892. });
  893. // 初始化边界框变量
  894. let minX = Infinity,
  895. minY = Infinity,
  896. maxX = -Infinity,
  897. maxY = -Infinity;
  898. // 遍历每个多边形,计算其边界框,并更新最小/最大坐标
  899. polygons.forEach((polygon) => {
  900. const thisbbox = bbox(polygon);
  901. minX = Math.min(minX, thisbbox[0]);
  902. minY = Math.min(minY, thisbbox[1]);
  903. maxX = Math.max(maxX, thisbbox[2]);
  904. maxY = Math.max(maxY, thisbbox[3]);
  905. });
  906. // 计算正方形的边长
  907. const sideLength = Math.max(maxX - minX, maxY - minY);
  908. // 创建正方形的四个角点
  909. const squareCoords = [
  910. [minX, minY],
  911. [minX + sideLength, minY],
  912. [minX + sideLength, minY + sideLength],
  913. [minX, minY + sideLength],
  914. [minX, minY], // 闭合多边形
  915. ];
  916. // // 使用Turf.js创建正方形多边形
  917. // const squarePolygon = polygon([squareCoords]);
  918. return squareCoords;
  919. },
  920. },
  921. watch: {
  922. "form.selDate": function (newValue) {
  923. if (!shadowQuery) {
  924. return;
  925. }
  926. this.loading = true;
  927. let st = new Date(newValue);
  928. st.setHours(this.form.startTime);
  929. let et = new Date(newValue);
  930. et.setHours(this.form.endTime);
  931. shadowQuery.startTime = Cesium.JulianDate.fromDate(st);
  932. shadowQuery.endTime = Cesium.JulianDate.fromDate(et);
  933. },
  934. "form.startTime": function (newValue) {
  935. if (!shadowQuery) {
  936. return;
  937. }
  938. this.loading = true;
  939. let thisdate = new Date(this.form.selDate);
  940. var st = thisdate;
  941. st.setHours(Number(newValue));
  942. shadowQuery.startTime = Cesium.JulianDate.fromDate(st);
  943. },
  944. "form.endTime": function (newValue) {
  945. if (!shadowQuery) {
  946. return;
  947. }
  948. this.loading = true;
  949. let thisdate = new Date(this.form.selDate);
  950. var et = thisdate;
  951. et.setHours(Number(newValue));
  952. shadowQuery.endTime = Cesium.JulianDate.fromDate(et);
  953. },
  954. "form.timeInterval": function (newValue) {
  955. if (!shadowQuery) {
  956. return;
  957. }
  958. this.loading = true;
  959. shadowQuery.timeInterval = Number(newValue);
  960. shadowQuery.build();
  961. },
  962. "form.spacing": function (newValue) {
  963. if (!shadowQuery) {
  964. return;
  965. }
  966. this.loading = true;
  967. shadowQuery.spacing = Number(newValue);
  968. shadowQuery.build();
  969. },
  970. "form.bottomHeight": function (newValue) {
  971. if (!shadowQuery) {
  972. return;
  973. }
  974. this.loading = true;
  975. var bh = Number(newValue);
  976. var eh = Number(this.form.extrudeHeight);
  977. shadowQuery.qureyRegion({
  978. position: this.points,
  979. bottom: bh,
  980. extend: eh,
  981. });
  982. shadowQuery.build();
  983. },
  984. "form.extrudeHeight": function (newValue) {
  985. if (!shadowQuery) {
  986. return;
  987. }
  988. this.loading = true;
  989. var bh = Number(this.form.bottomHeight);
  990. var eh = Number(newValue);
  991. shadowQuery.qureyRegion({
  992. position: this.points,
  993. bottom: bh,
  994. extend: eh,
  995. });
  996. shadowQuery.build();
  997. },
  998. },
  999. beforeDestroy() {
  1000. this.clear();
  1001. this.clearmarkedPoints();
  1002. if (shadowQuery) {
  1003. shadowQuery.destroy();
  1004. }
  1005. if (handlerPolygon) {
  1006. handlerPolygon.deactivate();
  1007. }
  1008. handlerPolygon = undefined;
  1009. this.dqSunlight();
  1010. },
  1011. };
  1012. </script>
  1013. <style lang="scss">
  1014. @import "@/../../zt.scss";
  1015. </style>
  1016. <style lang="scss" scoped>
  1017. .el-card {
  1018. border: 0px solid #02a7f0;
  1019. }
  1020. .el-form-item {
  1021. margin-bottom: 0;
  1022. }
  1023. .slider_padding {
  1024. padding: 0rem 0.5rem;
  1025. }
  1026. </style>