如何用openlayers加载手绘地图可以看另一篇文章:https://www.jianshu.com/p/0b4fd0173b56
一、加载矢量标注及聚合标注
通常需要在地图上标注出一些景点、建筑或者公共设施,可以选择使用矢量标注或者聚合标注。openlayers 添加矢量标注的原理是将标注添加到一个新建的矢量层上,再将矢量层添加到地图上叠加显示;聚合标注的原理同上,但它适用于标注的数据量非常大的场景,随着层级的放大,会展示更多的标注,缩小时,则将标注聚合显示,能够在大量加载标注时提高渲染性能。
1、矢量标注
// 引入的对象和方法
import Vectors from 'ol/layer/Vector.js'
import { WMTS, Vector } from 'ol/source.js'
import Feature from 'ol/Feature'
import OlGeomPoint from 'ol/geom/Point'
import OlStyleStyle from 'ol/style/Style'
import OlStyleIcon from 'ol/style/Icon'
import Text from 'ol/style/Text'
import Fill from 'ol/style/Fill'
/*创建矢量标注
*@param{object} data 标注的数据
*/
createLabel(data) {
// 初始化标签要素
let feature = new Feature({
geometry: new OlGeomPoint(fromLonLat([+data.lng, +data.lat])), // 标签位置
name: data.name, // 标注显示的文字
img: require('./../../assets/imgs/map_icon_location.png'), // 标注显示的logo图片
})
feature.setId(data.id) // 设置ID
feature.setStyle(this.createLabelStyle(feature)) // 设置标注样式
let source = new Vector({}) // 初始化矢量数据源
source.addFeature(feature) // 将标签要素添加至矢量数据源
let layer = new Vectors({ // 创建矢量图层
source: source
})
this.map.addLayer(layer) // 将矢量图层添加至地图
}
/*创建标注样式
*@param{object} feature 标注要素
*@return {object} 返回创建的标注样式对象
*/
createLabelStyle(feature) {
//返回一个样式
return new OlStyleStyle({
//图标样式
image: new OlStyleIcon({
anchor: [10, 18], //设置图标偏移
scale: 0.6, // 图标缩小显示
anchorOrigin: 'top-right', //标注样式的起点位置
anchorXUnits: 'pixels', //X方向单位:分数
anchorYUnits: 'pixels', //Y方向单位:像素
offsetOrigin: 'bottom-left', //偏移起点位置的方向
opacity: 0.9, //透明度
src: feature.get('img') //图片路径
}),
//文本样式
text: new Text({
textAlign: 'center', //对齐方式
textBaseline: 'middle', //文本基线
font: 'normal 12px 微软雅黑', //字体样式
text: feature.get('name'), //文本内容
offsetY: -25, // Y轴偏置
fill: new Fill({ //填充样式
color: '#ffffff'
}),
backgroundFill: new Fill({ // 填充背景
color: asString([0, 0, 0, 0.6]),
}),
padding: [2, 5, 2, 5],
}),
// 设置层级
zIndex: 199
});
}
2、聚合标注
使用聚合标注后,标注间隙过密甚至出现重合的问题得以解决,待用户放大地图后再加载更多的标注。
// 引入的对象和方法
import Cluster from 'ol/source/Cluster'
import { WMTS, Vector } from 'ol/source.js'
import Feature from 'ol/Feature'
import OlGeomPoint from 'ol/geom/Point'
import OlStyleStyle from 'ol/style/Style'
import OlStyleIcon from 'ol/style/Icon'
import Text from 'ol/style/Text'
import Fill from 'ol/style/Fill'
/*创建聚合标注
*@param{object} data 标注的数据
*/
createClusterLabel(data) {
// 初始化标签要素
let feature = new Feature({
geometry: new OlGeomPoint(fromLonLat([+data.lng, +data.lat])), // 标签位置
name: data.name, // 标注显示的文字
img: require('./../../assets/imgs/map_icon_location.png'), // 标注显示的logo图片
})
feature.setId(data.id) // 设置ID
let source = new Vector({}) // 初始化矢量数据源
source.addFeature(feature) // 将标签要素添加至矢量数据源
let cluster = new Cluster({ // 创建聚合标注对象
distance: 25, // 设置聚合标注的距离
source: source
})
let layer = new Vectors({ // 创建矢量图层
source: cluster,
style: function(feature, resolutions) { // 这里可以参照上面的createLableStyle写,聚合标注在feature中单独设置的样式不生效
return new OlStyleStyle({
image: new OlStyleIcon({
src: feature.values_.features[0].values_.img // 图片路径
...
})
...
})
}
})
this.map.addLayer(layer) // 将聚合标注添加至地图
}
二、加载覆盖物(场景:点击标注弹出对话框)
openlayers 可以创建一个 Overlay 覆盖层,这个覆盖层能够展示自己写的 html 内容,从而实现添加各种所需的覆盖物。也可以通过这个方法来添加标注,但覆盖层添加的覆盖物会影响地图的拖动(即在覆盖物上进行滑动操作时地图无法响应,虽然可以通过设置 stopEvent 将滑动事件传递到地图上,但这样会导致在IOS端的覆盖物无法进行点击操作)。
因此如果要添加可以点击并且不影响地图拖动的标注时,建议使用矢量层标注,而如果要展示自定义的一些内容,如点击地图弹出对话信息框、地图上的自定义按钮等则使用覆盖层更加合适。
1、加载覆盖层
import Overlay from 'ol/Overlay.js'
/*创建覆盖物
*/
addOverlay() {
this.overlay = new Overlay({
element: document.getElementById('overlay-dlg'), // 将自己写的 html 内容添加到覆盖层,html 内容略
positioning: 'bottom-center', // 覆盖层位置
autoPan: true, // 是否自动平移,当点击时对话框超出屏幕边距,会自动平移地图使其可见
autoPanMargin: 20, // 设置自动平移边距
offset: [0, -20], // 覆盖层偏移起点的位置
className: 'overlay-test' // 覆盖物在覆盖层的类名
})
}
2、添加地图点击监听事件
/* 实现点击标注后在对应位置弹出信息框, 拿到标注的 feature 除了设置经纬度外,
还能通过其 id 向后台接口获取要展示的数据,再渲染到覆盖层的对话框上,这里代码给的比较简要,
有需要的可根据具体业务结合这个思路自行扩充代码 */
var content = ducoment.getElementsById('content')
this.map.on('singleclick', (e) => {
// 获取点击的标注
let features = this.map.forEachFeatureAtPixel(evt.pixel, function (feature, layerVetor) { return feature })
// 设置覆盖物的经纬度
this.overlay.setPosition([feature.values_.geometry.flatCoordinates[0], feature.values_.geometry.flatCoordinates[1]])
// 设置要显示的信息
content.html = '点击的 id 是' + feature.id_
// 将覆盖物添加到地图上
this.map.addOverlay(this.overlay)
})
三、绘制路线
原理大致与添加矢量标注相同,都是添加在矢量图层上,不过这里添加的是线对象。
import OlGeomLine from 'ol/geom/LineString'
/*绘制路线
*@param{array} routeList 路线数据
*/
createLineStroke(routeList) {
this.lineFeature = new Vector({}) // 创建路线矢量数据源
for(let i = 0; i < routeList.length; i++) { // 注意我这里是同时绘制多条路线
let polyLine = routeList[i].polyline.split(';')
let polyLintArray = []
polyLine.forEach(item => { // 处理接口传过来的路径数据,转换为 openlayers 所需的经纬度数据格式
item = item.split(',')
item = fromLonLat([+item[0], +item[1]])
polyLintArray.push(item)
})
let feature = new Feature({ // 创建路线属性
type: 'route',
geometry: new OlGeomLine(polyLintArray)
})
feature.setStyle(this.createLineStyle(i + 1))
// 添加路线箭头,不需要绘制箭头的可忽略
let img = require('./../../assets/imgs/icon_moer.png')
let style = [this.createLineStyle(i + 1)]
let geometry = feature.getGeometry()
let k = 0
geometry.forEachSegment((start, end) => {
k++
if (k % 50 !== 0) {
return
}
let dx = end[0] - start[0]
let dy = end[1] - start[1]
let rotation = Math.atan2(dy, dx) // 获取子线段的角度
style.push(new OlStyleStyle({
geometry: new OlGeomPoint(end),
image: new OlStyleIcon({
scale: 0.6,
src: img,
anchor: [0.75, 0.5], // 图标锚点
rotateWithView: true, // 与地图视图一起旋转
// 设置子线段箭头图标样式的角度
rotation: -rotation // 因为角度以顺时针旋转为正值,所以前面添加负号
}),
zIndex: 200,
}))
})
feature.setStyle(style)
this.lineFeature.addFeature(feature) // 将路线属性添加至矢量数据源中
}
this.lineLayer = new Vectors({ // 将路线矢量数据源添加至矢量层
source: this.lineFeature
})
this.map.addLayer(this.lineLayer) // 将矢量层添加到地图
}
/*创建路线样式
*@param{int} index 路线顺序
*/
createLineStyle(index) {
//返回一个样式
return new OlStyleStyle({
stroke: new Stroke({ // 路线填充样式:宽度、颜色等
width: 8,
color: [16,168,218, 1],
}),
text: new Text({
text: '路段' + index, // 路线标签文字
font: 'normal 12px 微软雅黑', //字体样式
fill: new Fill({ //文字填充样式
color: [16,168,218, 1]
}),
backgroundFill: new Fill({
color: asString([255, 255, 255, 0.9]),
}),
padding: [1, 2, 1, 2],
}),
zIndex: 196
});
},