HomeView.vue 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. <template>
  2. <div
  3. class="home"
  4. >
  5. <div id="map" />
  6. <div class="panel">
  7. <el-select
  8. v-model="timeInDay"
  9. placeholder="Select"
  10. size="large"
  11. >
  12. <el-option
  13. v-for="item in timeOptions"
  14. :key="item.value"
  15. :label="item.label"
  16. :value="item.value"
  17. />
  18. </el-select>
  19. <el-checkbox
  20. v-model="isShowLabels"
  21. label="show labels"
  22. size="large"
  23. />
  24. <el-checkbox
  25. v-model="isShowTerrain"
  26. label="show terrain"
  27. size="large"
  28. />
  29. <el-checkbox
  30. v-model="isShowFog"
  31. label="show fog"
  32. size="large"
  33. />
  34. <el-checkbox
  35. v-model="isAutoRotate"
  36. label="auto rotate"
  37. size="large"
  38. />
  39. </div>
  40. </div>
  41. </template>
  42. <script setup>
  43. import { ref, computed, watch, onMounted } from "vue"
  44. import { useRoute, useRouter } from "vue-router"
  45. import { useStore } from "vuex"
  46. import mapboxgl from 'mapbox-gl'
  47. const route = useRoute()
  48. const router = useRouter()
  49. const store = useStore()
  50. mapboxgl.accessToken = 'pk.eyJ1IjoiamlidmFnIiwiYSI6ImNsdDVibmxvZDAwbjcyanAzNmpzOHpoeHUifQ.KKYv3iK0IiL37rNDYkb-dQ'
  51. let map = null
  52. onMounted(() => {
  53. map = new mapboxgl.Map({
  54. container: 'map', // container ID
  55. center: [2.294444, 48.858056], // starting position [lng, lat]
  56. zoom: 15.6, // starting zoom
  57. pitch: 72,
  58. })
  59. map.on('style.load', () => {
  60. // 隐藏标签。
  61. map.setConfigProperty('basemap', 'showPlaceLabels', false)
  62. map.setConfigProperty('basemap', 'showRoadLabels', false)
  63. map.setConfigProperty('basemap', 'showPointOfInterestLabels', false)
  64. map.setConfigProperty('basemap', 'showTransitLabels', false)
  65. // 准备 3D terrain source
  66. map.addSource('mapbox-dem', {
  67. 'type': 'raster-dem',
  68. 'url': 'mapbox://mapbox.terrain-rgb',
  69. 'tileSize': 512,
  70. 'maxzoom': 14
  71. })
  72. })
  73. map.on('click', (e) => {
  74. console.log(e.lngLat.wrap())
  75. })
  76. map.on('load', () => {
  77. // todo: 操控
  78. // 桥梁高亮
  79. map.addSource('bridge-demo-1', {
  80. 'type': 'geojson',
  81. 'data': {
  82. 'type': 'Feature',
  83. 'properties': {},
  84. 'geometry': {
  85. 'type': 'LineString',
  86. 'coordinates': [
  87. [
  88. 2.291370903863708,
  89. 48.860291730262816
  90. ],
  91. [
  92. 2.2926683589130334,
  93. 48.85941742662925
  94. ],
  95. ]
  96. }
  97. }
  98. })
  99. map.addSource('bridge-demo-2', {
  100. 'type': 'geojson',
  101. 'data': {
  102. 'type': 'Feature',
  103. 'properties': {},
  104. 'geometry': {
  105. 'type': 'LineString',
  106. 'coordinates': [
  107. [
  108. 2.2867527694825185,
  109. 48.85655006915218
  110. ],
  111. [
  112. 2.28846012300869,
  113. 48.85483140541177
  114. ],
  115. ]
  116. }
  117. }
  118. })
  119. map.addSource('bridge-demo-3', {
  120. 'type': 'geojson',
  121. 'data': {
  122. 'type': 'Feature',
  123. 'properties': {},
  124. 'geometry': {
  125. 'type': 'LineString',
  126. 'coordinates': [
  127. [
  128. 2.3016957824563633,
  129. 48.86404969411541
  130. ],
  131. [
  132. 2.3018487764027213,
  133. 48.862882778017564
  134. ],
  135. ]
  136. }
  137. }
  138. })
  139. map.addLayer({
  140. 'id': 'bridge-demo-1',
  141. 'type': 'line',
  142. 'source': 'bridge-demo-1',
  143. 'layout': {
  144. 'line-join': 'round',
  145. 'line-cap': 'round'
  146. },
  147. 'paint': {
  148. 'line-color': 'rgba(244, 208, 133, 0.8)',
  149. 'line-width': 40,
  150. }
  151. })
  152. map.addLayer({
  153. 'id': 'bridge-demo-2',
  154. 'type': 'line',
  155. 'source': 'bridge-demo-2',
  156. 'layout': {
  157. 'line-join': 'round',
  158. 'line-cap': 'round'
  159. },
  160. 'paint': {
  161. 'line-color': 'rgba(244, 208, 133, 0.8)',
  162. 'line-width': 40,
  163. }
  164. })
  165. map.addLayer({
  166. 'id': 'bridge-demo-3',
  167. 'type': 'line',
  168. 'source': 'bridge-demo-3',
  169. 'layout': {
  170. 'line-join': 'round',
  171. 'line-cap': 'round'
  172. },
  173. 'paint': {
  174. 'line-color': 'rgba(244, 208, 133, 0.8)',
  175. 'line-width': 40,
  176. }
  177. })
  178. // todo:操控
  179. // 操场高亮
  180. // Add a data source containing GeoJSON data.
  181. map.addSource('playground', {
  182. 'type': 'geojson',
  183. 'data': {
  184. 'type': 'Feature',
  185. 'geometry': {
  186. 'type': 'Polygon',
  187. // These coordinates outline playground.
  188. 'coordinates': [
  189. [
  190. [2.290685454637469, 48.85648212786501],
  191. [2.290678167471242, 48.85674317251818],
  192. [2.2909987705963886, 48.856988869994325],
  193. [2.2913584104396705, 48.85706691648497],
  194. [2.2926340590652217, 48.856240774123734],
  195. [2.2926899302722177, 48.85607261804398],
  196. [2.291903329216666, 48.85567435087097],
  197. [2.2916458346767286, 48.855731990701315],
  198. ]
  199. ]
  200. }
  201. }
  202. })
  203. map.addLayer({
  204. 'id': 'playground',
  205. 'type': 'fill',
  206. 'source': 'playground', // reference the data source
  207. 'layout': {},
  208. 'paint': {
  209. 'fill-color': '#0080ff', // blue color fill
  210. 'fill-opacity': 0.5
  211. }
  212. })
  213. })
  214. })
  215. // 时间选择
  216. const timeOptions = [
  217. {
  218. label: 'dusk',
  219. value: 'dusk',
  220. },
  221. {
  222. label: 'day',
  223. value: 'day',
  224. },
  225. {
  226. label: 'dawn',
  227. value: 'dawn',
  228. },
  229. {
  230. label: 'night',
  231. value: 'night',
  232. },
  233. ]
  234. const timeInDay = ref(timeOptions[1].value)
  235. watch(timeInDay, (v) => {
  236. map.setConfigProperty('basemap', 'lightPreset', v) // dusk, dawn, day, and night
  237. if (isShowFog.value) {
  238. if (timeInDay.value === 'day') {
  239. map.setFog({
  240. 'range': [-1, 2],
  241. 'horizon-blend': 0.3,
  242. 'color': 'white',
  243. 'high-color': '#add8e6',
  244. 'space-color': '#d8f2ff',
  245. 'star-intensity': 0.0
  246. })
  247. } else if (timeInDay.value === 'dusk' || timeInDay.value === 'dawn') {
  248. map.setFog({
  249. 'range': [-1, 2],
  250. 'horizon-blend': 0.3,
  251. 'color': '#999',
  252. 'high-color': '#add8e6',
  253. 'space-color': '#d8f2ff',
  254. 'star-intensity': 0.4
  255. })
  256. } else {
  257. map.setFog({
  258. 'range': [-1, 2],
  259. 'horizon-blend': 0.3,
  260. 'color': '#242B4B',
  261. 'high-color': '#161B36',
  262. 'space-color': '#0B1026',
  263. 'star-intensity': 0.8
  264. })
  265. }
  266. }
  267. })
  268. // 标签开关
  269. const isShowLabels = ref(false)
  270. watch(isShowLabels, (v) => {
  271. map.setConfigProperty('basemap', 'showPlaceLabels', v)
  272. map.setConfigProperty('basemap', 'showRoadLabels', v)
  273. map.setConfigProperty('basemap', 'showPointOfInterestLabels', v)
  274. map.setConfigProperty('basemap', 'showTransitLabels', v)
  275. })
  276. // 地形开关
  277. const isShowTerrain = ref(false)
  278. watch(isShowTerrain, (v) => {
  279. if (v) {
  280. map.setTerrain({
  281. 'source': 'mapbox-dem',
  282. 'exaggeration': 1
  283. })
  284. } else {
  285. map.setTerrain(null)
  286. }
  287. })
  288. // 雾开关
  289. const isShowFog = ref(false)
  290. watch(isShowFog, (v) => {
  291. if (v) {
  292. if (timeInDay.value === 'day') {
  293. map.setFog({
  294. 'range': [-1, 2],
  295. 'horizon-blend': 0.3,
  296. 'color': 'white',
  297. 'high-color': '#add8e6',
  298. 'space-color': '#d8f2ff',
  299. 'star-intensity': 0.0
  300. })
  301. } else if (timeInDay.value === 'dusk' || timeInDay.value === 'dawn') {
  302. map.setFog({
  303. 'range': [-1, 2],
  304. 'horizon-blend': 0.3,
  305. 'color': '#999',
  306. 'high-color': '#add8e6',
  307. 'space-color': '#d8f2ff',
  308. 'star-intensity': 0.4
  309. })
  310. } else {
  311. map.setFog({
  312. 'range': [-1, 2],
  313. 'horizon-blend': 0.3,
  314. 'color': '#242B4B',
  315. 'high-color': '#161B36',
  316. 'space-color': '#0B1026',
  317. 'star-intensity': 0.8
  318. })
  319. }
  320. } else {
  321. map.setFog(null)
  322. }
  323. })
  324. // 自动旋转开关
  325. const isAutoRotate = ref(false)
  326. let lastFrameTime = 0.0
  327. let animationTotalTime = 0.0
  328. let initialBearing = null
  329. function frameTask(timeStamp) {
  330. if (isAutoRotate.value) {
  331. animationTotalTime += ((timeStamp - lastFrameTime) / 1000.0)
  332. const rotation = initialBearing + animationTotalTime * 2.0
  333. map.setBearing(rotation % 360)
  334. lastFrameTime = timeStamp
  335. window.requestAnimationFrame(frameTask)
  336. }
  337. }
  338. watch(isAutoRotate, (v) => {
  339. if (v) {
  340. lastFrameTime = 0.0
  341. animationTotalTime = 0.0
  342. initialBearing = map.getBearing()
  343. window.requestAnimationFrame(frameTask)
  344. }
  345. })
  346. // 一种style可有多个layer,每个layer有对应的source。
  347. // todo: 日照控件
  348. // todo: 道路显隐控件
  349. // map.setPaintProperty('route', 'line-opacity', 0.9);
  350. // 添加自定义图层。同一个slot内的各个图层用beforeId参数指定顺序。
  351. // map.addLayer({ type: 'line', slot: 'middle' /* ... */ });
  352. // 移除图层
  353. //
  354. </script>
  355. <style lang="less" scoped>
  356. .home {
  357. position: absolute;
  358. left: 0;
  359. top: 0;
  360. width: 100%;
  361. height: 100%;
  362. #map{
  363. position: absolute;
  364. left: 0;
  365. top: 0;
  366. width: 100%;
  367. height: 100%;
  368. }
  369. .panel{
  370. position: absolute;
  371. top: 0;
  372. left: 0;
  373. padding: 10px;
  374. display: flex;
  375. flex-direction: column;
  376. background-color: rgba(255, 255, 255, 0.5);
  377. }
  378. }
  379. </style>