思路
- 在openlayers中线的坐标是[[56.77,35.12],[57.13,17.33],[73.21,17.24]]这样的一个二维数组
- 要实现拖动点修改线,就必须在线坐标基础上再生成几个坐标,这个坐标就是可拖动点
实现
1.在画线结束后,计算出控制点坐标,并生成点(包括线锚点)
draw = new Draw({
source: new VectorSource(),
type: 'LineString',
});
// 监听画线结束操作
draw.on('drawend',(event)=>{
const feature = event.feature; // 获取绘制完成的特征
const geometry = feature.getGeometry(); // 获取特征的几何对象
const coordinates = geometry.getCoordinates(); // 获取线的坐标数组
// 计算每对相邻点之间的中点坐标
let updatedCoordinates = [];
for (let i = 0; i < coordinates.length - 1; i++) {
const point1 = coordinates[i];
const point2 = coordinates[i + 1];
const midX = (point1[0] + point2[0]) / 2;
const midY = (point1[1] + point2[1]) / 2;
// 将当前点和中点插入
updatedCoordinates.push(point1);
updatedCoordinates.push([midX, midY]);
}
// 添加最后一个点
updatedCoordinates.push(coordinates[coordinates.length - 1]);
// 更新几何对象
geometry.setCoordinates(updatedCoordinates);
let key = 0
updatedCoordinates.forEach((ele, index) => {
let pointFeature = new Feature(new Point(ele)) //创建点特征
if(index % 2 === 0){ //是线的锚点
// 设置锚点的样式并标识锚点个数
pointFeature.setStyle(new Style({
image: new Circle({
radius: 7,
fill: new Fill({
color: '#ffcc33',
}),
}),
text: new Text({
text: key.toString(), // 将索引转换为字符串作为文本
font: '12px sans-serif',
fill: new Fill({
color: '#000',
}),
stroke: new Stroke({
color: '#fff',
width: 3,
}),
offsetX: 0, // 文本相对于点的位置偏移
offsetY: -15,
}),
}));
key++
}else{ //线的控制点
pointFeature.setStyle(new Style({
image: new Circle({
radius: 7,
fill: new Fill({
color: '#fff',
}),
}),
}));
pointFeature.set('index',index) // 用来标识控制点
}
})
})
2.拖动控制点后重新生成点位
modify.on('modifyend', (event) => {
// 拖动控制点时找到拖动的线特征和点特征,找到
let featurePoint = event.features.array_.find(ele => ele.values_.geometry.getType() === 'Point')
let featureLine = event.features.array_.find(ele => ele.values_.geometry.getType() === 'LineString').values_.geometry
let index = featurePoint.values_.index
let coords = featurePoint.getGeometry().getCoordinates()
let nates = featureLine.getCoordinates()
let arr = [nates[index - 1], coords, nates[index + 1]] // 用于计算重新生成的点位坐标
let newArr = []
if(!index) return //如果是锚点直接return
// 清除所有的点特征
source.forEachFeature(function(feature) {
if (feature.getGeometry().getType() === 'Point') {
source.removeFeature(feature);
}
});
// 计算新的控制点
for (let i = 0; i < arr.length-1; i++){
const point1 = arr[i];
const point2 = arr[i + 1];
const midX = (point1[0] + point2[0]) / 2;
const midY = (point1[1] + point2[1]) / 2;
newArr.push(point1)
newArr.push([midX, midY])
}
newArr.push(nates[index+1])
nates.splice(index-1,3, ...newArr) //替换原坐标数组的几个点位
featureLine.setCoordinates(nates); // 更新几何对象
let key=0
// 重新生成锚点,控制点
nates.forEach((ele, index2) => {
let pointFeature = new Feature(new Point(ele))
if (index2 % 2 === 0) {
pointFeature.setStyle(new Style({
image: new Circle({
radius: 7,
fill: new Fill({
color: '#ffcc33',
}),
}),
text: new Text({
text: key.toString(), // 将索引转换为字符串作为文本
font: '12px sans-serif',
fill: new Fill({
color: '#000',
}),
stroke: new Stroke({
color: '#fff',
width: 3,
}),
offsetX: 0, // 文本相对于点的位置偏移
offsetY: -15,
}),
}))
key++
} else {
pointFeature.setStyle(new Style({
image: new Circle({
radius: 7,
fill: new Fill({
color: '#fff',
}),
}),
}));
pointFeature.set('index',index2)
}
source.addFeature(pointFeature)
});
})
全部代码,(在官方示例上修改的,直接复制即可使用)
import Map from 'ol/Map.js';
import View from 'ol/View.js';
import { Draw, Modify, Snap } from 'ol/interaction.js';
import { OSM, Vector as VectorSource } from 'ol/source.js';
import { Tile as TileLayer, Vector as VectorLayer } from 'ol/layer.js';
import { get } from 'ol/proj.js';
import Feature from 'ol/Feature.js'
import { Point } from 'ol/geom.js'
import { Style, Circle, Fill, Stroke, Text } from 'ol/style.js'
const raster = new TileLayer({
source: new OSM(),
});
const source = new VectorSource();
const vector = new VectorLayer({
source: source,
style: {
'fill-color': 'rgba(255, 255, 255, 0.2)',
'stroke-color': '#ffcc33',
'stroke-width': 2,
'circle-radius': 7,
'circle-fill-color': '#ffcc33',
},
});
const map = new Map({
layers: [raster, vector],
target: 'map',
view: new View({
zoom: 4,
// 视图
projection: 'EPSG:4326', // 坐标系
// 初始化地图中心
center: [108.81, 23.22],
}),
});
const modify = new Modify({
source: source, style: null, insertVertexCondition: function (ele) {
return false;
}
});
map.addInteraction(modify);
let draw, snap;
const typeSelect = document.getElementById('type');
let lineFeature = null
function addInteractions() {
draw = new Draw({
source: source,
type: 'LineString',
});
map.addInteraction(draw);
draw.on('drawend', function (event) {
const feature = event.feature; // 获取绘制完成的特征
const geometry = feature.getGeometry(); // 获取特征的几何对象
const coordinates = geometry.getCoordinates(); // 获取线的坐标数组
let updatedCoordinates = [];
for (let i = 0; i < coordinates.length - 1; i++) {
const point1 = coordinates[i];
const point2 = coordinates[i + 1];
const midX = (point1[0] + point2[0]) / 2;
const midY = (point1[1] + point2[1]) / 2;
// 将当前点和中点插入到更新后的坐标数组中
updatedCoordinates.push(point1);
updatedCoordinates.push([midX, midY]);
}
// 添加最后一个点
updatedCoordinates.push(coordinates[coordinates.length - 1]);
// 更新几何对象
geometry.setCoordinates(updatedCoordinates);
let key = 0
updatedCoordinates.forEach((ele, index) => {
let pointFeature = new Feature(new Point(ele))
if (index % 2 === 0) {
pointFeature.setStyle(new Style({
image: new Circle({
radius: 7,
fill: new Fill({
color: '#ffcc33',
}),
}),
text: new Text({
text: key.toString(), // 将索引转换为字符串作为文本
font: '12px sans-serif',
fill: new Fill({
color: '#000',
}),
stroke: new Stroke({
color: '#fff',
width: 3,
}),
offsetX: 0, // 文本相对于点的位置偏移
offsetY: -15,
}),
}));
key++
} else {
pointFeature.setStyle(new Style({
image: new Circle({
radius: 7,
fill: new Fill({
color: '#fff',
}),
}),
}));
pointFeature.set('index', index)
}
source.addFeature(pointFeature)
});
setTimeout(() => {
draw.setActive(false)
}, 500);
});
modify.on('modifyend', (event) => {
// 控制点位于坐标数组的第几个
let featurePoint = event.features.array_.find(ele => ele.values_.geometry.getType() === 'Point')
let featureLine = event.features.array_.find(ele => ele.values_.geometry.getType() === 'LineString').values_.geometry
let index = featurePoint.values_.index
let coords = featurePoint.getGeometry().getCoordinates()
let nates = featureLine.getCoordinates()
let arr = [nates[index - 1], coords, nates[index + 1]]
let newArr = []
// 清除所有的点特征
if (!index) return
source.forEachFeature(function (feature) {
if (feature.getGeometry().getType() === 'Point') {
source.removeFeature(feature);
}
});
for (let i = 0; i < arr.length - 1; i++) {
const point1 = arr[i];
const point2 = arr[i + 1];
const midX = (point1[0] + point2[0]) / 2;
const midY = (point1[1] + point2[1]) / 2;
newArr.push(point1)
newArr.push([midX, midY])
}
newArr.push(nates[index + 1])
nates.splice(index - 1, 3, ...newArr)
featureLine.setCoordinates(nates);
let key = 0
nates.forEach((ele, index2) => {
let pointFeature = new Feature(new Point(ele))
if (index2 % 2 === 0) {
pointFeature.setStyle(new Style({
image: new Circle({
radius: 7,
fill: new Fill({
color: '#ffcc33',
}),
}),
text: new Text({
text: key.toString(), // 将索引转换为字符串作为文本
font: '12px sans-serif',
fill: new Fill({
color: '#000',
}),
stroke: new Stroke({
color: '#fff',
width: 3,
}),
offsetX: 0, // 文本相对于点的位置偏移
offsetY: -15,
}),
}))
key++
} else {
pointFeature.setStyle(new Style({
image: new Circle({
radius: 7,
fill: new Fill({
color: '#fff',
}),
}),
}));
pointFeature.set('index', index2)
}
source.addFeature(pointFeature)
});
})
snap = new Snap({ source: source });
map.addInteraction(snap);
}
/**
* Handle change event.
*/
typeSelect.onchange = function () {
map.removeInteraction(draw);
map.removeInteraction(snap);
addInteractions();
};
addInteractions();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Draw and Modify Features</title>
<link rel="stylesheet" href="node_modules/ol/ol.css">
<style>
.map {
width: 100%;
height: 400px;
}
</style>
</head>
<body>
<div id="map" class="map"></div>
<form>
<label for="type">Geometry type </label>
<select id="type">
<option value="LineString">LineString</option>
<option value="Polygon">Polygon</option>
<option value="Circle">Circle</option>
</select>
</form>
<!-- Pointer events polyfill for old browsers, see https://caniuse.com/#feat=pointer -->
<script src="./resources/elm-pep.js"></script>
<script type="module" src="main.js"></script>
</body>
</html>
实现效果