leafletHelper.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. // leaflet 地图组件
  2. // author: wzx
  3. // version: 1.0
  4. import L from 'leaflet'
  5. // import 'leaflet.motion/dist/leaflet.motion.min.js'
  6. import patch from '@/utils/map/sub/patch'
  7. import config from '@/utils/map/sub/config'
  8. import global from '@/utils/map/sub/global'
  9. import player from '@/utils/map/sub/player'
  10. import checkPoint from '@/utils/map/sub/checkPoint'
  11. import route from '@/utils/map/sub/route'
  12. import store from '@/store/index'
  13. export default {
  14. global: global,
  15. config: config,
  16. player: player,
  17. checkPoint: checkPoint,
  18. route: route,
  19. exControlContainer: null,
  20. elRoute: null,
  21. elPlayer: null,
  22. elPlayerTooltip: null,
  23. elPlayerTrail: null,
  24. elList: null,
  25. elFullScreen: null,
  26. // 地图初始化
  27. // mapid <string> 地图容器的id
  28. // mapUrl <string> 地图瓦片的地址
  29. // options <object> 地图选项
  30. init(caller, mapid, mapUrl, options) {
  31. patch.run()
  32. global.init()
  33. global.setCaller(caller)
  34. global.setMapUrl(mapUrl)
  35. global.mapOptions = Object.assign(config.mapDefaultOptions, options)
  36. if (global.mapOptions.maxBounds != null) {
  37. global.setPreloadBounds(global.mapOptions.maxBounds)
  38. // console.log("preloadBounds: ", global.mapOptions.maxBounds)
  39. // global.mapOptions.maxBounds = L.latLngBounds(global.mapOptions.maxBounds).pad(0.3)
  40. global.mapOptions.maxBounds = null
  41. // console.log("maxBounds: ", global.mapOptions.maxBounds)
  42. }
  43. global.map = L.map(mapid, global.mapOptions)
  44. // global.map = L.map(mapid, {
  45. // attributionControl: false
  46. // }).setView(centPoint, zoomNum)
  47. L.control.attribution({
  48. // prefix: '&copy; 小飞龙定向', // 地图右下角属性文本的前缀内容
  49. prefix: false // 地图右下角属性文本的前缀内容
  50. }).addTo(global.map);
  51. L.control.scale({
  52. maxWidth: 120, // 控件的最大宽度,单位是像素
  53. metric: true, // 是否显示公制比例线(米/公里)
  54. imperial: false, // 是否显示英制比例线(英里/英尺)
  55. position: 'topright' // 控件的位置(地图的一个角)。可能的值是 ‘topleft’、 ‘topright’、 ‘bottomleft’ 或 ‘bottomright’
  56. }).addTo(global.map)
  57. this.extendControl()
  58. // 添加地图点击弹窗
  59. global.map.on('click', (e) => {
  60. console.log("坐标", e.latlng.toString())
  61. // L.popup().setLatLng(e.latlng)
  62. // .setContent("坐标:" + e.latlng.toString())
  63. // .openOn(global.map);
  64. });
  65. // global.map.on('resize', (e) => {
  66. // console.log("[resize] 地图大小调整", e)
  67. // this.onWindowResize()
  68. // });
  69. // global.map.on('invalidateSize', (e) => {
  70. // console.log("[invalidateSize] 地图容器大小调整", e)
  71. // // this.onWindowResize()
  72. // });
  73. global.map.on('zoomstart', (e) => {
  74. // console.log("[zoomstart] 即将开始地图缩放", e)
  75. player.onZoomStart(e)
  76. checkPoint.onZoomStart(e)
  77. route.onZoomStart(e)
  78. });
  79. global.map.on('zoom', (e) => {
  80. // console.log("[zoom] 地图缩放", e)
  81. player.onZoom(e)
  82. checkPoint.onZoom(e)
  83. route.onZoom(e)
  84. });
  85. global.map.on('zoomend', (e) => {
  86. // console.log("[zoomend] 地图缩放结束", e)
  87. this.preLoadTile()
  88. player.onZoomEnd(e)
  89. checkPoint.onZoomEnd(e)
  90. route.onZoomEnd(e)
  91. });
  92. // global.map.on('zoomanim', (e) => {
  93. // console.log("地图缩放中...", e)
  94. // // player.onZoomEnd(e)
  95. // // checkPoint.onZoomEnd(e)
  96. // // route.onZoomEnd(e)
  97. // });
  98. // global.map.on('moveend', (e) => {
  99. // // console.log("[zoomend] 地图移动结束", e)
  100. // this.preLoadTile()
  101. // });
  102. this.addMapLayer(mapUrl)
  103. player.init()
  104. checkPoint.init()
  105. route.init()
  106. this.preLoadTile()
  107. },
  108. addMapLayer(mapUrl) {
  109. // console.log('[addMapLayer] mapurl', mapurl)
  110. // var mapurl = 'static/map/' + mapid + '/{z}/{x}/{y}.png'
  111. var tileUrl = mapUrl + '/{z}/{x}/{y}.png'
  112. global.map_layer = L.tileLayer(tileUrl, {
  113. // minZoom: minZoom,
  114. // maxZoom: maxZoom,
  115. errorTileUrl: '/static/image/tileMiss.png',
  116. tms: false, // 是否反转Y轴坐标,
  117. // keepBuffer: 20, // 平移地图时,在卸载切片之前,请保留这么多行和几列的切片
  118. // attribution: '版权所有'
  119. }).addTo(global.map);
  120. },
  121. // onWindowResize() {
  122. // var centPoint = global.mapOptions.center
  123. // if (centPoint != null) {
  124. // console.log("[Leaflet] onWindowResize centPoint:", centPoint)
  125. // global.map.setView(centPoint)
  126. // // global.map.panTo(centPoint)
  127. // }
  128. // },
  129. extendControl() {
  130. var that = this
  131. L.Control.Search = L.Control.extend({
  132. options: {
  133. position: 'topleft' //初始位置
  134. },
  135. initialize: function(options) {
  136. L.Util.extend(this.options, options)
  137. },
  138. onAdd: function(map) {
  139. // var caller = global.getCaller()
  140. //创建Dom元素 L.DomUtil.create('元素类型', 'class类名')
  141. that.exControlContainer = L.DomUtil.create('div', 'leaflet-bar')
  142. that.elRoute = L.DomUtil.create('a', 'leaflet-control-common')
  143. that.elRoute.innerHTML = '<span>路线</span>'
  144. if (store.state.mapControlRoute) {
  145. L.DomUtil.setClass(that.elRoute, 'leaflet-control-selected')
  146. }
  147. that.elPlayer = L.DomUtil.create('a', 'leaflet-control-common')
  148. that.elPlayer.innerHTML = '<span>玩家</span>'
  149. if (store.state.mapControlPlayer) {
  150. L.DomUtil.setClass(that.elPlayer, 'leaflet-control-selected')
  151. }
  152. that.elPlayerTooltip = L.DomUtil.create('a', 'leaflet-control-common')
  153. that.elPlayerTooltip.innerHTML = '<span>提示</span>'
  154. if (store.state.mapControlTooltip) {
  155. L.DomUtil.setClass(that.elPlayerTooltip, 'leaflet-control-selected')
  156. }
  157. that.elPlayerTrail = L.DomUtil.create('a', 'leaflet-control-common')
  158. that.elPlayerTrail.innerHTML = '<span>轨迹</span>'
  159. if (store.state.mapControlTrail) {
  160. L.DomUtil.setClass(that.elPlayerTrail, 'leaflet-control-selected')
  161. }
  162. that.elList = L.DomUtil.create('a', 'leaflet-control-common')
  163. that.elList.innerHTML = '<span>列表</span>'
  164. if (store.state.mapPopupShow) {
  165. L.DomUtil.setClass(that.elList, 'leaflet-control-selected')
  166. }
  167. that.elFullScreen = L.DomUtil.create('a', 'leaflet-control-common')
  168. that.elFullScreen.innerHTML = '<span>全屏</span>'
  169. if (store.state.fullScreen) {
  170. L.DomUtil.setClass(that.elFullScreen, 'leaflet-control-selected')
  171. }
  172. // that.exControlContainer.style.display = 'flex'
  173. that.exControlContainer.appendChild(that.elRoute)
  174. that.exControlContainer.appendChild(that.elPlayer)
  175. that.exControlContainer.appendChild(that.elPlayerTooltip)
  176. that.exControlContainer.appendChild(that.elPlayerTrail)
  177. that.exControlContainer.appendChild(that.elList)
  178. that.exControlContainer.appendChild(that.elFullScreen)
  179. //注册事件
  180. L.DomEvent.addListener(that.elRoute, 'click dblclick', function(ev) {
  181. that.elRouteClick(ev)
  182. }, that.route)
  183. L.DomEvent.addListener(that.elPlayer, 'click dblclick', function(ev) {
  184. that.elPlayerClick(ev)
  185. }, that.player)
  186. L.DomEvent.addListener(that.elPlayerTooltip, 'click dblclick', function(ev) {
  187. that.elPlayerTooltipClick(ev)
  188. }, that.player)
  189. L.DomEvent.addListener(that.elPlayerTrail, 'click dblclick', function(ev) {
  190. that.elPlayerTrailClick(ev)
  191. }, that.player)
  192. L.DomEvent.addListener(that.elList, 'click dblclick', function(ev) {
  193. that.elListClick(ev)
  194. }, this)
  195. L.DomEvent.addListener(that.elFullScreen, 'click dblclick', function(ev) {
  196. that.elFullScreenClick(ev)
  197. }, this)
  198. //返回这个主元素
  199. return that.exControlContainer
  200. },
  201. onRemove: function(map) {
  202. // Clean up the DOM
  203. },
  204. })
  205. //在L.control上添加一个search(封装好的函数)
  206. L.control.Search = function(options) {
  207. return new L.Control.Search(options)
  208. }
  209. //将自定义控件添加到地图上
  210. L.control.Search().addTo(global.map)
  211. },
  212. preventEvent(ev) {
  213. L.DomEvent.stopPropagation(ev)
  214. L.DomEvent.preventDefault(ev)
  215. },
  216. elRouteClick(ev) {
  217. this.preventEvent(ev)
  218. this.route.toggle()
  219. this.toggleControl(this.elRoute)
  220. },
  221. elPlayerClick(ev) {
  222. this.preventEvent(ev)
  223. this.toggleControl(this.elPlayer)
  224. this.player.togglePlayer()
  225. },
  226. elPlayerTooltipClick(ev) {
  227. this.preventEvent(ev)
  228. this.toggleControl(this.elPlayerTooltip)
  229. this.player.toggleTooltip()
  230. console.log("store.state.mapControlPlayer: " + store.state.mapControlPlayer)
  231. if (!store.state.mapControlPlayer) {
  232. this.player.togglePlayer(true)
  233. // this.player.toggleTooltipFlag = false
  234. this.toggleControl(this.elPlayer)
  235. }
  236. },
  237. elPlayerTrailClick(ev) {
  238. this.preventEvent(ev)
  239. this.toggleControl(this.elPlayerTrail)
  240. this.player.toggleTrail()
  241. },
  242. elListClick(ev) {
  243. this.preventEvent(ev)
  244. this.toggleControl(this.elList)
  245. global.getCaller().popupToggle()
  246. },
  247. elFullScreenClick(ev) {
  248. this.preventEvent(ev)
  249. this.toggleControl(this.elFullScreen)
  250. global.getCaller().fullScreenToggle()
  251. },
  252. toggleControl(el) {
  253. if (L.DomUtil.hasClass(el, 'leaflet-control-selected')) {
  254. L.DomUtil.removeClass(el, 'leaflet-control-selected')
  255. } else {
  256. L.DomUtil.setClass(el, 'leaflet-control-selected')
  257. }
  258. },
  259. long2tile(lon, zoom) {
  260. return (Math.floor((lon + 180) / 360 * Math.pow(2, zoom)));
  261. },
  262. lat2tile(lat, zoom) {
  263. return (Math.floor((1 - Math.log(Math.tan(lat * Math.PI / 180) + 1 / Math.cos(lat * Math.PI / 180)) / Math.PI) /
  264. 2 * Math.pow(2, zoom)));
  265. },
  266. // 预加载地图瓦片
  267. preLoadTile() {
  268. var zoom = global.map.getZoom()
  269. if (zoom >= global.mapOptions.maxZoom) {
  270. console.log("[preLoadTile] 已达到最大缩放级别,无需预加载地图瓦片")
  271. return
  272. }
  273. var preloadZoom = zoom + 1;
  274. if (global.getPreloadTileMapByZoom(preloadZoom) != null) {
  275. console.log("[preLoadTile] 跳过,已预加载地图瓦片 Zoom: " + preloadZoom)
  276. return
  277. } else {
  278. console.log("[preLoadTile] 预加载地图瓦片 Zoom: " + preloadZoom)
  279. global.setPreloadTileMap({
  280. zoom: preloadZoom
  281. })
  282. }
  283. var bounds = null
  284. var preloadBounds = global.getPreloadBounds()
  285. if (preloadBounds != null) {
  286. // console.log("[preLoadTile] preloadBounds", preloadBounds)
  287. bounds = L.latLngBounds(preloadBounds)
  288. } else {
  289. console.warn("[preLoadTile] preloadBounds == null")
  290. bounds = global.map.getBounds().pad(0.2)
  291. }
  292. var west = bounds.getWest()
  293. var south = bounds.getSouth()
  294. var east = bounds.getEast()
  295. var north = bounds.getNorth()
  296. // console.log("[preLoadTile] 预加载地图瓦片", bounds, west, south, east, north, zoom)
  297. // L.circleMarker([south, west], {
  298. // radius: 18, // 圆的半径 px
  299. // weight: 1.6, // 描边宽度 px
  300. // opacity: 1.0, // 描边不透明度
  301. // fill: true, // 是否填充
  302. // fillColor: 'red', // 填充颜色
  303. // fillOpacity: 1.0, // 填充不透明度
  304. // })
  305. // .addTo(global.map)
  306. // console.log("[preLoadTile] circleMarker", [south, west])
  307. // L.circleMarker([north, east], {
  308. // radius: 18, // 圆的半径 px
  309. // weight: 1.6, // 描边宽度 px
  310. // opacity: 1.0, // 描边不透明度
  311. // fill: true, // 是否填充
  312. // fillColor: 'red', // 填充颜色
  313. // fillOpacity: 1.0, // 填充不透明度
  314. // })
  315. // .addTo(global.map)
  316. // console.log("[preLoadTile] circleMarker", [north, east])
  317. // Determine which tile we need
  318. var dataEast = this.long2tile(east, preloadZoom)
  319. var dataWest = this.long2tile(west, preloadZoom)
  320. var dataNorth = this.lat2tile(north, preloadZoom)
  321. var dataSouth = this.lat2tile(south, preloadZoom)
  322. var mapUrl = global.getMapUrl()
  323. // console.log("[preLoadTile] dataNorth = " + dataNorth + " dataSouth = " + dataSouth)
  324. for (let y = dataNorth; y < dataSouth + 1; y++) {
  325. // console.log("[preLoadTile] y = " + y)
  326. for (let x = dataWest; x < dataEast + 1; x++) {
  327. // console.log("[preLoadTile] x = " + x)
  328. var url = mapUrl + '/' + preloadZoom + '/' + x + '/' + y + '.png'
  329. var img = new Image()
  330. img.src = url
  331. // console.log("[preLoadTile] 预加载地图瓦片", url)
  332. }
  333. }
  334. },
  335. test() {
  336. console.log("test ok")
  337. },
  338. free() {
  339. console.log("[Leaflet] free()")
  340. player.free()
  341. checkPoint.free()
  342. route.free()
  343. }
  344. }