topographicMap.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  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" size="small" @click="resetViewport">初始化视角</el-button>
  6. <el-button type="primary" size="small" @click="switchLayer">返回</el-button>
  7. <el-tooltip
  8. class="box-item"
  9. effect="dark"
  10. :content="`点击${allyShow ? '隐藏' : '常显'}菜单栏`"
  11. placement="bottom-end"
  12. >
  13. <el-icon
  14. style="margin-left: 20px"
  15. size="20px"
  16. :color="allyShow ? '#1890ff' : '#f25656'"
  17. @click="allyShow = !allyShow"
  18. >
  19. <House
  20. :style="`transform: rotate(${
  21. allyShow ? -45 : 45
  22. }deg); transition: 0.2s; cursor: pointer;`"
  23. />
  24. </el-icon>
  25. </el-tooltip>
  26. </div>
  27. <comModelDialog :showcomModelDia="showcomModelDia" :modelVal="modelVal" @showDia="showComDia" />
  28. </div>
  29. </template>
  30. <script>
  31. import * as Cesium from "../../Cesium";
  32. import "../../Cesium/Widgets/widgets.css";
  33. import fjLonLatJson from "./fjLonLat.json";
  34. import comModelDialog from "@/components/comModelDialog.vue"
  35. export default {
  36. components: {
  37. comModelDialog
  38. },
  39. data() {
  40. return {
  41. allyShow: true,
  42. viewer: null,
  43. showcomModelDia: false,
  44. modelVal: null
  45. }
  46. },
  47. mounted() {
  48. this.initCesium();
  49. },
  50. methods: {
  51. async initCesium() {
  52. // Cesium.Ion.defaultAccessToken =
  53. // "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIwYTQwNDk3MC05YTZkLTQ2ZTEtODc0MS1lZTFkYjFlOTFmNmQiLCJpZCI6MTcyNDQ1LCJpYXQiOjE3NTQ4ODA4MzF9.KnhENYiHxNwTkhTWRA-lHqG59coLVT2FsIyOru2TV3E";
  54. const box = document.getElementById('cesiumContainer')
  55. const viewer = new Cesium.Viewer(box, {
  56. animation: false,//是否创建动画小器件,左下角仪表
  57. baseLayerPicker: false,//是否显示图层选择器,右上角图层选择按钮
  58. baseLayer: false, // 不显示默认图层
  59. fullscreenButton: false,//是否显示全屏按钮,右下角全屏选择按钮
  60. timeline: false,//是否显示时间轴
  61. infoBox: false,//是否显示信息框
  62. sceneModePicker: false,//是否显示场景模式切换按钮
  63. vrButton: false,
  64. geocoder: false,//是否显示地理编码按钮
  65. homeButton: false,//是否显示地图导航按钮
  66. selectionIndicator: false,
  67. navigationHelpButton: false,
  68. shouldAnimate: true,
  69. imageryProvider: false, //控制默认底图的显示
  70. showGroundAtmosphere: false,
  71. depthTestAgainstTerrain: true,
  72. dynamicAtmosphereLightingFromSun: false,
  73. })
  74. const url = 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer'
  75. const layer = Cesium.ImageryLayer.fromProviderAsync(
  76. Cesium.ArcGisMapServerImageryProvider.fromUrl(url)
  77. )
  78. viewer.imageryLayers.add(layer)
  79. viewer._cesiumWidget._creditContainer.style.display = "none";
  80. viewer.scene.globe.enableLighting = false;
  81. viewer.scene.sun.show = false;
  82. viewer.scene.moon.show = false;
  83. viewer.shadows = false;
  84. viewer.scene.skyAtmosphere.show = true;
  85. viewer._cesiumWidget._creditContainer.style.display = "none";
  86. viewer.terrainProvider = await Cesium.createWorldTerrainAsync({
  87. requestWaterMask: true,
  88. requestVertexNormals: true,
  89. });
  90. // this.csceneElliposid(viewer)
  91. viewer.camera.flyTo({
  92. // destination: Cesium.Cartesian3.fromDegrees(106.44, 37.27, 2000),// 场站位置
  93. destination: Cesium.Cartesian3.fromDegrees(106.712885, 37.413269, 8000),
  94. orientation: {
  95. heading: 0.9,
  96. pitch: -0.5,
  97. roll: 0,
  98. },
  99. duration: 3,
  100. });
  101. // 添加一些3D风机模型
  102. fjLonLatJson.data.forEach(e => {
  103. this.addModel(
  104. viewer,
  105. "./static/model/fjSolo/model.glb",
  106. "风机",
  107. e.longitude,
  108. e.latitude,
  109. 0.5,
  110. e
  111. );
  112. });
  113. //添加3D光伏模型
  114. this.addModel(
  115. viewer,
  116. "./static/model/SolarPowerPlant/model.glb",
  117. "光伏",
  118. 106.797343,
  119. 37.546013,
  120. 20,
  121. null
  122. );
  123. // 创建一个方向光
  124. const directionalLight = new Cesium.DirectionalLight({
  125. direction: Cesium.Cartesian3.normalize(
  126. new Cesium.Cartesian3(0, 0, 1), // 光照方向(从左前上方来)
  127. new Cesium.Cartesian3()
  128. ),
  129. color: Cesium.Color.WHITE, // 光源颜色
  130. intensity: 10.0 // 强度(默认 1.0,可调高)
  131. });
  132. // 替换默认光源
  133. viewer.scene.light = directionalLight;
  134. this.viewer = viewer
  135. },
  136. addModel(viewer, uri, name, lon, lat, scale, val) {
  137. const hpRoll = new Cesium.HeadingPitchRoll(90.0, 0.0, 0.0);
  138. const position = Cesium.Cartesian3.fromDegrees(lon, lat);
  139. const orientation = Cesium.Transforms.headingPitchRollQuaternion(
  140. position,
  141. hpRoll
  142. );
  143. const entity = viewer.entities.add({
  144. name, // 模型名称
  145. position, // 模型位置
  146. orientation, // 模型朝向
  147. model: {
  148. uri,
  149. scale,
  150. // 模型贴地
  151. heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
  152. },
  153. //添加标签
  154. label: {
  155. text: val ? val.name : name, // 标签文字
  156. font: '14px sans-serif', // 字体
  157. fillColor: Cesium.Color.YELLOW, // 填充颜色
  158. outlineColor: Cesium.Color.BLACK, // 描边颜色
  159. outlineWidth: 2, // 描边宽度
  160. style: Cesium.LabelStyle.FILL_AND_OUTLINE, // 填充+描边
  161. verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // 文本在模型上方
  162. horizontalOrigin: Cesium.HorizontalOrigin.CENTER, // 水平居中
  163. eyeOffset: new Cesium.Cartesian3(50, 1150, 5), // 向上 15 米
  164. translucencyByDistance: new Cesium.NearFarScalar(1.0e6, 1.0, 6.0e6, 0.0)
  165. }
  166. });
  167. let that = this
  168. // 创建事件处理器
  169. const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
  170. handler.setInputAction(function(click) {
  171. const picked = viewer.scene.pick(click.position);
  172. if (picked && picked.id === entity) {
  173. console.log('你点击了标签或模型!', entity);
  174. console.log('标签或模型数据!', val);
  175. // alert('你点击了: ' + entity.label.text.getValue());
  176. // this.$refs.comModelDialog.init(val)
  177. if (name !== '光伏') {
  178. that.showcomModelDia = true
  179. that.modelVal = val
  180. }
  181. }
  182. }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
  183. },
  184. showComDia(val) {
  185. this.showcomModelDia = val
  186. this.modelVal = null
  187. },
  188. csceneElliposid(viewer) {
  189. let that = this
  190. // 获取 scene 和 ellipsoid
  191. var scene = viewer.scene;
  192. var labels = viewer.scene.primitives.add(new Cesium.LabelCollection());
  193. // 创建 ScreenSpaceEventHandler
  194. var handler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
  195. // 监听左键点击事件
  196. handler.setInputAction(async(click) => {
  197. // 获取点击位置的笛卡尔坐标
  198. var position = click.position;
  199. if (!position) return;
  200. // 使用 globe.pick 获取包含地形高度的坐标
  201. var ray = viewer.camera.getPickRay(position);
  202. var cartesian = viewer.scene.globe.pick(ray, viewer.scene);
  203. if (cartesian) {
  204. // 转换为地理坐标
  205. var cartographic = Cesium.Cartographic.fromCartesian(cartesian);
  206. var longitude = Cesium.Math.toDegrees(cartographic.longitude);
  207. var latitude = Cesium.Math.toDegrees(cartographic.latitude);
  208. var height = cartographic.height;
  209. // 格式化坐标
  210. var text = `经度: ${longitude.toFixed(6)}°\n纬度: ${latitude.toFixed(6)}°`;
  211. // 创建一个标签
  212. var label = labels.add({
  213. position: cartesian,
  214. text: text,
  215. font: '14px monospace',
  216. fillColor: Cesium.Color.fromCssColorString('#1d70df'),
  217. // style: Cesium.LabelStyle.FILL_AND_OUTLINE,
  218. // outlineColor: Cesium.Color.BLACK,
  219. outlineWidth: 2,
  220. verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // 标签在点的上方
  221. pixelOffset: new Cesium.Cartesian2(0, -20), // 向上偏移一点
  222. disableDepthTest: true, // 让标签始终可见(即使在地球背面)
  223. scale: 0.8
  224. });
  225. // 5秒后移除标签
  226. setTimeout(function() {
  227. labels.remove(label);
  228. }, 5000);
  229. console.log(`点击坐标: ${longitude.toFixed(6)}, ${latitude.toFixed(6)}, ${height.toFixed(2)}m`);
  230. }
  231. }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
  232. },
  233. // 重置视角
  234. resetViewport() {
  235. // 设置初始视图为宁夏
  236. this.viewer.camera.flyTo({
  237. destination: Cesium.Cartesian3.fromDegrees(106.712885, 37.413269, 8000),
  238. orientation: {
  239. heading: 0.9,
  240. pitch: -0.5,
  241. roll: 0,
  242. },
  243. duration: 3,
  244. });
  245. },
  246. switchLayer() {
  247. this.$router.push({
  248. path: '/'
  249. })
  250. }
  251. }
  252. }
  253. </script>
  254. <style lang="less" scoped>
  255. .mapBox {
  256. width: 100%;
  257. height: 100%;
  258. position: relative;
  259. box-sizing: content-box;
  260. .menuBox {
  261. position: absolute;
  262. left: 0;
  263. top: 0;
  264. width: 100%;
  265. background: #fff;
  266. opacity: 0.8;
  267. display: flex;
  268. justify-content: flex-end;
  269. align-items: center;
  270. padding: 10px;
  271. &.switch {
  272. opacity: 0;
  273. transition: 0.2s;
  274. &:hover {
  275. opacity: 1;
  276. transition: 0.2s;
  277. }
  278. }
  279. }
  280. }
  281. </style>