cesium.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. <template>
  2. <div class="mapBox">
  3. <div id="cesiumContainer" style="width: 100%; height: 100vh"></div>
  4. <div class="menuBox" :class="allyShow ? '' : 'switch'">
  5. <el-button type="primary" @click="resetViewport()">初始化视角</el-button>
  6. <el-button
  7. :type="windLayer ? 'danger' : 'primary'"
  8. @click="switchWindLayer"
  9. >{{ windLayer ? "关闭" : "显示" }}风场图</el-button
  10. >
  11. <el-button
  12. :type="cloudLayer ? 'danger' : 'primary'"
  13. @click="switchCloudLayer"
  14. >{{ cloudLayer ? "关闭" : "显示" }}云图</el-button
  15. >
  16. <el-button
  17. :type="rainLayer ? 'danger' : 'primary'"
  18. @click="switchRainLayer"
  19. >{{ rainLayer ? "关闭" : "显示" }}降雨图</el-button
  20. >
  21. <el-tooltip
  22. class="box-item"
  23. effect="dark"
  24. :content="`点击${allyShow ? '隐藏' : '常显'}菜单栏`"
  25. placement="bottom-end"
  26. >
  27. <el-icon
  28. style="margin-left: 20px"
  29. size="20px"
  30. :color="allyShow ? '#1890ff' : '#f25656'"
  31. @click="allyShow = !allyShow"
  32. >
  33. <House
  34. :style="`transform: rotate(${
  35. allyShow ? -45 : 45
  36. }deg); transition: 0.2s; cursor: pointer;`"
  37. />
  38. </el-icon>
  39. </el-tooltip>
  40. </div>
  41. </div>
  42. </template>
  43. <script>
  44. import * as Cesium from "../Cesium";
  45. import "../Cesium/Widgets/widgets.css";
  46. import Windy from "../assets/wind/Windy_source.js";
  47. import SingleImageProvider from "../assets/wind/SingleImageProvider";
  48. import basicGeoJson from "../assets/geoJson/basic.json";
  49. import windLineJson from "../assets/geoJson/windLine_2017121300.json";
  50. import axios from "axios";
  51. export default {
  52. name: "CesiumMap",
  53. data() {
  54. return {
  55. allyShow: false,
  56. viewer: null,
  57. windLayer: null, // 风场图
  58. windLayerTimmer: null, // 风场图计时器
  59. cloudLayer: null, // 卫星云图
  60. rainLayer: null, //
  61. };
  62. },
  63. mounted() {
  64. this.initCesium();
  65. },
  66. unmounted() {
  67. if (this.windLayer !== null) {
  68. clearInterval(this.windLayerTimmer);
  69. this.windLayer.removeLines();
  70. this.windLayer = null;
  71. }
  72. },
  73. methods: {
  74. initCesium() {
  75. // 需要从 https://cesium.com/ion/signup 获取
  76. Cesium.Ion.defaultAccessToken =
  77. "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIwYTQwNDk3MC05YTZkLTQ2ZTEtODc0MS1lZTFkYjFlOTFmNmQiLCJpZCI6MTcyNDQ1LCJpYXQiOjE3NTQ4ODA4MzF9.KnhENYiHxNwTkhTWRA-lHqG59coLVT2FsIyOru2TV3E";
  78. const viewer = new Cesium.Viewer("cesiumContainer", {
  79. baseLayerPicker: false,
  80. animation: false,
  81. vrButton: false,
  82. geocoder: false,
  83. homeButton: false,
  84. infoBox: false,
  85. sceneModePicker: false,
  86. selectionIndicator: false,
  87. timeline: false,
  88. fullscreenButton: false,
  89. navigationHelpButton: false,
  90. shouldAnimate: true,
  91. });
  92. // 隐藏logo
  93. // this.$nextTick(() => {
  94. // document.querySelector(".cesium-viewer-bottom")?.remove();
  95. // });
  96. // 隐藏logo
  97. viewer.cesiumWidget.creditContainer.style.display = "none";
  98. // 添加中文底图
  99. const imageryProvider = new Cesium.UrlTemplateImageryProvider({
  100. url: "https://webst02.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}",
  101. credit: "高德地图",
  102. });
  103. viewer.imageryLayers.addImageryProvider(imageryProvider);
  104. // 添加一些3D模型
  105. // const addModel = (name, lon, lat, height) => {
  106. // viewer.entities.add({
  107. // name: name,
  108. // position: Cesium.Cartesian3.fromDegrees(lon, lat, height),
  109. // model: {
  110. // uri: "/sample-data/models/CesiumAir/Cesium_Air.glb",
  111. // scale: 50000.0,
  112. // },
  113. // });
  114. // };
  115. // addModel("模型1", 116.4, 39.9, 1000);
  116. // addModel("模型2", 121.47, 31.23, 1000);
  117. // 添加点击事件显示坐标
  118. viewer.screenSpaceEventHandler.setInputAction((movement) => {
  119. const cartesian = viewer.camera.pickEllipsoid(
  120. movement.position,
  121. viewer.scene.globe.ellipsoid
  122. );
  123. if (cartesian) {
  124. const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
  125. const lon = Cesium.Math.toDegrees(cartographic.longitude).toFixed(5);
  126. const lat = Cesium.Math.toDegrees(cartographic.latitude).toFixed(5);
  127. viewer.entities.removeAll();
  128. viewer.entities.add({
  129. position: cartesian,
  130. point: {
  131. pixelSize: 10,
  132. color: Cesium.Color.RED,
  133. },
  134. label: {
  135. text: `经度: ${lon}°, 纬度: ${lat}°`,
  136. font: '16px "Microsoft YaHei"',
  137. fillColor: Cesium.Color.WHITE,
  138. outlineColor: Cesium.Color.BLACK,
  139. outlineWidth: 2,
  140. style: Cesium.LabelStyle.FILL_AND_OUTLINE,
  141. pixelOffset: new Cesium.Cartesian2(0, -30),
  142. },
  143. });
  144. }
  145. }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
  146. this.viewer = viewer;
  147. this.initGeoJsonData();
  148. },
  149. // 初始化 geoJson 数据
  150. async initGeoJsonData() {
  151. // 创建GeoJSON数据源
  152. await new Cesium.GeoJsonDataSource.load(basicGeoJson, {
  153. stroke: Cesium.Color.WHITE, // 边界线颜色
  154. fill: Cesium.Color.BLACK.withAlpha(0.1), // 填充颜色
  155. strokeWidth: 1, // 边界线宽度
  156. markerSymbol: "?", // 点要素的符号
  157. }).then((dataSource) => {
  158. // 添加到视图
  159. this.viewer.dataSources.add(dataSource);
  160. var entities = dataSource.entities.values;
  161. for (let i = 0; i < entities.length; i++) {
  162. let entity = entities[i];
  163. let polyPositions = entity.polygon.hierarchy.getValue(
  164. Cesium.JulianDate.now()
  165. ).positions;
  166. //单独设置线条样式
  167. var positions = entity.polygon.hierarchy._value.positions;
  168. entity.polyline = {
  169. positions: positions,
  170. width: 1,
  171. outline: false,
  172. };
  173. }
  174. // 添加中文标签图层
  175. const labelLayer = new Cesium.LabelCollection();
  176. this.viewer.scene.primitives.add(labelLayer);
  177. const cities = [];
  178. basicGeoJson?.features?.forEach((ele) => {
  179. if (Array.isArray(ele.properties.centroid)) {
  180. const name = ele.properties.name;
  181. const lon = ele.properties.centroid[0];
  182. const lat = ele.properties.centroid[1];
  183. cities.push({ name, lon, lat });
  184. }
  185. });
  186. cities.forEach((city) => {
  187. labelLayer.add({
  188. position: Cesium.Cartesian3.fromDegrees(city.lon, city.lat),
  189. text: city.name,
  190. font: 'bold 16px "Microsoft YaHei", sans-serif',
  191. fillColor: Cesium.Color.YELLOW,
  192. outlineColor: Cesium.Color.BLACK,
  193. outlineWidth: 2,
  194. style: Cesium.LabelStyle.FILL_AND_OUTLINE,
  195. pixelOffset: new Cesium.Cartesian2(0, 0), // 设置为0
  196. horizontalOrigin: Cesium.HorizontalOrigin.CENTER, // 水平居中
  197. verticalOrigin: Cesium.VerticalOrigin.CENTER, // 垂直居中
  198. });
  199. });
  200. this.resetViewport();
  201. });
  202. },
  203. // 重置视角
  204. resetViewport(height = 0) {
  205. // 设置初始视图为宁夏
  206. this.viewer.camera.flyTo({
  207. destination: Cesium.Cartesian3.fromDegrees(
  208. 106.169866,
  209. 38.46637,
  210. height || 8000000
  211. ),
  212. orientation: {
  213. heading: Cesium.Math.toRadians(0),
  214. pitch: Cesium.Math.toRadians(-90),
  215. roll: 0.0,
  216. },
  217. duration: 1.0,
  218. });
  219. },
  220. // 添加风场图
  221. showWindLayer() {
  222. if (!this.windLayer) {
  223. this.resetViewport(20000000);
  224. this.windLayer = new Windy(windLineJson, this.viewer);
  225. this.windLayerTimmer = setInterval(() => {
  226. this.windLayer.animate();
  227. }, 200);
  228. }
  229. },
  230. // 将风向角度数据转换为矢量坐标
  231. windToVector(speed, direction) {
  232. // 转换为弧度(气象角度:0°=北风,90°=东风)
  233. const rad = Cesium.Math.toRadians(direction);
  234. // 计算UV分量(U: 东西方向,V: 南北方向)
  235. return {
  236. u: -speed * Math.sin(rad), // 东正西负
  237. v: -speed * Math.cos(rad), // 北正南负
  238. };
  239. },
  240. // 显示云图
  241. showCloudLayer() {
  242. // 设置云图位置(示例:覆盖中国区域)
  243. // const rectangle = Cesium.Rectangle.fromDegrees(
  244. // 73.66, // 西经
  245. // 18.16, // 南纬
  246. // 135.05, // 东经
  247. // 53.55 // 北纬
  248. // );
  249. const now = new Date();
  250. const year = now.getFullYear();
  251. const month = String(now.getMonth() + 1).padStart(2, "0");
  252. const day = String(now.getDate()).padStart(2, "0");
  253. const hour = String(Math.floor(now.getHours() / 3) * 3).padStart(2, "0"); // 每3小时更新
  254. // 创建云图图层
  255. const cloudLayer = this.viewer.imageryLayers.addImageryProvider(
  256. new Cesium.UrlTemplateImageryProvider({
  257. url: `https://data.ventusky.com/${year}/${month}/${day}/icon/whole_world/hour_${hour}/icon_oblacnost_${year}${month}${day}_${hour}.jpg`,
  258. rectangle: Cesium.Rectangle.fromDegrees(-180, -90, 180, 90),
  259. credit: "实时卫星云图",
  260. })
  261. );
  262. // 设置蓝色调效果
  263. cloudLayer.alpha = 0.75; // 透明度
  264. cloudLayer.brightness = 1; // 亮度
  265. cloudLayer.contrast = 1; // 对比度
  266. this.cloudLayer = cloudLayer;
  267. },
  268. // 移除风场图
  269. removeWindLayer() {
  270. if (this.windLayer) {
  271. clearInterval(this.windLayerTimmer);
  272. this.windLayer.removeLines();
  273. this.viewer.imageryLayers.remove(this.windLayer);
  274. this.windLayer = null;
  275. }
  276. },
  277. // 移除卫星云图
  278. removeCloudLayer() {
  279. if (this.cloudLayer) {
  280. this.viewer.imageryLayers.remove(this.cloudLayer);
  281. this.cloudLayer = null;
  282. }
  283. },
  284. // 切换风场图显隐
  285. switchWindLayer() {
  286. if (this.windLayer) {
  287. this.removeWindLayer();
  288. } else {
  289. this.showWindLayer();
  290. }
  291. },
  292. // 切换微星图显隐
  293. switchCloudLayer() {
  294. if (this.cloudLayer) {
  295. this.removeCloudLayer();
  296. } else {
  297. this.showCloudLayer();
  298. }
  299. },
  300. switchRainLayer() {
  301. this.$router.push({
  302. path: "/satellitecloudchart",
  303. });
  304. },
  305. },
  306. };
  307. </script>
  308. <style lang="less" scoped>
  309. .mapBox {
  310. width: 100%;
  311. height: 100%;
  312. position: relative;
  313. box-sizing: content-box;
  314. .menuBox {
  315. position: absolute;
  316. left: 0;
  317. top: 0;
  318. width: 100%;
  319. background: #fff;
  320. display: flex;
  321. justify-content: flex-end;
  322. align-items: center;
  323. padding: 10px;
  324. &.switch {
  325. opacity: 0;
  326. transition: 0.2s;
  327. &:hover {
  328. opacity: 1;
  329. transition: 0.2s;
  330. }
  331. }
  332. }
  333. }
  334. </style>