leaflet实现地图上的自定义绘制,主要是通过监听map的click、mousemove、dblclick事件来获得对应的点位,然后根据获得的点位进行实时的绘制来达到相应的效果。
监听map的click事件,获得第一个点,然后开启map的mousemove事件监听,实时重绘矩形,然后再地图第二次点击时,获得第二个点,构成最终绘制的矩形,同时去掉对map的click事件和mousemove事件的监听。
createRectangle() {
let tmplist = []
let juxing;
map.on("click", (e) => {
tmplist.push([e.latlng.lat, e.latlng.lng])
this.map.on('mousemove', (ev) => {
if (tmplist.length > 1) {
tmplist.splice(1)
}
tmplist.push([ev.latlng.lat, ev.latlng.lng])
this.tmpgroup.clearLayers()
juxing = L.rectangle(tmplist, {//绘制矩形
color: "#ff7800",
weight: 1
}).addTo(this.tmpgroup);
})
if (tmplist.length > 1) {
tmplist.pop()//第二次点击会触发第一次的push()导致得到三个数据(后两个一样),所以删除最后一个
this.map.off('mousemove')//两点确定,移除鼠标移动事件
this.map.off('click')//两点确定,移除点击事件,
this.tmpgroup.clearLayers()
L.rectangle(tmplist, {//绘制矩形
color: "#ff7800",
weight: 1
}).addTo(this.rectgroup);
}
})
}
监听map的click事件,获得第一个点,然后开启map的mousemove事件监听,当点少于1个的时候,绘制成线,做扇形的一条边,然后再地图第二次点击时,获得第二个点,构成扇形的第二个点,同时在移动时有第3个点,则可构成扇形,进行实时重绘,在地图地3次点击时,绘制成最终的扇形。
扇形需要计算第1个点和第2个点之间的距离作为扇形的半径,计算第1个点和第2个点、第1个点和第3个点的夹角,可以使用地理空间分析库turf来完成计算,
_circle(latlng, options) {
let svg = L.svg();
return L.semiCircle(latlng, L.extend({
radius: 50000,
color: '#f03',
opacity: 1,
renderer: svg,
weight: 1
}, options));
},
_dis(fromPoint, toPoint) {
let from = turf.point(fromPoint); //[-75.343, 39.984]
let to = turf.point(toPoint);
let options = { units: 'kilometers' };
let distance = turf.rhumbDistance(from, to, options);
return distance;
},
_ber(fromPoint, toPoint) {
let point1 = turf.point(fromPoint);
let point2 = turf.point(toPoint);
let bearing = turf.rhumbBearing(point1, point2);
return bearing;
},
_drawSemiCircle(pointList, layerGroup) {
let tmp = JSON.parse(JSON.stringify(pointList))
console.log(JSON.stringify(pointList));
let p1 = tmp[0].reverse();
let p2 = tmp[1].reverse();
let radius = this._dis(p1, p2);
//计算第1个点和第2点前鼠标点之间的弧度
let ber1 = this._ber(p1, p2);
//计算第1个点和第3点前鼠标点之间的弧度
let p3 = tmp[2].reverse();
let ber2 = this._ber(p1, p3);
console.log('radius-->' + radius + ',ber1--->' + ber1 + ',ber2--->' + ber2);
if (!!radius && !!ber1 && !!ber2) {
/*
方式一:采用SemiCircle类库,但是无法获取到点串
let scircle = this._circle(p1.reverse(), {
radius: radius * 1000,
color: '#2260b4',
startAngle: ber1,
stopAngle: ber2,
weight: 4
});
scircle.addTo(layerGroup);*/
//方式二,通过计算出扇形的点位,以面的方式在地图上叠加
let points = this._getSemiCirclePoints(p1.reverse(), radius / 100, ber1, ber2, 500);
points[points.length] = points[0];
let semiCirclePolygon = L.polygon(points);
semiCirclePolygon.addTo(layerGroup)
}
},
/**
*
* @param center 中心点 数组 [lat, lon]
* @param radius 半径
* @param startAngle 起始角度
* @param endAngle 终止角度
* @param pointNum 圆弧上点的个数
*/
_getSemiCirclePoints(center, radius, startAngle, endAngle, pointNum) {
let sin;
let cos;
let x;
let y;
let angle;
let points = [];
points.push(center);
for (let i = 0; i <= pointNum; i++) {
angle = startAngle + (endAngle - startAngle) * i / pointNum;
sin = Math.sin(angle * Math.PI / 180);
cos = Math.cos(angle * Math.PI / 180);
y = center[0] + radius * cos;
x = center[1] + radius * sin;
points[i] = [y, x];
}
let point = points;
point.push(center);
return point;
},
createSemiCircle() {
let pointList = [];
this.semicirclegroup.clearLayers();
this.map.on("click", (e) => {
pointList.push([e.latlng.lat, e.latlng.lng]);
this.map.on('mousemove', (ev) => {
let lastPoint = pointList[pointList.length - 1];
if (pointList.length === 1) {
let latlngs = [
lastPoint,
[ev.latlng.lat, ev.latlng.lng]
]
this.semicirclegroup.clearLayers();
L.polyline(latlngs, {
color: "#03f",
weight: 3
}).addTo(this.semicirclegroup);
} else if (pointList.length === 2) {
//绘制成一个扇形
let arr = JSON.parse(JSON.stringify(pointList));
arr.push([ev.latlng.lat, ev.latlng.lng]);
this.semicirclegroup.clearLayers();
this._drawSemiCircle(arr, this.semicirclegroup);
}
})
if (pointList.length > 2) {
this.map.off('mousemove')//两点确定,移除鼠标移动事件
this.map.off('click')//两点确定,移除点击事件,
//绘制扇形在地图上
this.semicirclegroup.clearLayers();
let arr = [
pointList[0], pointList[1], pointList[pointList.length - 1]
];
this._drawSemiCircle(arr, this.semicirclegroup);
}
})
},
线绘制的实现思路跟上面的类似,只是多一个,这个会做一个临时图层来显示最后一段绘制的效果。
createPolyline() {
let pointList = [];
map.on("click", (e) => {
pointList.push([e.latlng.lat, e.latlng.lng]);
this.map.on('mousemove', (ev) => {
//临时图层直线是最后一段线即可
let lastPoint = pointList[pointList.length - 1]
let latlngs = [
lastPoint,
[ev.latlng.lat, ev.latlng.lng]
]
this.tmpgroup.clearLayers();
L.polyline(latlngs, {
color: "#2260b4",
weight: 3
}).addTo(this.tmpgroup);
})
this.map.on('dblclick', (ev) => {
this.map.off('mousemove')//两点确定,移除鼠标移动事件
this.map.off('click')//两点确定,移除点击事件,
pointList.push([ev.latlng.lat, ev.latlng.lng]);
this.tmpgroup.clearLayers();
this.semicirclegroup.clearLayers()
if (pointList.length > 1) {
L.polyline(pointList, {
color: "#ff7800",
weight: 5
}).addTo(this.semicirclegroup);
}
})
if (pointList.length > 1) {
this.semicirclegroup.clearLayers();
L.polyline(pointList, {
color: "#ff7800",
weight: 3
}).addTo(this.semicirclegroup);
}
})
},
参考文章:
leaflet 画扇形