leafletHelper.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. // leaflet 地图组件
  2. // author: wzx
  3. // version: 2.0
  4. import L from 'leaflet'
  5. // import patch from '@/utils/map/sub/patch'
  6. import config from '@/utils/map/sub/config'
  7. import global from '@/utils/map/sub/global'
  8. import player from '@/utils/map/sub/player'
  9. import playerRoute from '@/utils/map/sub/playerRoute'
  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. playerRoute: playerRoute,
  18. // checkPoint: checkPoint,
  19. route: route,
  20. exControlContainer: null,
  21. elRoute: null,
  22. elPlayer: null,
  23. elPlayerTooltip: null,
  24. elPlayerTrail: null,
  25. elList: null,
  26. elFullScreen: null,
  27. // 地图初始化
  28. // mapid <string> 地图容器的id
  29. // mapUrl <string> 地图图片的地址
  30. // mapConfig <string> 地图图片的地理参考信息
  31. // options <object> 地图选项
  32. async init(caller, mapid, mapUrl, mapConfig, options) {
  33. // console.log("mapUrl", mapUrl);
  34. // patch.run()
  35. global.init()
  36. global.setCaller(caller)
  37. global.setMapUrl(mapUrl)
  38. global.mapOptions = Object.assign(config.mapDefaultOptions, options)
  39. const mapImageInfo = await uni.getImageInfo({src: mapUrl});
  40. // console.log(mapImageInfo);
  41. const jgwData = await this.parseJgwContent(mapConfig);
  42. const bounds = await this.getImageBoundsByJgw(jgwData, mapImageInfo.width, mapImageInfo.height);
  43. const center = this.getImageCenterByBounds(bounds);
  44. // global.mapOptions.maxBounds = bounds; // 地图最大范围,超出范围则地图无法拖动(可为空)
  45. global.mapOptions.center = center; // 地图中心点
  46. // console.log("global.mapOptions", global.mapOptions);
  47. global.map = L.map(mapid, global.mapOptions);
  48. // global.map = L.map(mapid, {
  49. // attributionControl: false
  50. // }).setView(centPoint, zoomNum)
  51. L.control.attribution({
  52. prefix: '&copy; 彩图奔跑 All Rights Reserved.', // 地图右下角属性文本的前缀内容
  53. // prefix: false // 地图右下角属性文本的前缀内容
  54. }).addTo(global.map);
  55. L.control.scale({
  56. maxWidth: 120, // 控件的最大宽度,单位是像素
  57. metric: true, // 是否显示公制比例线(米/公里)
  58. imperial: false, // 是否显示英制比例线(英里/英尺)
  59. position: 'topright' // 控件的位置(地图的一个角)。可能的值是 ‘topleft’、 ‘topright’、 ‘bottomleft’ 或 ‘bottomright’
  60. }).addTo(global.map)
  61. this.extendControl()
  62. // 添加地图点击弹窗
  63. global.map.on('click', (e) => {
  64. console.log("坐标", e.latlng.toString())
  65. // L.popup().setLatLng(e.latlng)
  66. // .setContent("坐标:" + e.latlng.toString())
  67. // .openOn(global.map);
  68. });
  69. // global.map.on('resize', (e) => {
  70. // console.log("[resize] 地图大小调整", e)
  71. // this.onWindowResize()
  72. // });
  73. // global.map.on('invalidateSize', (e) => {
  74. // console.log("[invalidateSize] 地图容器大小调整", e)
  75. // // this.onWindowResize()
  76. // });
  77. global.map.on('zoomstart', (e) => {
  78. // console.log("[zoomstart] 即将开始地图缩放", e)
  79. player.onZoomStart(e)
  80. // checkPoint.onZoomStart(e)
  81. route.onZoomStart(e)
  82. });
  83. global.map.on('zoom', (e) => {
  84. // console.log("[zoom] 地图缩放", e)
  85. player.onZoom(e)
  86. // checkPoint.onZoom(e)
  87. route.onZoom(e)
  88. });
  89. global.map.on('zoomend', (e) => {
  90. // console.log("[zoomend] 地图缩放结束", e)
  91. // this.preLoadTile()
  92. player.onZoomEnd(e)
  93. // checkPoint.onZoomEnd(e)
  94. route.onZoomEnd(e)
  95. });
  96. this.addMapLayer(mapUrl, bounds);
  97. player.init()
  98. // checkPoint.init()
  99. route.init()
  100. console.log("[init] 地图初始化成功")
  101. },
  102. async readJgwFile(jgwUrl) {
  103. // console.log(jgwUrl);
  104. const response = await fetch(jgwUrl);
  105. // const lines = await response.text().split('\n');
  106. return this.parseJgwContent(response)
  107. },
  108. parseJgwContent(jgwContent) {
  109. const lines = jgwContent.split('\r\n');
  110. // console.log(lines);
  111. const a = parseFloat(lines[0]); // x pixel size
  112. const b = parseFloat(lines[1]); // x rotation
  113. const c = parseFloat(lines[2]); // y rotation
  114. const d = parseFloat(lines[3]); // y pixel size
  115. const x_origin = parseFloat(lines[4]); // x coordinate of upper left corner
  116. const y_origin = parseFloat(lines[5]); // y coordinate of upper left corner
  117. return {
  118. a,
  119. b,
  120. c,
  121. d,
  122. x_origin,
  123. y_origin
  124. };
  125. },
  126. async getImageBoundsByJgw(jgwData, imageWidth, imageHeight) {
  127. // console.log("[getImageBoundsByJgw] jgwData", jgwData);
  128. const xmin = jgwData.x_origin;
  129. const ymax = jgwData.y_origin;
  130. const xmax = jgwData.x_origin + jgwData.a * imageWidth;
  131. const ymin = jgwData.y_origin + jgwData.d * imageHeight;
  132. const wgs84LatLng1 = this.Convert_EPSG3857_To_WGS84(ymax, xmin);
  133. const wgs84LatLng2 = this.Convert_EPSG3857_To_WGS84(ymin, xmax);
  134. const bounds = [
  135. [wgs84LatLng1.lat, wgs84LatLng1.lng],
  136. [wgs84LatLng2.lat, wgs84LatLng2.lng]
  137. ];
  138. // console.log("[getImageBoundsByJgw] bounds", bounds);
  139. return bounds;
  140. },
  141. getImageCenterByBounds(bounds) {
  142. const wgs84Center = {
  143. lat: bounds[0][0] + (bounds[1][0] -bounds[0][0]) / 2,
  144. lng: bounds[0][1] + (bounds[1][1] -bounds[0][1]) / 2,
  145. }
  146. return wgs84Center;
  147. },
  148. // EPSG3857坐标(平面坐标) 转 WGS84经纬度坐标(球状坐标)
  149. Convert_EPSG3857_To_WGS84(lat, lng) {
  150. let tempLng = lng / 20037508.34 * 180;
  151. let tempLat = lat / 20037508.34 * 180;
  152. tempLat = 180 / Math.PI * (2 * Math.atan(Math.exp(tempLat * Math.PI / 180)) - Math.PI / 2);
  153. return {
  154. lng: lng == 0 ? 0 : tempLng,
  155. lat: lat == 0 ? 0 : tempLat
  156. };
  157. },
  158. // WGS84经纬度坐标(球状坐标) 转 EPSG3857坐标(平面坐标)
  159. Convert_WGS84_To_EPSG3857(lat, lng) {
  160. let mercator = {};
  161. let x = lng * 20037508.34 / 180;
  162. let y = Math.log(Math.tan((90 + lat) * Math.PI / 360)) / (Math.PI / 180);
  163. y = y * 20037508.34 / 180;
  164. mercator.x = x;
  165. mercator.y = y;
  166. return {
  167. lng: lng == 0 ? 0 : mercator.x,
  168. lat: lat == 0 ? 0 : mercator.y
  169. };
  170. },
  171. addMapLayer(mapUrl, bounds) {
  172. // console.log('[addMapLayer] mapurl', mapurl)
  173. global.map_layer = L.imageOverlay(mapUrl, bounds).addTo(global.map);
  174. },
  175. addMapLayer2(mapUrl) {
  176. // console.log('[addMapLayer] mapurl', mapurl)
  177. // var mapurl = 'static/map/' + mapid + '/{z}/{x}/{y}.png'
  178. // this.setMapServerUrl(mapUrl)
  179. // var tileUrl = mapUrl + '/{z}/{x}/{y}.png'
  180. var tileUrl = '{z}/{x}/{y}.png'
  181. global.map_layer = L.tileLayer(tileUrl, {
  182. // minZoom: minZoom,
  183. // maxZoom: maxZoom,
  184. errorTileUrl: '/static/image/tileMiss.png',
  185. tms: false, // 是否反转Y轴坐标,
  186. // keepBuffer: 20, // 平移地图时,在卸载切片之前,请保留这么多行和几列的切片
  187. // attribution: '版权所有'
  188. }).addTo(global.map);
  189. },
  190. // onWindowResize() {
  191. // var centPoint = global.mapOptions.center
  192. // if (centPoint != null) {
  193. // console.log("[Leaflet] onWindowResize centPoint:", centPoint)
  194. // global.map.setView(centPoint)
  195. // // global.map.panTo(centPoint)
  196. // }
  197. // },
  198. extendControl() {
  199. var that = this
  200. L.Control.Search = L.Control.extend({
  201. options: {
  202. position: 'topleft' //初始位置
  203. },
  204. initialize: function(options) {
  205. L.Util.extend(this.options, options)
  206. },
  207. onAdd: function(map) {
  208. // var caller = global.getCaller()
  209. //创建Dom元素 L.DomUtil.create('元素类型', 'class类名')
  210. that.exControlContainer = L.DomUtil.create('div', 'leaflet-bar')
  211. that.elRoute = L.DomUtil.create('div', 'leaflet-control-common')
  212. that.elRoute.innerHTML = '<img class="leaflet-bar-ico" src="static/map/route.png"></img><span>路线</span>'
  213. if (store.state.mapControlRoute) {
  214. L.DomUtil.setClass(that.elRoute, 'leaflet-control-selected')
  215. }
  216. that.elPlayer = L.DomUtil.create('div', 'leaflet-control-common')
  217. that.elPlayer.innerHTML = '<img class="leaflet-bar-ico" src="static/map/player.png"></img><span>玩家</span>'
  218. if (store.state.mapControlPlayer) {
  219. L.DomUtil.setClass(that.elPlayer, 'leaflet-control-selected')
  220. }
  221. that.elPlayerTooltip = L.DomUtil.create('div', 'leaflet-control-common')
  222. that.elPlayerTooltip.innerHTML = '<img class="leaflet-bar-ico" src="static/map/tooltip.png"></img><span>提示</span>'
  223. if (store.state.mapControlTooltip) {
  224. L.DomUtil.setClass(that.elPlayerTooltip, 'leaflet-control-selected')
  225. }
  226. that.elPlayerTrail = L.DomUtil.create('div', 'leaflet-control-common')
  227. that.elPlayerTrail.innerHTML = '<img class="leaflet-bar-ico" src="static/map/trail.png"></img><span>轨迹</span>'
  228. if (store.state.mapControlTrail) {
  229. L.DomUtil.setClass(that.elPlayerTrail, 'leaflet-control-selected')
  230. }
  231. that.elList = L.DomUtil.create('div', 'leaflet-control-common')
  232. that.elList.innerHTML = '<img class="leaflet-bar-ico" src="static/map/list.png"></img><span>列表</span>'
  233. if (store.state.mapPopupShow) {
  234. L.DomUtil.setClass(that.elList, 'leaflet-control-selected')
  235. }
  236. that.elFullScreen = L.DomUtil.create('div', 'leaflet-control-common')
  237. that.elFullScreen.innerHTML = '<img class="leaflet-bar-ico" src="static/map/fullscreen.png"></img><span>全屏</span>'
  238. if (store.state.fullScreen) {
  239. L.DomUtil.setClass(that.elFullScreen, 'leaflet-control-selected')
  240. }
  241. // that.exControlContainer.style.display = 'flex'
  242. that.exControlContainer.appendChild(that.elRoute)
  243. that.exControlContainer.appendChild(that.elPlayer)
  244. that.exControlContainer.appendChild(that.elPlayerTooltip)
  245. that.exControlContainer.appendChild(that.elPlayerTrail)
  246. that.exControlContainer.appendChild(that.elList)
  247. that.exControlContainer.appendChild(that.elFullScreen)
  248. //注册事件
  249. L.DomEvent.addListener(that.elRoute, 'click dblclick', function(ev) {
  250. that.elRouteClick(ev)
  251. }, that.route)
  252. L.DomEvent.addListener(that.elPlayer, 'click dblclick', function(ev) {
  253. that.elPlayerClick(ev)
  254. }, that.player)
  255. L.DomEvent.addListener(that.elPlayerTooltip, 'click dblclick', function(ev) {
  256. that.elPlayerTooltipClick(ev)
  257. }, that.player)
  258. L.DomEvent.addListener(that.elPlayerTrail, 'click dblclick', function(ev) {
  259. that.elPlayerTrailClick(ev)
  260. }, that.player)
  261. L.DomEvent.addListener(that.elList, 'click dblclick', function(ev) {
  262. that.elListClick(ev)
  263. }, this)
  264. L.DomEvent.addListener(that.elFullScreen, 'click dblclick', function(ev) {
  265. that.elFullScreenClick(ev)
  266. }, this)
  267. //返回这个主元素
  268. return that.exControlContainer
  269. },
  270. onRemove: function(map) {
  271. // Clean up the DOM
  272. },
  273. })
  274. //在L.control上添加一个search(封装好的函数)
  275. L.control.Search = function(options) {
  276. return new L.Control.Search(options)
  277. }
  278. //将自定义控件添加到地图上
  279. L.control.Search().addTo(global.map)
  280. },
  281. preventEvent(ev) {
  282. L.DomEvent.stopPropagation(ev)
  283. L.DomEvent.preventDefault(ev)
  284. },
  285. elRouteClick(ev) {
  286. this.preventEvent(ev)
  287. this.route.toggle()
  288. this.toggleControl(this.elRoute)
  289. },
  290. elPlayerClick(ev) {
  291. this.preventEvent(ev)
  292. this.toggleControl(this.elPlayer)
  293. this.player.togglePlayer()
  294. },
  295. elPlayerTooltipClick(ev) {
  296. this.preventEvent(ev)
  297. this.toggleControl(this.elPlayerTooltip)
  298. this.player.toggleTooltip()
  299. console.log("store.state.mapControlPlayer: " + store.state.mapControlPlayer)
  300. if (!store.state.mapControlPlayer) {
  301. this.player.togglePlayer(true)
  302. // this.player.toggleTooltipFlag = false
  303. this.toggleControl(this.elPlayer)
  304. }
  305. },
  306. elPlayerTrailClick(ev) {
  307. this.preventEvent(ev)
  308. this.toggleControl(this.elPlayerTrail)
  309. this.player.toggleTrail()
  310. },
  311. elListClick(ev) {
  312. this.preventEvent(ev)
  313. this.toggleControl(this.elList)
  314. global.getCaller().popupToggle()
  315. },
  316. elFullScreenClick(ev) {
  317. this.preventEvent(ev)
  318. this.toggleControl(this.elFullScreen)
  319. global.getCaller().fullScreenToggle()
  320. },
  321. toggleControl(el) {
  322. if (L.DomUtil.hasClass(el, 'leaflet-control-selected')) {
  323. // L.DomUtil.removeClass(el, 'leaflet-control-selected')
  324. L.DomUtil.setClass(el, 'leaflet-control-common')
  325. } else {
  326. L.DomUtil.setClass(el, 'leaflet-control-selected')
  327. }
  328. },
  329. long2tile(lon, zoom) {
  330. return (Math.floor((lon + 180) / 360 * Math.pow(2, zoom)));
  331. },
  332. lat2tile(lat, zoom) {
  333. return (Math.floor((1 - Math.log(Math.tan(lat * Math.PI / 180) + 1 / Math.cos(lat * Math.PI / 180)) / Math.PI) /
  334. 2 * Math.pow(2, zoom)));
  335. },
  336. // 获取图片的Blob值
  337. getImageBlob(url, cb) {
  338. var xhr = new XMLHttpRequest();
  339. xhr.open("get", url, true);
  340. xhr.responseType = "blob";
  341. xhr.onload = function() {
  342. if (this.status == 200) {
  343. if(cb) cb(this.response);
  344. }
  345. };
  346. xhr.send();
  347. },
  348. // 预加载地图瓦片
  349. /* preLoadTile() {
  350. return
  351. var zoom = global.map.getZoom()
  352. if (zoom >= global.mapOptions.maxZoom) {
  353. console.log("[preLoadTile] 已达到最大缩放级别,无需预加载地图瓦片")
  354. return
  355. }
  356. var preloadZoom = zoom + 1;
  357. if (global.getPreloadTileMapByZoom(preloadZoom) != null) {
  358. console.log("[preLoadTile] 跳过,已预加载地图瓦片 Zoom: " + preloadZoom)
  359. return
  360. } else {
  361. console.log("[preLoadTile] 预加载地图瓦片 Zoom: " + preloadZoom)
  362. global.setPreloadTileMap({
  363. zoom: preloadZoom
  364. })
  365. }
  366. var bounds = null
  367. var preloadBounds = global.getPreloadBounds()
  368. if (preloadBounds != null) {
  369. // console.log("[preLoadTile] preloadBounds", preloadBounds)
  370. bounds = L.latLngBounds(preloadBounds)
  371. } else {
  372. console.warn("[preLoadTile] preloadBounds == null")
  373. bounds = global.map.getBounds().pad(0.2)
  374. }
  375. var west = bounds.getWest()
  376. var south = bounds.getSouth()
  377. var east = bounds.getEast()
  378. var north = bounds.getNorth()
  379. // console.log("[preLoadTile] 预加载地图瓦片", bounds, west, south, east, north, zoom)
  380. // Determine which tile we need
  381. var dataEast = this.long2tile(east, preloadZoom)
  382. var dataWest = this.long2tile(west, preloadZoom)
  383. var dataNorth = this.lat2tile(north, preloadZoom)
  384. var dataSouth = this.lat2tile(south, preloadZoom)
  385. var mapUrl = global.getMapUrl()
  386. // console.log("[preLoadTile] dataNorth = " + dataNorth + " dataSouth = " + dataSouth)
  387. for (let y = dataNorth; y < dataSouth + 1; y++) {
  388. // console.log("[preLoadTile] y = " + y)
  389. for (let x = dataWest; x < dataEast + 1; x++) {
  390. // console.log("[preLoadTile] x = " + x)
  391. var url = mapUrl + '/' + preloadZoom + '/' + x + '/' + y + '.png'
  392. var img = new Image()
  393. this.getImageBlob( url , function(blob){
  394. // console.log(blob)
  395. img.src = URL.createObjectURL(blob);
  396. });
  397. // img.src = url
  398. // console.log("[preLoadTile] 预加载地图瓦片", url)
  399. }
  400. }
  401. }, */
  402. free() {
  403. console.log("[Leaflet] free()")
  404. player.free()
  405. // checkPoint.free()
  406. route.free()
  407. }
  408. }