arcgis js 简单实现拖拽移动要素和点击添加要素

503 阅读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

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

②创建一个地图视图

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

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

// 创建一个图形
      const 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
      const 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
      const polygonGraphic = new Graphic({
        geometry: polygon,
        symbol: fillSymbol,
        attributes: {
          state: "hi~"
        }
      });

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

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

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

屏幕截图1.png

⑤接下来实现拖拽移动图形

//先看如下两个移动要素的函数
//我们常用的要素有三种点、线、面。
//下面这个函数是移动面要素的,前面我们的三角形就是一个面要素。
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;
          }
        });
      }
//下面这个函数是移动线要素的
function translatePolyline(startPoint, currPoint, polyline) {
        let dx = currPoint.x - startPoint.x;
        let dy = currPoint.y - startPoint.y;

        polyline.paths.forEach(path => {
          if (Array.isArray(path[0])) {
            path.forEach(coord => {
              coord[0] += dx;
              coord[1] += dy;
            });
          } else {
            path[0] += dx;
            path[1] += dy;
          }
        });
      }

//接下来监听视图的拖拽事件
view.on('drag', e => {
        //e.action是用来判断拖拽过程的状态的,开始拖拽(按下鼠标刚开始),正在拖拽(按下鼠标拖拽中),结束拖拽(松开鼠标,结束拖拽)
        if (e.action === 'start') {
          view.hitTest(e).then(resp => {
            if (resp.results.filter(item => item.graphic.geometry)[0]?.graphic) { //判断当前拖拽的是否是指定的要素
              e.stopPropagation(); //这个方法是阻止默认行为的
              selectedGraphic = resp.results.filter(item => item.graphic.geometry)[0]?.graphic;  //获取到当前要素
              // 判断是否是地理坐标系,转换坐标系
              if (selectedGraphic.geometry.spatialReference.isWGS84) {
                selectedGraphic.geometry = webMercatorUtils.geographicToWebMercator(selectedGraphic.geometry);
              }
              origin = view.toMap(e);   //获取当前鼠标位置,这个是一开始的位置
            }
          })
        } else if (e.action === 'update') {   //这个if里面是正在拖拽(按下鼠标拖拽中)的过程
          if (selectedGraphic) { //判断一开始拖拽获取的要素是否为空,不为空就要删掉这个要素,要不然拖拽后还有一个要素保留在原来的位置
            e.stopPropagation(); //这个方法是阻止默认行为的
            if (graphicsLayer.graphics.items.includes(selectedGraphic)) { //判断这个要素是否是在graphicsLayer图层上的
              graphicsLayer.remove(selectedGraphic); //删除要素
            }
            if (tmpGraphic) { //这个tmpGraphic其实就是拖拽过程中的每一个要素,每到一个最新的位置,就要把之前的要素删掉
              graphicsLayer.remove(tmpGraphic);
              tmpGraphic = null; 
            }
            tmpGraphic = selectedGraphic.clone(); //拷贝一个最初的位置的要素
            switch (tmpGraphic.geometry.type) { //这个switch就是判断当前要素是那种类型的要素,会执行对应的移动方法
              case "point":
                tmpGraphic.geometry = view.toMap(e);
                if (selectedGraphic.geometry.spatialReference.isWebMercator) {
                  tmpGraphic.geometry = webMercatorUtils.webMercatorToGeographic(tmpGraphic.geometry);
                }
                graphicsLayer.add(tmpGraphic);
                break;
              case "polygon":
                translatePolygon(origin, view.toMap(e), tmpGraphic.geometry); //开始执行移动方法传入参数(初始位置, 鼠标当前最新位置, 需要移动的要素的geometry)
                //移动完后我们需要把要素的坐标转回地理坐标
                if (selectedGraphic.geometry.spatialReference.isWebMercator) {
                  tmpGraphic.geometry = webMercatorUtils.webMercatorToGeographic(tmpGraphic.geometry);
                }
                graphicsLayer.add(tmpGraphic); //添加这个移动后的要素到图层上
                break;
              case "polyline":
                translatePolyline(origin, view.toMap(e), tmpGraphic.geometry);
                if (selectedGraphic.geometry.spatialReference.isWebMercator) {
                  tmpGraphic.geometry = webMercatorUtils.webMercatorToGeographic(tmpGraphic.geometry);
                }
                graphicsLayer.add(tmpGraphic);
                break;
              default:
                break;
            }
          }
        } else if (e.action === 'end') { //这里是移动的结束,就是鼠标松开的时候
          selectedGraphic = null;
          tmpGraphic = null;
          origin = null;
        }
      });

⑥下面我们实现在鼠标点击的位置上添加一个要素,其实跟上面的拖拽同理,都可以通过移动要素来实现。

//监听视图的点击事件
view.on('click', (e) => {
        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],
              ]
            }
          })
          
          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)
        })
      })

最后

完整代码如下

<!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%;
    }
  </style>
  <script>
    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) {
      //弹出框
      let popup_template = {
        title: "属性",
        content: [
          {
            type: "fields",
            fieldInfos: [
              {
                fieldName: "座位号",
                editable: true, // 设置可编辑
              },
              {
                fieldName: "状态",
                editable: true, // 设置可编辑
              },
              {
                fieldName: "描述",
                editable: true, // 设置可编辑
              },
              {
                fieldName: "备注",
                editable: true, // 设置可编辑
              },
            ],
          },
        ],
      };
      // 创建一个地图
      const map = new Map({
        basemap: "streets-navigation-vector"
      });

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

      // 创建一个图形
      const 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
      const 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
      const polygonGraphic = new Graphic({
        geometry: polygon,
        symbol: fillSymbol,
        attributes: {
          座位号: "hi~"
        }
      });

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

      let selectedGraphic = null;
      let tmpGraphic = null;
      let origin = null;

      view.on('drag', e => {
        if (e.action === 'start') {
          view.hitTest(e).then(resp => {
            if (resp.results.filter(item => item.graphic.geometry)[0]?.graphic) {
              e.stopPropagation();
              selectedGraphic = resp.results.filter(item => item.graphic.geometry)[0]?.graphic;
              // doing this with web mercator or the spatial ref of the basemap, we don't want to use
              // wgs84
              if (selectedGraphic.geometry.spatialReference.isWGS84) {
                selectedGraphic.geometry = webMercatorUtils.geographicToWebMercator(selectedGraphic.geometry);
              }
              origin = view.toMap(e);
            }
          })
        } else if (e.action === 'update') {
          if (selectedGraphic) {
            e.stopPropagation();
            if (graphicsLayer.graphics.items.includes(selectedGraphic)) {
              graphicsLayer.remove(selectedGraphic);
            }
            if (tmpGraphic) {
              graphicsLayer.remove(tmpGraphic);
              tmpGraphic = null;
            }
            tmpGraphic = selectedGraphic.clone();
            switch (tmpGraphic.geometry.type) {
              case "point":
                tmpGraphic.geometry = view.toMap(e);
                if (selectedGraphic.geometry.spatialReference.isWebMercator) {
                  tmpGraphic.geometry = webMercatorUtils.webMercatorToGeographic(tmpGraphic.geometry);
                }
                graphicsLayer.add(tmpGraphic);
                break;
              case "polygon":
                translatePolygon(origin, view.toMap(e), tmpGraphic.geometry);
                if (selectedGraphic.geometry.spatialReference.isWebMercator) {
                  tmpGraphic.geometry = webMercatorUtils.webMercatorToGeographic(tmpGraphic.geometry);
                }
                graphicsLayer.add(tmpGraphic);
                break;
              case "polyline":
                translatePolyline(origin, view.toMap(e), tmpGraphic.geometry);
                if (selectedGraphic.geometry.spatialReference.isWebMercator) {
                  tmpGraphic.geometry = webMercatorUtils.webMercatorToGeographic(tmpGraphic.geometry);
                }
                graphicsLayer.add(tmpGraphic);
                break;
              default:
                break;
            }
          }
        } else if (e.action === 'end') {
          selectedGraphic = null;
          tmpGraphic = null;
          origin = null;
        }
      });

      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;
          }
        });
      }

      function translatePolyline(startPoint, currPoint, polyline) {
        let dx = currPoint.x - startPoint.x;
        let dy = currPoint.y - startPoint.y;

        polyline.paths.forEach(path => {
          if (Array.isArray(path[0])) {
            path.forEach(coord => {
              coord[0] += dx;
              coord[1] += dy;
            });
          } else {
            path[0] += dx;
            path[1] += dy;
          }
        });
      }

      view.on('click', (e) => {
        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],
              ]
            }
          })
          
          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)

          if (new_graphic.geometry.spatialReference.isWebMercator) {
            new_graphic.geometry = webMercatorUtils.webMercatorToGeographic(new_graphic.geometry);
          }
          graphicsLayer.add(new_graphic)
        })
      })
    });
  </script>
</head>

<body>
  <div id="viewDiv"></div>
</body>

</html>

结语

不想做前端牛马了~~~