目录
百度地图 JavaScript API GL 地图聚合点、地图框选功能开发
参考地址: 百度地图的开发者频道
注意:使用前要先登录百度账号申请一个浏览器端的 AK,详见 AK 申请
创建地图以及初始化交通流量图层
引用 GL 版本的 JS API:
<script src="https://api.map.baidu.com/api?v=1.0&type=webgl&ak=你的密钥"></script>
初始化地图的 JavaScript 代码:
const map = new BMapGL.Map('map', { // minZoom: 11, // maxZoom: 20, }); map.centerAndZoom('武汉', 12, { callback: () => { // 一定要在回调中开启交通流量图层 map.setTrafficOn(); }, }); map.enableScrollWheelZoom(true);
BMapGL.Map
BMapGL.Map 类是地图API的核心类,用来实例化一个地图。请注意 WebGL 版本的地图 API 的命名空间是BMapGL。
构造函数 | 描述 |
---|---|
Map(container: String | HTMLElement, opts: MapOptions ) | 在指定的容器内创建地图实例,之后需要调用Map.centerAndZoom()方法对地图进行初始化,未进行初始化的地图将不能进行任何操作。 |
MapOptions
属性 | 类型 | 描述 |
---|---|---|
minZoom | Number | 地图允许展示的最小级别 |
maxZoom | Number | 地图允许展示的最大级别 |
mapType | MapTypeId | 地图类型,默认为BMAP_NORMAL_MAP |
enableAutoResize | Boolean | 开启自动适应地图容器变化,默认启用 |
enableTilt | Boolean | 是否允许地图倾斜 |
enableRotate | Boolean | 是否允许地图旋转 |
enableRotateGestures | Boolean | 是否允许通过手势旋转地图。 |
enableTiltGestures | Boolean | 是否允许通过手势倾斜地图。 |
overlayTop | Boolean | 覆盖物是否显示在文字上面,默认false |
fixCenterWhenPinch | Boolean | 手势缩放是否固定中心点,默认不固定,由手指中心点决定 |
displayOptions | Object | 配置地图显示元素。该参数详细信息请参见 setDisplayOptions方法 |
map.centerAndZoom() 对地图进行初始化
调用 new BMapGL.Map
之后需要使用 map.centerAndZoom()
对地图进行初始化,否则不能对地图进行任何操作。
入参:
centerAndZoom(center: Point , zoom: Number)
描述:
初始化地图,如果 center 的类型为 Point 时,zoom 必须赋值,范围 3-19 级,若调用高清底图(针对移动端开发)时,zoom 可赋值范围为 3-18 级。如果 center 类型为字符串时,比如“北京”,zoom可以忽略,地图将自动根据 center 适配最佳 zoom 级别。
设置地图元素
通过 map.setDisplayOptions({option: displayOptions }) 可以设置地图元素显隐。
displayOptions
属性 | 类型 | 描述 |
---|---|---|
poi | boolean | 是否显示 POI 信息。注意:poi 、poiText 与 poiIcon 均用来配置 POI 显示。当 poi 为 true 时,可配置另外两个选项;如果为 false ,则另外两个选项不再生效。 |
poiText | boolean | 是否显示POI文字信息 |
poiIcon | boolean | 是否显示POI的Icon |
overlay | boolean | 是否显示覆盖物 |
building | boolean | 是否显示3D建筑物(仅支持 WebGL 方式渲染的地图) |
indoor | boolean | 是否显示室内图(仅支持 WebGL 方式渲染的地图) |
street | boolean | 是否显示路网(只对卫星图和地球模式有效) |
skyColor | Array | 配置天空的颜色,数组中首个元素表示地面颜色,第二个元素表示天空颜色。从而形成渐变,支持只传入一个元素。 |
点覆盖物 (Marker)
通过 Marker
构造函数可以根据经纬度在地图上生成各类的点覆盖物。
构造函数:Marker(point: Point , opts: MarkerOptions )
使用自定义图片生成指定大小的 Marker,并且设置名称(label)
示例:生成一个 32 * 47 像素的图片 Marker,并且将地图的中心点定位到点位的经纬度上。
map.centerAndZoom('武汉', 12, { callback: () => { // 构造一个包含经纬度和点位名称的数据 const data = { lng: 114.32947483740818, lat: 30.62295757673447, name: '汉口江滩三期', }; // 提前定义 Marker 的宽高 const width = 32; const height = 47; // 构造一个 Point 点位 const point = new BMapGL.Point(data.lng, data.lat); // 构造一个 Marker const marker = new BMapGL.Marker(point, { icon: new BMapGL.Icon( // icon 接收一个 Icon 对象,通过 BMapGL.Icon 构造函数生成 '/marker-red.png', // 图标的绝对路径 new BMapGL.Size(width, height), // 图标的尺寸,通过 BMapGL.Size 构造函数生成 // Marker 的配置项,参考: // https://mapopen-pub-jsapi.bj.bcebos.com/jsapi/reference/jsapi_webgl_1_0.html { // anchor 可以理解为要将图片的哪个坐标点位设置到对应的经纬度上,左上角为 (0,0) ,右下为正 // new BMapGL.Size(width / 2, height) 的意思就是将图片底边的中心点定到对应的经纬度上去 anchor: new BMapGL.Size(width / 2, height), imageOffset: new BMapGL.Size(0, 0), imageSize: new BMapGL.Size(width, height), } ), }); // 设置 marker 的 z-index 层级 marker.setZIndex(2); // 构造 label,参考: // https://mapopen-pub-jsapi.bj.bcebos.com/jsapi/reference/jsapi_webgl_1_0.html#a3b8 const label = new BMapGL.Label( // label 的 html `<span title="${data.name}">${data.name}</span>`, { position: point, // label 的偏移, (0,10) 代表向下偏移 10 像素 offset: new BMapGL.Size(0, 10), } ); // 使用 Object 对象描述 label 的样式 label.setStyle({ border: 0, backgroundColor: 'rgba(0, 14, 17, 0.6)', maxWidth: '120px', // 使用 css 实现横向灵活居中而不是 offset transform: 'translateX(-50%)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', borderRadius: '4px', padding: '2px 6px', color: '#fff', fontSize: '18px', }); // 设置层级 label.setZIndex(2); // 绑定 marker marker.setLabel(label); // 覆盖物上图 map.addOverlay(marker); // 将地图定位到点位,并且设置层级为18 // noAnimation 设置为 true 可以取消 setViewport 的动画过程 map.setViewport( { center: point, zoom: 18, }, { noAnimation: true, } ); }, }); map.enableScrollWheelZoom(true);
信息窗体 (Infowindow)
通过构造函数生成infowindow对象并设置 HTML 内容:
// 构造 infowindow const infoWindowContent = ` <div class="info-content"> <div class="label">名称:${data.name}</div> <div class="label">位置:${data.lng},${data.lat}</div> </div> `; const infoWindow = new BMapGL.InfoWindow(infoWindowContent, { title: '点位信息', width: 360 }); // 点标记添加点击事件 marker.addEventListener('click', () => { map.setViewport( { center: point, zoom: 18, }, { noAnimation: true, callback: () => { map.openInfoWindow(infoWindow, point); infoWindow.disableCloseOnClick(); }, } ); });
关于 infowindow 自带的丑箭头以及立体阴影效果
百度地图渲染出的 infowindow 自带一个白色的箭头以及一个向右上方倾斜的阴影,如下图:
如果你不想要它,可以使用 css 隐藏自带箭头的图片元素以及自带的阴影,并自己用 css 写一个箭头,下面给一个简单的例子:
/* 隐藏箭头 */ img[src="http://webmap0.bdimg.com/image/api/iw_tail.png"] { display: none; } /* 写一个普通箭头 */ .BMap_bubble_pop::after { position: absolute; top: 100%; left: 50%; content: ''; margin-left: -8px; border-left: 8px solid transparent; border-right: 8px solid transparent; border-top: 8px solid #e4e4e4; border-bottom: 8px solid transparent; } /* 隐藏阴影 */ .shadow[type='infowindow_shadow'] { display: none; }
聚合点 (Cluster)
BMapGL 中没有关于聚合点的渲染相关的类,那是因为百度将其放在了 MapVGL 中。
聚合点分为点聚合和图标聚合,如果想自定义不同聚合程度下的图标样式,推荐使用图标聚合 IconClusterLayer,代码参考官方示例即可。
热力图(heatmap)
热力图也是热力图分为 热力点 和 热力图,代码参考官方示例即可。
鼠标绘制工具
基于百度地图 JSAPI GL 版本,百度推出了 bmap-draw ,它提供了一系列 API 用于完成鼠标绘制的诸多功能,比如绘制矩形,圆形,多边形等。
绘制(Draw)
绘制类 用于鼠标绘制点线面,支持吸附绘制,是鼠标绘制的基础能力。开启之后鼠标会变成十字准星样式,按照提示操作鼠标即可完成图形的绘制。
选择工具(Select)
bmap-draw 提供了 Select 类用于地图中的 Marker 的框选,以 矩形框选 为例,配置项 type
设置为 DrawingType.DRAWING_RECTANGLE
,基于绑定监听 OperateEventType.COMPLETE
事件,实现框选功能。
聚合点的框选
在 bmap-draw 的选择工具代码示例中可以看出选择工具默认支持的是 GeoJSONLayer 图层,也即点(marker)、线(polyline)、面(polygon),看起来似乎无法支持聚合图层的框选,但有时我们就是需要框选出地图上的聚合点,这时就需要使用百度推出的另一个工具 BMapGLLib。
BMapGLLib 是基于百度地图 JSAPI GL 版的 JavaScript 开源工具库。拥有一下能力:
鼠标绘制工具条库 示例
提供鼠标绘制点、线、面、多边形(矩形、圆)的编辑工具条的开源代码库。且用户可使用JavaScript API对应覆盖物(点、线、面等)类接口对其进行属性(如颜色、线宽等)设置、编辑(如开启线顶点编辑等)等功能。
测距工具 示例
百度地图的测距工具类,对外开放。 允许用户在地图上点击完成距离的测量。 使用者可以自定义测距线段的相关样式,例如线宽、颜色、测距结果所用的单位制等等。
几何运算 示例
GeoUtils类提供若干几何算法,用来帮助用户判断点与矩形、 圆形、多边形线、多边形面的关系,并提供计算折线长度和多边形的面积的公式。
视角轨迹动画 示例
TrackAnimation类提供视角轨迹动画展示效果。
区域限制 示例
百度地图浏览区域限制类,对外开放。 允许开发者输入限定浏览的地图区域的Bounds值, 则地图浏览者只能在限定区域内浏览地图。
百度地图的infoBox。类似于infoWindow,比infoWindow更有灵活性,比如可以定制border,关闭按钮样式等。
百度地图的富Marker类,对外开放。 允许用户在自定义丰富的Marker展现样式,并添加点击、双击、拖拽等事件。
路书 示例
百度地图路书类,实现Marker沿路线运动,对外开放。 用户可以在地图上自定义轨迹运动,支持暂停/停止功能,并可以自定义路过某个点的图片,文字介绍等。
下面是一个框选聚合点的完整示例:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>百度地图 js api 使用教程</title> <script src="https://api.map.baidu.com/api?v=1.0&type=webgl&ak=J3N7G7JzrLDHYXqIx7JwJyTUwWOdxtTY"></script> <script src="https://unpkg.com/mapvgl/dist/mapvgl.min.js"></script> <script src="/bmap-draw.min.js"></script> <script src="/GeoUtils.js"></script> <style> * { margin: 0; padding: 0; } #map { width: 100vw; height: 100vh; } .btn-con { position: absolute; top: 24px; left: 40px; z-index: 6; } .btn { width: 90px; height: 32px; border-radius: 3px; background-color: #2985f7; color: #fff; border-width: 0; cursor: pointer; } </style> </head> <body> <div class="btn-con"> <button class="btn" id="openCircle">圆形框选</button> <button class="btn" id="openRect">矩形框选</button> <button class="btn" id="openPolygon">多边形框选</button> <button class="btn" id="close">关闭</button> <button class="btn" id="clear">清除</button> </div> <div id="map"></div> <script> const map = new BMapGL.Map('map'); map.centerAndZoom('武汉', 14, { callback: () => { renderClusterLayer(); initDrawUtil(); initEvents(); }, }); map.enableScrollWheelZoom(true); // 鼠标绘制工具 let scene, rectDraw, polygonDraw, circleDraw, activeDraw; // 图标聚合图层 let iconClusterLayer = null; // 绑定事件 function initEvents() { document.querySelector('#openCircle').addEventListener('click', openCircle); document.querySelector('#openRect').addEventListener('click', openRect); document.querySelector('#openPolygon').addEventListener('click', openPolygon); document.querySelector('#close').addEventListener('click', close); document.querySelector('#clear').addEventListener('click', clear); function openCircle() { activeDraw = circleDraw; circleDraw.open(); } function openRect() { activeDraw = rectDraw; rectDraw.open(); } function openPolygon() { activeDraw = polygonDraw; polygonDraw.open(); } function close() { activeDraw.close(); } function clear() { scene && scene.clearData(); } } // 渲染聚合点 function renderClusterLayer() { const data = [ { name: '位置1', lng: 114.32074427093403, lat: 30.614411917020718, }, { name: '位置2', lng: 114.31715104889412, lat: 30.614038958043224, }, { name: '位置3', lng: 114.31513884455177, lat: 30.60950117426803, }, { name: '位置4', lng: 114.31844460882849, lat: 30.611490366197277, }, { name: '位置5', lng: 114.32268461083558, lat: 30.611490366197277, }, ]; const geojsonData = []; data.forEach((item) => { geojsonData.push({ geometry: { type: 'Point', coordinates: [item.lng, item.lat], }, properties: { extraData: item, // 将数据本身保存到这里后面要用 icon: 'https://maponline0.bdimg.com/sty/map_icons2x/MapRes/zhongyangjigou.png', width: 40, height: 40, }, }); }); const view = new mapvgl.View({ map: map, }); iconClusterLayer = new mapvgl.IconClusterLayer({ textOptions: { fontSize: 20, format: function (count) { return count; }, }, iconOptions: { width: 40, height: 40, }, iconExtent: { 0: 'https://a.amap.com/jsapi_demos/static/images/blue.png', 5: 'https://a.amap.com/jsapi_demos/static/images/green.png', 10: 'https://a.amap.com/jsapi_demos/static/images/orange.png', 15: 'https://a.amap.com/jsapi_demos/static/images/red.png', 34: 'https://a.amap.com/jsapi_demos/static/images/darkRed.png', }, // enablePicked: true, onClick: (e) => { if (!e.dataItem) { return; } const { id, children, geometry, properties } = e.dataItem; if (children) { console.log(children); alert('点击了聚合点'); } else { console.log(properties); alert('点击了Marker'); } }, }); view.addLayer(iconClusterLayer); iconClusterLayer.setData(geojsonData); } // 初始化鼠标绘制工具 function initDrawUtil() { scene = new BMapGLDraw.DrawScene(map, { noLimit: true, // 不对框选范围做限制 }); const labelOptions = { borderRadius: '2px', background: '#b5d3fb', border: '1px solid #b5d3fb', color: '#333', fontSize: '12px', letterSpacing: '0', padding: '5px', }; const baseOpts = { fillColor: '#fff', strokeColor: '#00ffee', strokeWeight: 5, strokeOpacity: 1, fillOpacity: 0.2, }; rectDraw = new BMapGLDraw.RectDraw(scene, { isOpen: false, isSeries: false, labelOptions, baseOpts, }); polygonDraw = new BMapGLDraw.PolygonDraw(scene, { isSeries: false, isOpen: false, labelOptions, baseOpts, }); circleDraw = new BMapGLDraw.CircleDraw(scene, { isOpen: false, isSeries: false, labelOptions, baseOpts, }); const rectCallback = (e) => { if (!e.target.overlay.toGeoJSON) { alert('当前jsapi版本不支持圆选'); return; } // 获取当前层级下地图中的聚合数据 const clusterData = iconClusterLayer.getClusterData(map.getBounds(), map.getZoom()); console.log('clusterData', clusterData); const clusterPoints = []; clusterData.forEach((v) => { const pt = new BMapGL.Point(...v.geometry.coordinates); const bds = e.target.overlay.getBounds(); if (BMapGLLib.GeoUtils.isPointInRect(pt, bds)) { clusterPoints.push(v); } }); // clusterPoints 是框选到的聚合点和普通 marker 点 console.log(getClusterInnerMarkers(clusterPoints)); }; const polygonCallback = (e) => { if (!e.target.overlay.toGeoJSON) { alert('当前jsapi版本不支持圆选'); return; } // 获取当前层级下地图中的聚合数据 const clusterData = iconClusterLayer.getClusterData(map.getBounds(), map.getZoom()); const clusterPoints = []; clusterData.forEach((v) => { const pt = new BMapGL.Point(...v.geometry.coordinates); if (BMapGLLib.GeoUtils.isPointInPolygon(pt, e.target.overlay)) { clusterPoints.push(v); } }); console.log(getClusterInnerMarkers(clusterPoints)); }; const circleCallback = (e) => { if (!e.target.overlay.toGeoJSON) { alert('当前jsapi版本不支持圆选'); return; } const center = e.target.overlay.getCenter(); const circle = new BMapGL.Circle(center, e.target.overlay.radius); // 圆 // 获取当前层级下地图中的聚合数据 const clusterData = iconClusterLayer.getClusterData(map.getBounds(), map.getZoom()); const clusterPoints = []; clusterData.forEach((v) => { const pt = new BMapGL.Point(...v.geometry.coordinates); if (BMapGLLib.GeoUtils.isPointInCircle(pt, circle)) { clusterPoints.push(v); } }); console.log(getClusterInnerMarkers(clusterPoints)); }; rectDraw.addEventListener(BMapGLDraw.OperateEventType.COMPLETE, rectCallback); polygonDraw.addEventListener(BMapGLDraw.OperateEventType.COMPLETE, polygonCallback); circleDraw.addEventListener(BMapGLDraw.OperateEventType.COMPLETE, circleCallback); } // 聚合点的数据提取出来 function getClusterInnerMarkers(clusterPoints) { let clusterMarkers = []; clusterPoints.forEach((pt) => { if (pt.type === 'Feature') { // 聚合点 clusterMarkers = clusterMarkers.concat(iconClusterLayer.getClusterPoints(pt.id)); } else { clusterMarkers.push(pt); } }); return clusterMarkers; } </script> </body> </html>
其中有几个 API 比较关键,但是没找到具体的文档,只能从源码中获取相关信息:
iconClusterLayer.getClusterData
该方法的入参为 Bound 和 Zoom,顾名思义是获取地图的聚合图层上对应的 Bound 和 Zoom 上所有的点,包括聚合点和普通点,一般情况下需要对返回值遍历判断类型
iconClusterLayer.getClusterPoints(id)
通过聚合点的 id 获取聚合点下的所有的普通 marker 点的 geojson 数组。
该方法的入参为聚合点的 geojson 数据的 id,在上面的getClusterData
方法返回的聚合点数组中会返回。BMapGLLib.GeoUtils.isPointInRect
该方法判断一个点是否在一个矩形内
BMapGLLib.GeoUtils.isPointInPolygon
该方法判断一个点是否在一个多边形内
BMapGLLib.GeoUtils.isPointInCircle
该方法判断一个点是否在一个圆形内
over~