leafletHelper.js 16 KB

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