arcgis js 范围选择

245 阅读5分钟

需要用到的类

require([
      "esri/Map",
      "esri/views/MapView",
      "esri/layers/FeatureLayer",
      "esri/Graphic",
      "esri/widgets/Editor",
      "esri/layers/GraphicsLayer",
      "esri/geometry/support/webMercatorUtils"
    ], function (Map, MapView, FeatureLayer, Graphic, Editor, GraphicsLayer, webMercatorUtils) {
    //这里面编写代码
    })

①创建一个地图map

let map = new Map({
        basemap: "streets-navigation-vector"; //地图的底图
      });

②创建一个地图视图

      let view = new MapView({
        container: "viewDiv", //容器,可以认为是放置地图的地方
        map: map, //地图
        center: [-80, 35],
        zoom: 3
      });

③先创建一个图形要素(一个三角形)

// 创建一个图形
      let polygon = {
        type: "polygon", // autocasts as new Polygon()
        rings: [
          [-64.78, 32.3],
          [-66.07, 18.45],
          [-80.21, 25.78],
          [-64.78, 32.3]
        ]
      };

      // Create a symbol for rendering the graphic
      let fillSymbol = {
        type: "simple-fill", // autocasts as new SimpleFillSymbol()
        color: [227, 139, 79, 0.8],
        outline: { // autocasts as new SimpleLineSymbol()
          color: [255, 255, 255],
          width: 1
        }
      };

      // Add the geometry and symbol to a new graphic
      let polygonGraphic = new Graphic({
        geometry: polygon,
        symbol: fillSymbol,
        attributes: {
          state: "hi~"
        }
      });

④现在我们再创建一个graphicsLayer图层和graphicsLayer_select图层,并且把此图层添加到地图上。

// 创建一个只有graphic要素的FeatureLayer图层
let graphicsLayer = new GraphicsLayer({ id: 'hello' })
let graphicsLayer_select = new GraphicsLayer({ id: 'select' })
// 将图层添加到地图上
map.add(graphicsLayer);
map.add(graphicsLayer_select);
//将前面我们创建的三角形要素添加到graphicsLayer图层上
graphicsLayer.add(polygonGraphic)

此时地图上已经有一个图形要素了

屏幕截图1.png

⑤接下来实现长按鼠标选取范围

      let select_flag = false; //用来判断当前是否处于范围选取状态
      let tmpGraphic = null; //
      let origin = null;  //存储最开始的点位

      view.on('drag', e => {
        if (!select_flag) return //如果不是处于范围选取状态,则退出
        if (e.action === 'start') { //开始拖拽
          remove_border() //去掉要素的红边框
          view.hitTest(e).then(resp => {
            e.stopPropagation(); //阻止默认行为
            origin = view.toMap(e); //存储最开始的点位
          })
        } else if (e.action === 'update') { //拖拽过程中
          e.stopPropagation(); //阻止默认行为
          if (!origin) return //如果没有最开始点位则,跳出
          if (tmpGraphic !== null) { //判断最新的要素是否存在
            graphicsLayer_select.remove(tmpGraphic) //删除最新的要素
            tmpGraphic = null
          }
          tmpGraphic = new Graphic({ //新建最新的要素
            symbol: {
              type: "simple-fill",
              color: 'rgba(102, 102, 102, .1)',
              outline: {
                color: [255, 255, 255],
                width: 1,
              },
            },
            geometry: {
              type: "polygon",
              rings: generateQuadrilateral([origin.x, origin.y], [view.toMap(e).x, view.toMap(e).y]) //这个函数根据最开始点位和最后点位生成最新要素的各点坐标
            }
          })
          tmpGraphic.geometry = webMercatorUtils.webMercatorToGeographic(tmpGraphic.geometry);//转换坐标系
          graphicsLayer_select.add(tmpGraphic) //添加最新的要素到graphicsLayer_select图层上

        } else if (e.action === 'end') {
        //最后这里是获取被选取的要素,给这些要素添加一个红边框
          const graphics = graphicsLayer.graphics.items
          const select_graphics = graphics.filter(item => geometryEngine.intersects( //获取被选取的要素
            tmpGraphic.geometry,
            item.geometry
          ))
          select_graphics.forEach(item => { //生成新的带红边框的要素,且添加到graphicsLayer图层上
            const { geometry, symbol, attributes } = item
            graphicsLayer.add(new Graphic({
              geometry: geometry,
              symbol: {
                type: symbol.type, // autocasts as new SimpleFillSymbol()
                color: symbol.color,
                outline: { // autocasts as new SimpleLineSymbol()
                  color: 'red',
                  width: 1
                }
              },
              attributes,
            }))
            graphicsLayer.remove(item) //删除原来graphicsLayer图层上的原来的要素
          })
          selectedGraphic = null;
          tmpGraphic = null;
          origin = null;
          graphicsLayer_select.removeMany(graphicsLayer_select.graphics.items)
        }
      });
      //下面这个函数是生成面要素(范围选择要素)的各点坐标
      function generateQuadrilateral(startPoint, endPoint) {
        let point1 = startPoint;
        let point2 = [startPoint[0], endPoint[1]];
        let point3 = endPoint;
        let point4 = [endPoint[0], startPoint[1]];

        // 将四个点的坐标按顺序存储在数组中
        let quadrilateral = [point1, point2, point3, point4];

        return quadrilateral;
      }

      //去掉所有要素的红边框
      const remove_border = () => {
        const graphics = graphicsLayer.graphics.items
        const new_graphics = []
        for (let i = 0; i < graphics.length; i++) {
          let { geometry, symbol, attributes } = graphics[i]
          let graphic = new Graphic({
            geometry: geometry,
            symbol: {
              type: symbol.type,
              color: symbol.color,
              outline: {
                color: [255, 255, 255],
                width: 1
              }
            },
            attributes,
          })
          new_graphics.push(graphic)
        }
        graphicsLayer.removeMany(graphics)
        graphicsLayer.addMany(new_graphics)
      }

所有的代码

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
  <title>Creating a FeatureLayer with Graphics in ArcGIS JS 4</title>
  <link rel="stylesheet" href="https://js.arcgis.com/4.28/esri/themes/light/main.css">
  <script src="https://js.arcgis.com/4.28/"></script>
  <style>
    html,
    body,
    #viewDiv {
      padding: 0;
      margin: 0;
      height: 100%;
      width: 100%;
    }

    .container {
      position: absolute;
      top: 0;
      right: 0;
    }
    .add_container {
      position: absolute;
      top: 50px;
      right: 0;
    }
  </style>
  <script>
    require([
      "esri/Map",
      "esri/views/MapView",
      "esri/layers/FeatureLayer",
      "esri/Graphic",
      "esri/widgets/Editor",
      "esri/layers/GraphicsLayer",
      "esri/geometry/support/webMercatorUtils",
      "esri/geometry/geometryEngine"
    ], function (Map, MapView, FeatureLayer, Graphic, Editor, GraphicsLayer, webMercatorUtils, geometryEngine) {
      //弹出框
      let popup_template = {
        title: "属性",
        content: [
          {
            type: "fields",
            fieldInfos: [
              {
                fieldName: "座位号",
                editable: true, // 设置可编辑
              },
              {
                fieldName: "状态",
                editable: true, // 设置可编辑
              },
              {
                fieldName: "描述",
                editable: true, // 设置可编辑
              },
              {
                fieldName: "备注",
                editable: true, // 设置可编辑
              },
            ],
          },
        ],
      };
      // 创建一个地图
      let map = new Map({
        basemap: "streets-navigation-vector"
      });

      // 创建一个地图视图
      let view = new MapView({
        container: "viewDiv",
        map: map,
        center: [-80, 35],
        zoom: 3
      });

      // 创建一个图形
      let polygon = {
        type: "polygon", // autocasts as new Polygon()
        rings: [
          [-64.78, 32.3],
          [-66.07, 18.45],
          [-80.21, 25.78],
          [-64.78, 32.3]
        ]
      };

      // Create a symbol for rendering the graphic
      let fillSymbol = {
        type: "simple-fill", // autocasts as new SimpleFillSymbol()
        color: [227, 139, 79, 0.8],
        outline: { // autocasts as new SimpleLineSymbol()
          color: [255, 255, 255],
          width: 1
        }
      };

      // Add the geometry and symbol to a new graphic
      let polygonGraphic = new Graphic({
        geometry: polygon,
        symbol: fillSymbol,
        attributes: {
          座位号: "hi~"
        }
      });

      // 创建一个只有graphic要素的FeatureLayer图层
      let graphicsLayer = new GraphicsLayer({ id: 'hello' })
      let graphicsLayer_select = new GraphicsLayer({ id: 'select' })
      // 将图层添加到地图中
      graphicsLayer.add(polygonGraphic)
      map.add(graphicsLayer);
      map.add(graphicsLayer_select);

      let select_flag = false;
      let tmpGraphic = null;
      let origin = null;

      view.on('drag', e => {
        if (!select_flag) return
        if (e.action === 'start') {
          remove_border()
          view.hitTest(e).then(resp => {
            e.stopPropagation();
            origin = view.toMap(e);
          })
        } else if (e.action === 'update') {
          e.stopPropagation();
          if (!origin) return
          if (tmpGraphic !== null) {
            graphicsLayer_select.remove(tmpGraphic)
            tmpGraphic = null
          }
          tmpGraphic = new Graphic({
            symbol: {
              type: "simple-fill",
              color: 'rgba(102, 102, 102, .1)',
              outline: {
                color: [255, 255, 255],
                width: 1,
              },
            },
            geometry: {
              type: "polygon",
              rings: generateQuadrilateral([origin.x, origin.y], [view.toMap(e).x, view.toMap(e).y])
            }
          })
          tmpGraphic.geometry = webMercatorUtils.webMercatorToGeographic(tmpGraphic.geometry);
          graphicsLayer_select.add(tmpGraphic)

        } else if (e.action === 'end') {
          const graphics = graphicsLayer.graphics.items
          const select_graphics = graphics.filter(item => geometryEngine.intersects(
            tmpGraphic.geometry,
            item.geometry
          ))
          select_graphics.forEach(item => {
            const { geometry, symbol, attributes } = item
            graphicsLayer.add(new Graphic({
              geometry: geometry,
              symbol: {
                type: symbol.type, // autocasts as new SimpleFillSymbol()
                color: symbol.color,
                outline: { // autocasts as new SimpleLineSymbol()
                  color: 'red',
                  width: 1
                }
              },
              attributes,
            }))
            graphicsLayer.remove(item)
          })
          selectedGraphic = null;
          tmpGraphic = null;
          origin = null;
          graphicsLayer_select.removeMany(graphicsLayer_select.graphics.items)
        }
      });
      //下面这个函数是生成面要素(范围选择要素)的各点坐标
      function generateQuadrilateral(startPoint, endPoint) {
        let point1 = startPoint;
        let point2 = [startPoint[0], endPoint[1]];
        let point3 = endPoint;
        let point4 = [endPoint[0], startPoint[1]];

        // 将四个点的坐标按顺序存储在数组中
        let quadrilateral = [point1, point2, point3, point4];

        return quadrilateral;
      }

      let add_graphic_flag = false;
      //监听视图的点击事件
      view.on('click', (e) => {
        if (!add_graphic_flag) return
        view.hitTest(e).then(res => {
          const new_graphic = new Graphic({ //新建一个要素,这个要素也是一个面要素,不过是一个正方形
            symbol: {
              type: "simple-fill",
              color: 'rgba(102, 102, 102, .1)',
              outline: {
                color: [255, 255, 255],
                width: 1,
              },
            },
            geometry: {
              type: "polygon",
              rings: [
                [-80.21, 18.45],
                [-80.21, 32.3],
                [-64.78, 32.3],
                [-64.78, 18.45],
              ]
            },
            attributes: {
              name: "点击添加"
            }
          })

          if (new_graphic.geometry.spatialReference.isWGS84) { //转换坐标系
            new_graphic.geometry = webMercatorUtils.geographicToWebMercator(new_graphic.geometry);
          }
          translatePolygon(new_graphic.geometry.centroid, view.toMap(e), new_graphic.geometry) //移动要素new_graphic.geometry.centroid就是当前要素的中心位置

          if (new_graphic.geometry.spatialReference.isWebMercator) { //转换回坐标系
            new_graphic.geometry = webMercatorUtils.webMercatorToGeographic(new_graphic.geometry);
          }
          graphicsLayer.add(new_graphic)
        })
      })


      function translatePolygon(startPoint, currPoint, polygon) {
        let dx = currPoint.x - startPoint.x;
        let dy = currPoint.y - startPoint.y;
        polygon.rings.forEach(ring => {
          if (Array.isArray(ring[0])) {
            ring.forEach(coord => {
              coord[0] += dx;
              coord[1] += dy;
            });
          } else {
            ring[0] += dx;
            ring[1] += dy;
          }
        });
      }

      window.addEventListener('load', () => {
        document.getElementsByClassName('container')[0].addEventListener('click', (e) => {
          if (e.target.style.color === 'orange') {
            e.target.style.color = 'black'
            select_flag = false

            remove_border()
          }
          else {
            e.target.style.color = 'orange'
            select_flag = true
          }
        })
        document.getElementsByClassName('add_container')[0].addEventListener('click', (e) => {
          if (e.target.style.color === 'orange') {
            e.target.style.color = 'black'
            add_graphic_flag = false
          }
          else {
            e.target.style.color = 'orange'
            add_graphic_flag = true
          }
        })
      })

      //去掉所有要素的红边框
      const remove_border = () => {
        const graphics = graphicsLayer.graphics.items
        const new_graphics = []
        for (let i = 0; i < graphics.length; i++) {
          let { geometry, symbol, attributes } = graphics[i]
          let graphic = new Graphic({
            geometry: geometry,
            symbol: {
              type: symbol.type,
              color: symbol.color,
              outline: {
                color: [255, 255, 255],
                width: 1
              }
            },
            attributes,
          })
          new_graphics.push(graphic)
        }
        graphicsLayer.removeMany(graphics)
        graphicsLayer.addMany(new_graphics)
      }

    });
  </script>
</head>

<body>
  <div id="viewDiv"></div>
  <div class="container">
    <button>选择</button>
  </div>
  <div class="add_container">
    <button>添加要素</button>
  </div>
</body>

</html>

结语

不想做前端牛马了~~~