leaflet实现自定义线、矩形和扇形的绘制

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来完成计算,

扇形的图形绘制有两种方式实现:
  • 第一种方法:采用Leaflet-semicircle来直接绘制,这种是采用svg进行的绘制,存在一个问题就是无法获取到会指出来的扇形的点位;
  • 第二种方法,采用计算公式来计算出整个扇形的点位,然后以面的方式在地图上绘制出来。
_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 画扇形

你可能感兴趣的:(GIS,前端,javascript,算法)