摘要:本次我开发的插件功能很简单,下载地图的快照,即将当前地图显示导出图片。之后通过插件本身拿到的地图上下文,开始下载。不加后两个参数在下会报错事件类型,是否冒泡,是否阻止浏览器的默认行为至此,一个下载地图快照的插件就完成了。
效果预览
mapbox 是一个非常好用的开源地图引擎,他支持得平台有android,ios,js,rn等等,功能多样,但是对于地图插件开发这一块,没找到具体的实施文档。因此本文以js 为例,来把开发mapbox插件这一过程记录下来。
mapboxvar map = new mapboxgl.Map({ container: "map", // container id style: "mapbox://styles/mapbox/streets-v11", // stylesheet location center: [-74.50, 40], // starting position [lng, lat] zoom: 9, // starting zoom preserveDrawingBuffer: true // 这个需要开启,才能获取正确的base64 });
在开发之前先简述下mapbox的地图。在显示一张地图时,有两个属性是必须的,一个就是container ,地图的容器,接受一个dom的id,另一个就是style,地图实际渲染所需的资源配置都在这里,mapbox是支持室内外地图的,也就是在style的source属性中去分别加载indoor,outdoor的资源(可以是瓦片,也可以是geojson),有了这两个属性,就可以将地图显示出来了,其余属性不过多介绍。
mapbox 插件mapbox 官方提供了很多插件,如线面绘制,地图比较等等。本次我开发的插件功能很简单,下载地图的快照,即将当前地图显示导出图片。mapbox 渲染完毕是一个canvas标签,而canvas 可以直接转成图片的base64资源,然后转成文件资源去进行下载。
本文重点放在开发一款mapbox插件,而非下载功能本身,所以具体下载流程在接下来的插件开发中插入。
插件开发流程因为官方没有提供开发插件的文档(没找到),因此从0到1这样一步一步来。
map.addControl(new mapboxgl.NavigationControl()); // 官方代码 // 插件的类 class Map2img { constructor() { } } map.addControl(new Map2img ());
上述代码为mapbox的一个示例,mapbox 提供了addControl这个api 用于将插件引入地图,在初始化好一个类(Map2img ),以同样的方式引入map,此时出现报错:
由此可知,一个可供map使用的插件类至少需要2个方法,onAdd ,onRemove。为了更详细的了解这两个方法的作用,直接去mapbox-gl-js里面搜索addControl。
mapbox-gl部分代码:
addControl(control: IControl, position?: ControlPosition) { if (position === undefined && control.getDefaultPosition) { position = control.getDefaultPosition(); } if (position === undefined) { position = "top-right"; } if (!control || !control.onAdd) { return this.fire(new ErrorEvent(new Error( "Invalid argument to map.addControl(). Argument must be a control with onAdd and onRemove methods."))); } const controlElement = control.onAdd(this); this._controls.push(control); const positionContainer = this._controlPositions[position]; if (position.indexOf("bottom") !== -1) { positionContainer.insertBefore(controlElement, positionContainer.firstChild); } else { positionContainer.appendChild(controlElement); } return this; }
从源码中不难看出,在把插件加入map之后,会触发插件(control)上的onAdd方法,这个方法返回一个dom元素,元素被插入到mapbox 的控制器(插件中),相当于把插件放入一个插槽。
因此,首先将类增加一个onAdd 方法,并返回一个dom元素,然后让他显示至右上角(top-right)。
Map2img.js
class Map2img { constructor(html) { this._html = html; // 初始化接收要显示的html } onAdd(map) { this._map = map; const el = document.createElement("div"); el.innerHTML = this._html this.bindEvent(el) // 添加点击事件 return el // 返回这个传入的html } onRemove(map) { this.container.parentNode.removeChild(this.container); this._map = null; return this } }
index.html
let eltemp = `插件` var downloadCtrl=new Map2img(eltemp) map.addControl(downloadCtrl, "top-right");
这样,就完成了插件的第一步,显示嵌入地图的问题。继续在加入的dom节点上增加点击监听事件,再点击之后通过在onAdd 方法中获取的地图上下文,进而获取到地图的canvas
bindEvent(el) { el.addEventListener("click", () => { const base64 = this._map.getCanvas().toDataURL() this.downloadFile(this._map.getStyle().name, base64) }) }
做到这一步后,发现并没有按照预想的结果,点击后触发相关事件。在这里卡了很久,没有找到原因,尝试过更改dom的z-index等等,更改事件监听方式等等,均没有触发点击效果。最终去翻阅官方的插件代码,发现官方的插件中,对于引入的dom,添加了一个mapboxgl-ctrl的样式,去mapbox-gl中搜索这个样式后,发现一个关键属性。
在这个css 中,有一个控制很关键。pointer-events 当这个属性为none时会阻止点击事件的触发(还有很多其他控制,不展开叙述),由此打开浏览器调试发现,果然插入dom的父级把这个属性置为none。
在加入这个样式之后,引入的插件成功的触发了点击方法。之后通过插件本身拿到的地图上下文,开始下载。
downloadFile(fileName, content) { let aLink = document.createElement("a"); let blob = this.base64ToBlob(content); //new Blob([content]); let evt = document.createEvent("HTMLEvents"); evt.initEvent("click", true, true);//initEvent 不加后两个参数在FF下会报错 事件类型,是否冒泡,是否阻止浏览器的默认行为 aLink.download = fileName; aLink.href = URL.createObjectURL(blob); aLink.dispatchEvent(new MouseEvent("click", {bubbles: true, cancelable: true, view: window})); } base64ToBlob(code) { let parts = code.split(";base64,"); let contentType = parts[0].split(":")[1]; let raw = window.atob(parts[1]); let rawLength = raw.length; let uInt8Array = new Uint8Array(rawLength); for (let i = 0; i < rawLength; ++i) { uInt8Array[i] = raw.charCodeAt(i); } return new Blob([uInt8Array], {type: contentType}); }
至此,一个下载地图快照的插件就完成了。
PS:如果是室内外地图,有分楼层下载地图快照且不希望下载中去切换楼层,影响当前显示的需求的话,我目前的做法是通过绝对定位,初始化一个新的地图在下面,然后用这个新的地图去切换楼层,然后将新地图的快照截取出来,因为地图下载只能是当前camera(视口)的图象。还有一点需要注意的是,如果不是手动触发,而是在地图load时就下载地图的话,需要主动延迟适当的时间,因为地图onload的方法不包含地图字体的显示加载,即区域名称,所以要有必要的延迟,以上是我目前解决问题的思路,有更好的方法欢迎交流分享!
项目地址:https://github.com/jiwenjiang...
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/106648.html
摘要:最近在折腾的端的可视化项目,由于相关业务的需要,用到了这一地图开发的神器。其技术奠基于与。分别指缩放级别地面法线偏移角地轴偏移角等,用以确定当前视窗所显示的地图区域和空间关系。个人认为其最大的亮点在于即使不了解内部实现,也不影响其使用。 最近在折腾的 web 端的可视化项目,由于相关业务的需要,用到了 Mapbox 这一地图开发的神器。在此先奉上一个基于mapbox-gl实现的demo...
入门 Leaflet 之小 Demo 写在前面 ---- WebGIS 开发基础之 Leaflet GIS 基本概念:GIS、Map、Layer、Feature、Geometry、Symbol、Data(Point、Polyline、Polygon)、Renderer、Scale、Project、Coordinates; GIS 开发概述:架构模式、常用平台和 SDK、二维三维 使用 Lea...
MapBox 项目中用到MapBox也是偶然的机会,项目中需要采用3D地图,当现有的工具(百度地图)无法满足我们的需求,我们肯定需要更高级开源的地图,无奈谷歌地图无法在国内使用,已是便找到Leafle,一开始惊艳于leafle的开源程度和其与众不同的地图风格,后来顺藤摸瓜,找到一个商业性地图,它便是我们的主角-MapBoxshowImg(https://segmentfault.com/img/b...
阅读 566·2021-11-18 10:02
阅读 1047·2021-11-02 14:41
阅读 673·2021-09-03 10:29
阅读 1892·2021-08-23 09:42
阅读 2727·2021-08-12 13:31
阅读 1198·2019-08-30 15:54
阅读 1951·2019-08-30 13:09
阅读 1427·2019-08-30 10:55