美文网首页
基于事件实现Arcgis绘制功能

基于事件实现Arcgis绘制功能

作者: dsjaikdnsajdnua | 来源:发表于2019-01-15 19:26 被阅读94次

    Arcgis JavaScript Api 3.23、Typescript

    背景

    需要做一个地图小工具(测距、侧面积、缓冲区分析),我尝试了Arcgis 自带的绘制工具,但由于图层比较复杂,并且自带的工具几乎不能定制。我就打算使用基本的事件去实现绘制点线面的工具。大体的步骤为:分析点线面的绘制过程,然后监听鼠标事件,再进行绘制。因为是半年前的事情了,所以就简单重现一下。以下主要提供思路参考(千万不要想直接复制代码就能运行)。

    分析点、线、面的绘制过程

    • 点:点很简单。就是监听鼠标事件,然后获取鼠标的坐标,然后再绘制就可以了。


      绘制点的动作
    • 圆:圆的绘制和点的差不多,也是监听鼠标单击事件,然后获取鼠标的坐标,再根据半径绘制圆。


      圆的绘制动作
    • 线:单击地图开始绘制,从起始点,线段跟随鼠标移动,再此单击鼠标,绘制2个端点的线,线段从新的起点继续跟随鼠标移动。直到双击,绘制多段线结束。


      线段绘制的动作
    • 面:单击地图开始绘制,从起始点,线段跟随鼠标移动,再此单击鼠标,绘制2个短点的线,线段从新的起点继续跟随鼠标移动,首尾相接,同时出现一个多边形,并随着鼠标,多边形的顶点不断变化。直到双击,绘制面结束。


      面绘制的动作

    实现过程

    重点!我一开始是直接监听map的事件,但是发现这样做在切换底图的时候,监听事件会出现问题(我遇到好几个问题,但是具体的我也没有做记录)。所以,我直接监听了地图DIV的原始dom的事件,放心监听的时间类型是:esri.AGSMouseEvent,所以会见地理信息都带过来。

    准备工作,写辅助的方法

    • 先写一个工具类:Utils.ts,然后写添加/移除事件的方法,为了兼容IE,直接使用最普通的监听也是可以的、写将点集合转化成线/面集合的二维数组。
      /**  
         *@description 事件绑定,兼容各浏览器  
         * @param target 事件触发对象   
         * @param type   事件  
         * @param func   事件处理函数  
         */  
        static addEvents(target:HTMLElement, type:string, func:any) {  
            if (target.addEventListener)    //非ie 和ie9  
                target.addEventListener(type, func, false);  
            else if ((target as any).attachEvent)   //ie6到ie8  
                (target as any).attachEvent("on" + type, func);  
            else target["on" + type] = func;   //ie5  
        };
    
    /**  为了不影响地图自身的事件,所以我们需要在操作完成之后移除所有的绘制事件
         * @description 事件移除,兼容各浏览器  
         * @param target 事件触发对象  
         * @param type   事件  
         * @param func   事件处理函数  
         */  
        static removeEvents(target:HTMLElement, type:string, func:any){  
            if (target.removeEventListener)  
                target.removeEventListener(type, func, false);  
            else if ((target as any).detachEvent)  
                (target as any).detachEvent("on" + type, func);  
            else target["on" + type] = null;  
        }; 
    
      /**
         * @param pts 将多个点数组,解析成线段的点集合。
         * @returns ptArr 返回点的二维数组
         */
      public static buildCoordinates(pts:Point[]){
          let ptArr:number[][] = [];
          if(pts){
              pts.forEach(pt => {
                  ptArr.push([pt.x,pt.y])
              });
          }
          return ptArr;
      }
    
     /**
       * 返回一个通用的面
       */
    public static getComFillSymbol(){
        return new SimpleFillSymbol(SimpleFillSymbol.STYLE_SOLID,new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color("red"), 2),new Color([30,30,30, 0.5]));
      }
    
    • 添加一个专门存放绘制临时图形的图层,因为绘制线/多边形的过程会不断的清除图层。这个图层不是常驻图层,所以能直接全部清除,方便操作
      drawEntity:{
            layer?:GraphicsLayer;
            geometry?:Geometry;//存放临时图形
        } = {
            layer:new GraphicsLayer({id:"buffer-draw",opacity: 1})
        }
    

    绘制过程。监听各种事件

    • 点、圆的绘制,调用以上的addEvents方法,添加对地图div的“click”事件,然后在点击事件里面(arcgis官方封装过有传递坐标信息),根据事件传递过来的坐标信息绘制相应的点符号即可:
        /**  
         * @param viewDiv 为地图的div   
         * @param toolboxLayer   将图形绘制的图层  
         * @param func   事件处理函数  
         */  
    Utils.addEvents(this.viewDiv,"click",function(event: Event){
      let point = (event as esri.AGSMouseEvent).mapPoint;
      let circle = new Circle({
          center: point,
          radius: 10,
          radiusUnit: "esriKilometers"
      });
      let symbol = new SimpleFillSymbol(SimpleFillSymbol.STYLE_SOLID,new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color("red"), 2),new Color([30,30,30, 0.5]));
      let graphic = new Graphic(circle, symbol);
      this.toolboxLayer.add(graphic);
    })
    
    

    下面是绘制圆的代码,绘制点的,只需要把circle替换成点就可以了。其他不变

    let point = (event as esri.AGSMouseEvent).mapPoint;
    let symbol = new PictureMarkerSymbol({
        "url":this.pinConfig.pic.url,//图片地址
        "height":20,
        "width":13,
        "type":"esriPMS",
    });
    let graphic = new Graphic(point, symbol);
    this.toolboxLayer.add(graphic);
    

    • 线的绘制:线的过程需要监听3个事件:点击(开始)、移动(过程)、双击(结束)
      -监听单击事件,绘制一个点,并开始绘制和记录之后的所有点击的坐标:
        /**
         * @param point 添加的点
         * @prop clickPoints 存储点的变量
         */
      addClickPoint(point:Point){//用于记录画线过程中的所有点坐标,用于后面的绘制多边形
            this.clickPoints.push(point);
            return this.clickPoints.length;
        }
        /**
         * 用于监听点击的方法。
         */
      drawPolylineClick(event: Event){
            let point  (event as esri.AGSMouseEvent).mapPoint;
            let len = this.addClickPoint(point);
            if(len===1){//当第一个点的时候,绘制
                let geometry = new Polyline(ToolUtils.buildCoordinates([point]));
                let graphic = new Graphic(geometry, new SimpleLineSymbol());
                this.toolboxLayer.add(graphic);
            }else{
                let gLen = this.toolboxLayer.graphics.length;
                let geo = ( this.toolboxLayer.graphics[gLen-1].geometry as Polyline).insertPoint(0, len-1, point)
                this.toolboxLayer.graphics[gLen-1].setGeometry(geo);
            }
        }
      /**
         * 在鼠标移动的时候,我们需要不断的监听并绘制线。并将过程的图形存放于drawEntity变量中
         */
        drawPolylineMouseMove(event: Event){
            let point = (event as esri.AGSMouseEvent).mapPoint;
            let len = this.clickPoints.length;
            this.drawEntity.layer.clear();
            if(len>0){
                let lastPt = this.clickPoints[len-1];
                let polyline = new Polyline(ToolUtils.buildCoordinates([lastPt,point]));
                let graphic = new Graphic(polyline, new SimpleLineSymbol());
                this.drawEntity.layer.add(graphic);//这个图层不是常驻图层,所以能直接全部清除,方便操作
            }
        }
    /*这个是辅助方法*/
      generateBufferGraphic(geometry:Geometry,distance: number | number[],unit: string | number,){
            let geometryTmp =<Polygon>GeometryEngine.geodesicBuffer(geometry,distance,unit);
            this.BufferLineAnalyze(geometry,this.currentLayers);
            let graphic = new Graphic(geometryTmp, ToolUtils.getComFillSymbol());
            return graphic;
        }
       /**
         * 监听双击事件,已停止绘制
         */
        drawPolylineDblClick(event: Event){
            if(this.clickPoints.length < 1){
                this.$message.warning("请绘制2个以上的点")
                return;
            }
            let point = (event as esri.AGSMouseEvent).mapPoint;
            let len = this.addClickPoint(point);
            let graphics = this.toolboxLayer.graphics
            let gLen = graphics.length;
            let geo = ( graphics[gLen-1].geometry as Polyline).insertPoint(0, len-1, point)
            graphics[gLen-1].setGeometry(geo);
            this.endDrawPolyline();
            let graphic = this.generateBufferGraphic(graphics[gLen-1].geometry,this.BufferConfig.bufferDistance,this.BufferConfig.unit);
            this.BufferLineAnalyze(graphic.geometry,this.currentLayers);
            this.toolboxLayer.remove(graphics[gLen-1])
            this.toolboxLayer.add(graphic);
        }
    /**
         * 将上面的事件都添加进来
         */
        drawPolyline(){
            Utils.addEvents(this.viewDiv,"click",this.drawPolylineClick)
            Utils.addEvents(this.viewDiv,"mousemove",this.drawPolylineMouseMove)
            Utils.addEvents(this.viewDiv,"dblclick",this.drawPolylineDblClick)
        }
        /**
         * 双击绘制结束,将所有事件移除
         */
        endDrawPolyline(){
            this.drawEntity.layer.clear();
            Utils.removeEvents(this.viewDiv,"click",this.drawPolylineClick)
            Utils.removeEvents(this.viewDiv,"mousemove",this.drawPolylineMouseMove)
            Utils.removeEvents(this.viewDiv,"dblclick",this.drawPolylineDblClick)
            this.enableAllEvent();
        }
    

    • 面的绘制:线的过程需要监听3个事件:点击(开始)移动(过程,需要实时绘制首位相接的面和跟随鼠标移动的线)双击(结束,将所有的点绘制成一个面即可完成)
      /**
         * 监听面的点击。如果是第一个点则初始化面,之后的就直接在该面的基础上追加点
         */
        drawPolygonClick(event: Event){
            let point = (event as esri.AGSMouseEvent).mapPoint;
            let len = this.addClickPoint(point);
            if(len===1){//如果是第一个点则初始化面
                let geometry = new Polygon(ToolUtils.buildCoordinates([point]));
                let graphic = new Graphic(geometry, ToolUtils.getComFillSymbol());
                this.toolboxLayer.add(graphic);
            }else{//之后的就直接在该面的基础上追加点
                let graphics = this.toolboxLayer.graphics
                let gLen = graphics.length
                let geo = (this.toolboxLayer.graphics[gLen-1].geometry as Polygon).insertPoint(0, len-1, point)
                this.toolboxLayer.graphics[gLen-1].setGeometry(geo);
            }
        }
      /**
         * 监听绘制面过程中鼠标的移动。就是不断得更新线和面。不断得将前一个点和鼠标形成的线和面不断绘制
         */
        drawPolygonMouseMove(event: Event){
            let point = (event as esri.AGSMouseEvent).mapPoint;
            let len = this.clickPoints.length;
            this.drawEntity.layer.clear();
            if(len===1){//当只有一个点,那么就只绘制线
                let lastPt = this.clickPoints[0];
                let polyline = new Polyline(ToolUtils.buildCoordinates([lastPt,point]));
                let graphic = new Graphic(polyline, ToolUtils.getComFillSymbol());
                this.drawEntity.layer.add(graphic);
            }else if(len>1){//如果大于2个点,则开始绘制面
                let lastPt = this.clickPoints[len-1];
                let firstPt = this.clickPoints[0];
                let geometry = new Polygon(ToolUtils.buildCoordinates([firstPt,point,lastPt]));
                let graphic = new Graphic(geometry, ToolUtils.getComFillSymbol());
                this.drawEntity.layer.add(graphic);
            }
        }
    /**
         * 监听双击,结束绘制
         */
        drawPolygonDblClick(event: Event){
            if(this.clickPoints.length < 2){
                this.$message.warning("请绘制2个以上的点")
                return;
            }
            let point = (event as esri.AGSMouseEvent).mapPoint;
            let len = this.addClickPoint(point);
            let graphics = this.toolboxLayer.graphics
            let gLen = graphics.length
            let geo = (graphics[gLen-1].geometry as Polygon).insertPoint(0, len-1, point)
            graphics[gLen-1].setGeometry(geo);
            this.endDrawPolygon();
        }
    /**
         * 开始绘制面,将所有的事件添加
         */
        drawPolygon(){
            let lay = AllEsriMap.findLayer(this.drawEntity.layer.id);
            Utils.addEvents(this.viewDiv,"click",this.drawPolygonClick)
            Utils.addEvents(this.viewDiv,"mousemove",this.drawPolygonMouseMove)
            Utils.addEvents(this.viewDiv,"dblclick",this.drawPolygonDblClick)
        }
        /**
         * 结束绘制面,将所有的事件移除
         */
        endDrawPolygon(){
            this.drawEntity.layer.clear();
            this.clickPoints = [];
            Utils.removeEvents(this.viewDiv,"click",this.drawPolygonClick)
            Utils.removeEvents(this.viewDiv,"mousemove",this.drawPolygonMouseMove)
            Utils.removeEvents(this.viewDiv,"dblclick",this.drawPolygonDblClick)
            this.enableAllEvent();
        }
    

    总结

    以上,就是实现的过程。一开始,我还是使用arcgis自带的针对map的点击事件,发现切换底图(地形图,影像图)会出现不能绘制的问题。这个纳闷了很久。所以查了下,监听原生的dom也是可以的。其实所有的都是为了缓冲区分析服务的。自己写工具,可以兼容所有的图层。针对不同的图层(要素图层,自己绘制的图层)都可以进行缓冲区分析。下次有空再写吧

    相关文章

      网友评论

          本文标题:基于事件实现Arcgis绘制功能

          本文链接:https://www.haomeiwen.com/subject/cbiuhqtx.html