需求
点击画圆、打点、画多边形、画折线按钮,用户可以在地图上进行对应的操作。同时根据特定条件,过滤出特定的点位。比如:在绘制的圆内部的点位、距离折线最近的10个点位、距离绘制的点最近的10个点位。
实现过程
通过OpenLayers的ol.interaction.Draw()实现。通过定义不同的type,绘制不同的feature。在地图上绘制的图形,实际上就是实现特定的feature。绘制完成后,通过监听drawend事件,可以拿到用户绘制的图形对象。再针对不同的type,做对应的处理。
- 可通过
geometry.intersectsExtent()传入feature信息,判断feature是否在geometry的范围内。 - 可通过
ol.sphere.getDistance([lon1, lat1], [lon2, lat2])计算两个经纬度之间的距离。经纬度的默认坐标系是EPSG:4326下的,经纬度可通过feature.getGeometry().getCoordinates()获得。
知识点
ol.interaction.Draw()用于在地图上进行绘制几何要素的交互操作。它允许用户通过鼠标或触摸设备在地图上绘制点、线、多边形等几何要素。创建后调用map.addInteraction()将交互添加到地图。调用map.removeInteraction()将交互从地图上删除。ol.interaction.Draw({type})的type参数Circle绘制圆,Polygon绘制多边形,LineString绘制折线,Point绘制点。ol.interaction.Draw({style})的stlye参数可以设置用户绘制过程中feature的样式style.image可设置鼠标鼠标样式,style.fill可设置填充样式,style.stroke可设置线条样式。绘制完成的样式,在drawend回调内,直接给event.feature设置样式。new ol.geom.LineString()创建折线的geometry。传入折线上点位坐标数组[[lon1, lat1], [lon2, lat2], [lon3, lat3]]。
new ol.style.Style({
image: new ol.style.Icon({
src: base64Img,
scale: 0.6,
anchor: [0.5, 0.5],
rotateWithView: true,
rotation: 0,
opacity: 1
}),
fill: new ol.style.Fill({ color: 'rgba(0, 0, 0, 0.1)' }),
stroke: new ol.style.Stroke({ color: '#07c160', width: 2 })
})
-
ol.interaction.Draw({source})的source参数设置在哪个图层的资源上进行绘制。要删除绘制的图形需要调用创建Draw时传入的source的removeFeature(feature)方法。 -
feature.getGeometry().intersectsExtent()方法用于检查几何图形是否与给定的范围(extent)相交。返回值是一个布尔值,表示是否相交。 -
feature.getGeometry().getExtent()用于获取几何对象的边界框(extent)。返回值是一个包含四个坐标值的数组,表示几何对象的最小外包矩形的边界框,形式为[minX, minY, maxX, maxY]。 -
feature.getGeometry().getCoordinates()用于获取 Feature 几何坐标的方法。 -
lineFeature.getGeometry().getClosestPoint(pointCoords)用于获取折线线要素上距离给定点最近的点坐标。 -
ol.sphere.getDistance()用户计算两个坐标之间的距离。参数是WGS84坐标系下的经纬度,返回的距离单位是米。 -
new ol.geom.LineString(coordinates)用于将一个由一系列坐标点连接而成的线。coordinates参数是一个包含线上各个点坐标的数组。返回值可以看做是一个geometry。可以作为new ol.Feature()的参数。 -
drawVector.getSource().addFeatures(featuresArr)批量添加feature到图层。没找到批量删除的方法,只能一个一个的删除drawVector.getSource().removeFeature(feature)。
代码HTML+CSS+JS
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/openlayers/8.2.0/ol.min.css" integrity="sha512-bc9nJM5uKHN+wK7rtqMnzlGicwJBWR11SIDFJlYBe5fVOwjHGtXX8KMyYZ4sMgSL0CoUjo4GYgIBucOtqX/RUQ==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<title>图形绘制</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html,
body,
#app,
.app-map {
height: 100%;
height: 100%;
}
.app-btns {
position: fixed;
right: 10px;
top: 10px;
background-color: #fff;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, .5);
width: 210px;
padding: 25px;
text-align: center;
border-radius: 5px;
display: flex;
flex-direction: column;
z-index: 2;
}
.app-btns button {
font-size: 18px;
border: none;
padding: 12px 20px;
border-radius: 4px;
color: #fff;
background-color: #409eff;
border-color: #409eff;
cursor: pointer;
border: 1px solid #dcdfe6;
margin-bottom: 5px;
}
.app-btns button:hover {
background: #66b1ff;
border-color: #66b1ff;
}
.app-btns button.active {
background-color: #07c160;
}
</style>
</head>
<body>
<div id="app">
<div class="app-map" id="app-map"></div>
<div class="app-btns">
<button type='button' :class="{active: item.id === currentId}" v-for='item in drawTypes' @click='handleClickDraw(item)'>{{item.text}}</button>
<button type='button' @click='handleClickCancel' style="background-color: #808080;">退出绘制</button>
<button type='button' @click='handleClickDelete' style="background-color: #ff0000;">删除图形</button>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/openlayers/8.2.0/dist/ol.min.js" integrity="sha512-+nvfloZUX7awRy1yslYBsicmHKh/qFW5w79+AiGiNcbewg0nBy7AS4G3+aK/Rm+eGPOKlO3tLuVphMxFXeKeOQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.4.14/vue.global.prod.min.js" integrity="sha512-huEQFMCpBzGkSDSPVAeQFMfvWuQJWs09DslYxQ1xHeaCGQlBiky9KKZuXX7zfb0ytmgvfpTIKKAmlCZT94TAlQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script>
const { createApp } = Vue;
// feature图片
const base64Img = 'data:image/png;base64,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';
// 画时多边形鼠标悬浮样式
const polygonHover = 'data:image/png;base64,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';
// 画时圆鼠标悬浮样式
const circleHover = 'data:image/png;base64,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';
// 画时折线鼠标悬浮样式
const lineStringHover = 'data:image/png;base64,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';
// 基础样式
const basePointStyle = {
src: base64Img,
scale: 0.6,
anchor: [0.5, 0.5],
rotateWithView: true,
rotation: 0,
opacity: 1
};
// 根据基础样式,创建其他样式
const createStyle = (imageParams, styleParams) => {
return new ol.style.Style({
image: new ol.style.Icon({
...basePointStyle,
...imageParams
}),
...styleParams
});
};
// 填充样式
const fill = new ol.style.Fill({ color: 'rgba(0, 0, 0, 0.1)' });
// 线条样式
const stroke = new ol.style.Stroke({ color: '#07c160', width: 2 });
// 基础样式style
const baseStyle = createStyle({
scale: 1
});
// 范围内的点位样式
const inStyle = createStyle({ scale: 1.5 });
// 在地图上绘制的点的样式
const pointStyle = createStyle({ scale: 2 });
// 圆,多边形范围外的点位的样式
const outStyle = createStyle({ opacity: 0.6 });
// 绘制点时鼠标悬浮的状态
const hoverStyle = {
"Point": createStyle({ scale: 1.5 }),
'Polygon': createStyle({
src: polygonHover,
scale: 0.3
}, {
fill, stroke
}),
'Circle': createStyle({
src: circleHover,
scale: 0.3
}, {
fill, stroke
}),
'LineString': createStyle({
src: lineStringHover,
scale: 0.3
}, {
stroke
})
};
const vm = createApp({
data() {
return {
map: {}, // 地图实例
drawSource: {}, // 绘制图形的图层资源
draw: null, // 绘制实例
pointLayer: null, // 点位图层
prevFeature: null, // 当前绘制完成的图形
currentId: -1, // 当前绘制类型id
closest10Features: [], // 最近的10个点位
drawTypes: [{
type: 'Circle',
text: '画圆',
id: 1
}, {
type: 'Point',
text: '打点',
id: 2
}, {
type: 'Polygon',
text: '画多边形',
id: 3
}, {
type: 'LineString',
text: '画折线',
id: 5
}]
}
},
methods: {
// 初始化地图
initMap() {
// 创建放置用户绘制的feature的图层
this.drawSource = new ol.source.Vector();
const layer = new ol.layer.Vector({
source: this.drawSource,
});
// 高德地图瓦片地址
const mianLayer = new ol.layer.Tile({
source: new ol.source.XYZ({
url: 'http://wprd04.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=7&x={x}&y={y}&z={z}'
}),
name: '初始化地图图层'
});
// 初始化地图
this.map = new ol.Map({
target: 'app-map',
layers: [mianLayer, layer],
view: new ol.View({
projection: 'EPSG:3857',
//设定中心点,因为默认坐标系为 3587,所以要将我们常用的经纬度坐标系4326 转换为 3587坐标系
center: ol.proj.transform([111.8453154, 32.7383500], 'EPSG:4326', 'EPSG:3857'),
zoom: 5,
})
});
// 绑定地图事件
this.bindMapEvt();
// 渲染1000个点位到地图上
this.renderPoint(1000);
},
// 绑定地图事件
bindMapEvt() {
// 监听鼠标点击
this.map.on('click', (evt) => {
const coordinate = ol.proj.transform(evt.coordinate, 'EPSG:3857', 'EPSG:4326')
console.log('当前点击坐标为 : ' + coordinate[0].toFixed(7) + ',' + coordinate[1].toFixed(7));
});
},
// 在地图上添加点位
renderPoint(numPoints) {
const positions = this.createCircularPosition(numPoints); // 生成坐标数据
// 根据positions创建一个新的数据源和要素数组,
const vectorSource = new ol.source.Vector({
features: positions.map(e => {
const feature = new ol.Feature({
geometry: new ol.geom.Point(ol.proj.fromLonLat(e)),
custom: {
id: Math.ceil(Math.random() * 100000)
}
});
feature.setStyle(baseStyle);
return feature;
})
});
// 创建带有数据源的矢量图层
this.pointLayer = new ol.layer.Vector({
source: vectorSource,
layerID: 'addpointLayer'
});
// 将矢量图层添加到地图上
this.map.addLayer(this.pointLayer);
},
// 点击绘制各种图形
handleClickDraw(item) {
const { map } = this;
const { type, geometryFunction, id } = item;
// 同一种类型点击,不处理
if (this.currentId === id) {
return;
}
this.currentId = id;
// 删除上一种绘制
this.draw && map.removeInteraction(this.draw);
// 恢复所有点位的透明度为1
this.setFeatureOpacity();
// 根据 type 创建交互
this.draw = new ol.interaction.Draw({
source: this.drawSource,
type: type,
style: hoverStyle[type],
geometryFunction: geometryFunction
});
// 将交互添加到地图
map.addInteraction(this.draw);
// 监听绘制完成的事件
this.draw.on('drawend', (event) => {
// 获取绘制的feature
const feature = event.feature;
// 删除上一个feature
this.prevFeature && this.drawSource.removeFeature(this.prevFeature);
this.prevFeature = feature;
// 删除折线较近的10个点位的连线
this.removeClosestLine();
// 如果是圆和多边形,将圆内、外的点位设置不同的样式
if (type === "Circle" || type === 'Polygon') {
this.handleDrawCirclePolygonEnd(feature);
}
// 如果是打点,计算点位距离1000个已有点位的距离,选取最近的10个,将其连接到绘制的点上
if (type === 'Point') {
this.handleDrawPointEnd(feature);
}
// 如果是折线,设置折线的颜色,计算1000个已有点位距离折线的最短距离,选取最近的10个,将其连接到折线上
if (type === 'LineString') {
this.handleDrawLineStringEnd(feature);
}
});
},
// 处理绘制圆和多边形的处理
handleDrawCirclePolygonEnd(feature) {
// 设置绘制完成后圆和多边形的样式
feature.setStyle(new ol.style.Style({
fill: new ol.style.Fill({ color: 'rgba(255, 0, 0, 0)' }),
stroke: new ol.style.Stroke({ color: '#07c160', width: 2 })
}));
// 获取feature的Geometry
const geometry = feature.getGeometry();
// 遍历1000个点位
this.pointLayer.getSource().forEachFeature(function(point) {
const pointGeometry = point.getGeometry();
// 判断监控点位是否在范围内
if (geometry.intersectsExtent(pointGeometry.getExtent())) {
point.setStyle(inStyle); // 在范围内
} else {
point.setStyle(outStyle); // 不在范围内
}
});
},
// 处理绘制点结束
handleDrawPointEnd(feature) {
// 设置点的样式
feature.setStyle(pointStyle);
// 获取点的投影坐标系坐标
const pointCoord = feature.getGeometry().getCoordinates();
// 找出和这个投影坐标系坐标最近的10个点
const closest10 = this.calcDisFromPoint(pointCoord);
// 10个点和绘制的点连线
this.drawLine(closest10);
},
// 处理折线结束
handleDrawLineStringEnd(feature) {
// 设置折线样式
feature.setStyle(new ol.style.Style({
stroke: new ol.style.Stroke({
color: '#07c160', // 修改边框颜色为红色
width: 5, // 修改边框宽度
}),
}));
const pointDistanceInfo = [];
// 遍历1000个点位,计算距离
this.pointLayer.getSource().forEachFeature(function(point) {
// 获取折线的几何对象
const lineGeometry = feature.getGeometry();
// 获取点的坐标
const pointCoords = point.getGeometry().getCoordinates();
// 获取折线上与点最近的点
const closestPoint = lineGeometry.getClosestPoint(pointCoords);
// 将经纬度转换成WGS84坐标
const pointCoordsLogLat = ol.proj.transform(pointCoords, 'EPSG:3857', 'EPSG:4326');
const closestPointLogLat = ol.proj.transform(closestPoint, 'EPSG:3857', 'EPSG:4326');
// 计算点位和折线的最近距离
const currentDis = ol.sphere.getDistance(pointCoordsLogLat, closestPointLogLat);
pointDistanceInfo.push({
coordinate: [pointCoords, closestPoint],
dis: currentDis,
feature: point
})
});
// 按照距离从小到大排序
pointDistanceInfo.sort((v1, v2) => v1.dis - v2.dis);
// 连接较近的10个点位和折线
this.drawLine(pointDistanceInfo.slice(0, 10));
},
// 过滤1000个点位,计算点位和指定点的距离,返回距离最短的前10个
calcDisFromPoint(pointWithCoords) {
const pointDistanceInfo = [];
// 遍历点位,计算距离
this.pointLayer.getSource().forEachFeature(function(point) {
// 获取点的坐标
const pointCoords = point.getGeometry().getCoordinates();
const pointLogLat = ol.proj.transform(pointCoords, 'EPSG:3857', 'EPSG:4326');
const pointCoordsLonLag = ol.proj.transform(pointWithCoords, 'EPSG:3857', 'EPSG:4326');
// 计算点位之间的距离
const currentDis = ol.sphere.getDistance(pointLogLat, pointCoordsLonLag);
pointDistanceInfo.push({
coordinate: [pointWithCoords, pointCoords],
dis: currentDis,
feature: point
})
});
// 按照距离从小到大排序
pointDistanceInfo.sort((v1, v2) => v1.dis - v2.dis);
return pointDistanceInfo.slice(0, 10);
},
// 点击取消,删除上一个交互
handleClickCancel() {
this.currentId = 'none';
this.draw && this.map.removeInteraction(this.draw);
this.draw = null;
},
// 删除图形
handleClickDelete () {
// 删除上一个feature
this.prevFeature && this.drawSource.removeFeature(this.prevFeature);
// 删除连线
this.removeClosestLine();
// 恢复点位状态
this.setFeatureOpacity();
},
// 恢复点位状态
setFeatureOpacity() {
// 遍历1000个点位,恢复样式
this.pointLayer.getSource().forEachFeature(function(monitorFeature) {
monitorFeature.setStyle(baseStyle);
});
},
// 两点之间绘制直线,数组中的坐标需要是投影坐标
drawLine(pointArr) {
for (let i = 0; i < pointArr.length; i++) {
const { coordinate, feature } = pointArr[i];
// 创建折线
console.log(coordinate);
const lineString = new ol.geom.LineString(coordinate);
// 创建Feature
const lineFeature = new ol.Feature(lineString);
// 设置Feature样式
const lineStyle = new ol.style.Style({
stroke: new ol.style.Stroke({
color: '#' + Math.random().toString(16).slice(2, 8),
width: 3
}),
});
// 将样式应用到要素
lineFeature.setStyle(lineStyle);
feature.setStyle(inStyle);
this.closest10Features.push(lineFeature);
}
// 将10条线段添加到图层
this.drawSource.addFeatures(this.closest10Features);
},
// 删除距离折线相对较近的点位与折线的连线
removeClosestLine() {
if (this.closest10Features.length > 0) {
for (let i = 0; i < this.closest10Features.length; i++) {
this.drawSource.removeFeature(this.closest10Features[i]);
}
}
this.closest10Features = [];
},
// 创建数据
createCircularPosition(numPoints) {
const center = [108.55, 34.32];
const circularManyPosition = [];
const minLat = 3.86;
const maxLat = 53.56;
const minLon = 73.66;
const maxLon = 135.05;
for (var i = 0; i < numPoints; i++) {
var lat = Math.random() * (maxLat - minLat) + minLat;
var lon = Math.random() * (maxLon - minLon) + minLon;
circularManyPosition.push([lon, lat]);
}
return circularManyPosition;
}
},
mounted() {
this.initMap();
}
}).mount('#app')
</script>
</body>
</html>