从零开始的VUE+ArcGIS API for JS项目——地图标绘(线)

前言

地图标绘是WebGIS应用的基础应用之一,使用地图标绘共功能,主要是实现在地图上绘制点,线,面等图形,对地图上的重点区域进行标记。绘制过程中可能涉及到的需求包括可以自定义点线面的样式,颜色等,能够保存绘制的记录,能够定位绘制的图形等。
今天这篇文章,主要实现线图形的绘制,绘制完成后能够自动加入临时列表,通过列表可以删除,定位元素。

快速链接
arpgis api地址
初始化地图应用
底图切换
图层管理

创建地图标绘工具类

继续之前的工程中编辑,在toolbox文件夹下新建DrawLine.vue文件。标绘工具实现三部分功能

绘制简单线

首先提供一个按钮实现点击按钮激活地图绘制的功能
 <el-button circle  @click="drawline"> <el-icon><Position /></el-icon></el-button>
引入ArcGIS绘制图形的相关方法
import Graphic from '@arcgis/core/Graphic';
import Draw from '@arcgis/core/views/draw/Draw';
import * as geometryEngine from '@arcgis/core/geometry/geometryEngine'
实现drawline 方法
const draw = new Draw({
  view: props.viewObj //从home文件传入
});
const drawline = () => {
  //清除之前绘制的要素
  props.viewObj.graphics.removeAll();

  // 创建并返回PolyLineDrawAction的实例
  const action = draw.create("polyline");
  // 聚焦视图以激活用于绘制草图的键盘快捷键
  props.viewObj.focus();
 
  // 监听polylineDrawAction事件,在视图上绘制线条时给用户即时的视觉反馈。
  action.on(
  [
      "vertex-add",
      "vertex-remove",
      "cursor-update",
      "redo",
      "undo"
  ],
  updateVertices,
  );
  //绘制完成时执行
  action.on("draw-complete",onAddItem);
};
绘制线段完整的实现方法
const updateVertices=(event)=> {
  // 根据创建的折点绘制线段
  if (event.vertices.length > 1) {
    const result = createGraphic(event);

    // 如果最后一个顶点使直线与自身相交,//阻止事件触发
    if (result.selfIntersects) {
      event.preventDefault();
    }
  }
}
//创建图形
const createGraphic=(event)=> {
  const vertices = event.vertices;
  props.viewObj.graphics.removeAll();

  //设置绘制的图形类型、样式
  const graphic = new Graphic({
    geometry: {
      type: "polyline",
      paths: vertices,
      spatialReference:  props.viewObj.spatialReference
    },
    symbol: {
      type: "simple-line", // autocasts as new SimpleFillSymbol
      color: [4, 90, 141],
      width: 4,
      cap: "round",
      join: "round"
    }
  });

  // 检查是否自相交
  const intersectingSegment = getIntersectingSegment(graphic.geometry);

  // 如果自相交则添加一个新的线段
  if (intersectingSegment) {
    props.viewObj.graphics.addMany([graphic, intersectingSegment]);
  }
  //添加线段
  else {
    props.viewObj.graphics.add(graphic);
  }
  // 返回自相交状态
  return {
    selfIntersects: intersectingSegment
  };
}
//检查线段是否自相交
const isSelfIntersecting=(polyline)=> {
  if (polyline.paths[0].length < 3) {
    return false;
  }
  const line = polyline.clone();

  //获取正在绘制的线段的最后一段
  const lastSegment = getLastSegment(polyline);
  line.removePoint(0, line.paths[0].length - 1);

  //如果线段自相交则返回ture
  return geometryEngine.crosses(lastSegment, line);
}
//设置自相交线段的符号样式
const getIntersectingSegment=(polyline)=> {
  if (isSelfIntersecting(polyline)) {
    return new Graphic({
      geometry: getLastSegment(polyline),
      symbol: {
        type: "simple-line", // autocasts as new SimpleLineSymbol
        style: "short-dot",
        width: 3.5,
        color: "yellow"
      }
    });
  }
  return null;
}
// 获取正在绘制的折线的最后一段
const getLastSegment=(polyline)=> {
  const line = polyline.clone();
  const lastXYPoint = line.removePoint(0, line.paths[0].length - 1);
  const existingLineFinalPoint = line.getPoint(
    0,
    line.paths[0].length - 1
  );
  return {
    type: "polyline",
    spatialReference:  props.viewObj.spatialReference,
    hasZ: false,
    paths: [
      [
        [existingLineFinalPoint.x, existingLineFinalPoint.y],
        [lastXYPoint.x, lastXYPoint.y]
      ]
    ]
  };
}
以上代码实现了在地图上绘制线段的目标,当线段绘制完成后,需要将线段添加的列表中,然后的前端页面中显示列表。
首先添加一个table组件,用来显示绘制的列表,同时添加定位和删除按钮
 <el-table :data="tableData" style="width: 100%;margin-top: 15px;" :show-header="false" >
      <el-table-column prop="name" label="名称" width="180" />
      <el-table-column label="操作" min-width="80">
        <template #default="scope">
          <el-button
            link
            type="primary"
            size="small"
            @click.prevent="onCountyClickHandler(scope.$index)"
          >
          <el-icon><Position /></el-icon>
          </el-button>
          <el-button
            link
            type="primary"
            size="small"
            @click.prevent="deleteRow(scope.$index)"
          >
          <el-icon><Delete /></el-icon>
          </el-button>
        </template>
      </el-table-column>
    </el-table>
在绘制完成时执行方法:
 action.on("draw-complete",onAddItem);
onAddItem 方法实现,下面方法中 props.graphicsLayer,由home传入,目的是创建一个graphicsLayer图形,将绘制的图形全部存储到graphicsLayer中,通过graphicsLayer图层来管理这些标绘要素。定义一个空的tableData,存储元素名称和索引,在页面中显示:const tableData = ref([])
//绘制完成后,添加到列表中
const onAddItem = () => {
  var graphic =props.viewObj.graphics.getItemAt(0)
  props.graphicsLayer.add(graphic)
  tableData.value.push({
    name: graphic.geometry.type
  })
  props.viewObj.graphics.removeAll();
}
实现要素的定位和删除功能,定位功能使用view的goto方法进行图形定位。删除功能是将要素从graphicsLayer和tableData中删除。其中的index为添加到tableData时自动创建的索引,由<Table>传入
//定位选中的绘制图形
const onCountyClickHandler=(index)=> {
  const result =props.graphicsLayer.graphics.getItemAt(index)
  if (result) {
    props.viewObj
      .goTo(result.geometry.extent.expand(0))
  }
}
//删除选中的绘制图形
const deleteRow=(index)=>{
  props.graphicsLayer.graphics.removeAt(index)
  tableData.value.splice(index, 1)
}
//删除全部绘制图形
const deleteall=()=>{
  props.graphicsLayer.graphics.removeAll();
  tableData.value=[]
}
完成工具类的编写后,将其添加到home,vue中,添加到<el-dialog>中,与之前的工具箱并列,通过v-if命令控制不同工具的显示隐藏。添加 showdialog 方法,安装按钮类型控制弹窗。
 <template>
  <!-- 地图视图区域 -->
  <div id="viewDiv" class="mapdiv"></div>
  <!-- 自定义工具 -->
  <div id="toolbar" style="box-shadow: #ffffff00;">
    <el-button id="basemap" plain @click="showdialog">
      打开底图窗口
    </el-button>
    <el-button id="layers" plain @click= "showdialog">
      打开图层列表
    </el-button>
    <el-button id="draw" plain @click= "showdialog">
      打开绘图
    </el-button>
  </div> <div class="my-dialog">
    <el-dialog  v-model="dialogFormVisible" title="标绘" width="300"
      :modal=false
      :close-on-click-modal=false
      @open="handleOverlay"
      draggable>
      <!-- 加载自定义的底图切换工具 -->
      <Basemaptool v-if="showbasemap" @changebasemap="changebasemap"></Basemaptool>
      <Layertree v-if="showlayers" :viewObj="view"></Layertree>
      <Drawline v-if="showdraw" :viewObj="view" :graphicsLayer="graphicsLayer"></Drawline>
    </el-dialog>
  </div>
</template>
..........................
let showdialog=(val)=>{

  if(val.currentTarget.id =="layers")
    {
      dialogFormVisible.value=true
      showbasemap.value=false
      showlayers.value=true
      showdraw.value=false
    }
    else if(val.currentTarget.id =="basemap")
    {
      dialogFormVisible.value=true
      showbasemap.value=true
      showlayers.value=false
      showdraw.value=false
    }
    else if(val.currentTarget.id =="draw")
    {
      dialogFormVisible.value=true
      showbasemap.value=false
      showlayers.value=false
      showdraw.value=true
    }
}

引入GraphicsLayer 类,在home引入是为了防止打开或关闭标绘工具的时候,GraphicsLayer图层会自动清空的情况。
import GraphicsLayer from '@arcgis/core/layers/GraphicsLayer';
//初始化图层
const   graphicsLayer = new GraphicsLayer({
    id:"0",
    visible:true
  })
const init=()=> {
  ...
  //添加到视图中
  view.map.add(graphicsLayer);
  ...
 }

附drawline完整代码:

<template>
  <el-row>
    <el-button circle  @click="drawline"> <el-icon><Position /></el-icon></el-button>
    <el-button circle> <el-icon><Position /></el-icon></el-button>
    <el-button circle> <el-icon><Position /></el-icon></el-button>
    <el-button circle> <el-icon><Position /></el-icon></el-button>
    <el-button type="info"  circle> <el-icon><Setting /></el-icon></el-button>
    <el-button type="info"  circle @click="deleteall"> <el-icon><DeleteFilled /></el-icon></el-button>
  </el-row>
  <el-row>
    <el-table :data="tableData" style="width: 100%;margin-top: 15px;" :show-header="false" >
      <el-table-column prop="name" label="名称" width="180" />
      <el-table-column label="操作" min-width="80">
        <template #default="scope">
          <el-button
            link
            type="primary"
            size="small"
            @click.prevent="onCountyClickHandler(scope.$index)"
          >
          <el-icon><Position /></el-icon>
          </el-button>
          <el-button
            link
            type="primary"
            size="small"
            @click.prevent="deleteRow(scope.$index)"
          >
          <el-icon><Delete /></el-icon>
          </el-button>
        </template>
      </el-table-column>
    </el-table>
  </el-row>
</template>

<script lang="ts" setup>
import {ref} from 'vue'
import Graphic from '@arcgis/core/Graphic';
import Draw from '@arcgis/core/views/draw/Draw';
import * as geometryEngine from '@arcgis/core/geometry/geometryEngine'

//接收主地图的视图view对象
const props = defineProps({
  viewObj: Object,
  graphicsLayer:Object
});

const draw = new Draw({
  view: props.viewObj
});

const tableData = ref([])

const drawline = () => {
  //清除之前绘制的要素
  props.viewObj.graphics.removeAll();

  // 创建并返回PolyLineDrawAction的实例
  const action = draw.create("polyline");
  // 聚焦视图以激活用于绘制草图的键盘快捷键
  props.viewObj.focus();
 
  // 监听polylineDrawAction事件,在视图上绘制线条时给用户即时的视觉反馈。
  action.on(
  [
      "vertex-add",
      "vertex-remove",
      "cursor-update",
      "redo",
      "undo"
  ],
  updateVertices,
  );
  //绘制完成时执行
  action.on("draw-complete",onAddItem);
};

const updateVertices=(event)=> {
  // 根据创建的折点绘制线段
  if (event.vertices.length > 1) {
    const result = createGraphic(event);

    // 如果最后一个顶点使直线与自身相交,//阻止事件触发
    if (result.selfIntersects) {
      event.preventDefault();
    }
  }
}
//创建图形
const createGraphic=(event)=> {
  const vertices = event.vertices;
  props.viewObj.graphics.removeAll();

  //设置绘制的图形类型、样式
  const graphic = new Graphic({
    geometry: {
      type: "polyline",
      paths: vertices,
      spatialReference:  props.viewObj.spatialReference
    },
    symbol: {
      type: "simple-line", // autocasts as new SimpleFillSymbol
      color: [4, 90, 141],
      width: 4,
      cap: "round",
      join: "round"
    }
  });

  // 检查是否自相交
  const intersectingSegment = getIntersectingSegment(graphic.geometry);

  // 如果自相交则添加一个新的线段
  if (intersectingSegment) {
    props.viewObj.graphics.addMany([graphic, intersectingSegment]);
  }
  //添加线段
  else {
    props.viewObj.graphics.add(graphic);
  }
  // 返回自相交状态
  return {
    selfIntersects: intersectingSegment
  };
}
//检查线段是否自相交
const isSelfIntersecting=(polyline)=> {
  if (polyline.paths[0].length < 3) {
    return false;
  }
  const line = polyline.clone();

  //获取正在绘制的线段的最后一段
  const lastSegment = getLastSegment(polyline);
  line.removePoint(0, line.paths[0].length - 1);

  //如果线段自相交则返回ture
  return geometryEngine.crosses(lastSegment, line);
}
//设置自相交线段的符号样式
const getIntersectingSegment=(polyline)=> {
  if (isSelfIntersecting(polyline)) {
    return new Graphic({
      geometry: getLastSegment(polyline),
      symbol: {
        type: "simple-line", // autocasts as new SimpleLineSymbol
        style: "short-dot",
        width: 3.5,
        color: "yellow"
      }
    });
  }
  return null;
}
// 获取正在绘制的折线的最后一段
const getLastSegment=(polyline)=> {
  const line = polyline.clone();
  const lastXYPoint = line.removePoint(0, line.paths[0].length - 1);
  const existingLineFinalPoint = line.getPoint(
    0,
    line.paths[0].length - 1
  );
  return {
    type: "polyline",
    spatialReference:  props.viewObj.spatialReference,
    hasZ: false,
    paths: [
      [
        [existingLineFinalPoint.x, existingLineFinalPoint.y],
        [lastXYPoint.x, lastXYPoint.y]
      ]
    ]
  };
}
//绘制完成后,添加到列表中
const onAddItem = () => {
  var graphic =props.viewObj.graphics.getItemAt(0)
  props.graphicsLayer.add(graphic)
  tableData.value.push({
    name: graphic.geometry.type
  })
  props.viewObj.graphics.removeAll();
}

//定位选中的绘制图形
const onCountyClickHandler=(index)=> {
  const result =props.graphicsLayer.graphics.getItemAt(index)
  if (result) {
    props.viewObj
      .goTo(result.geometry.extent.expand(0))
  }
}
//删除选中的绘制图形
const deleteRow=(index)=>{
  props.graphicsLayer.graphics.removeAt(index)
  tableData.value.splice(index, 1)
}
//删除全部绘制图形
const deleteall=()=>{
  props.graphicsLayer.graphics.removeAll();
  tableData.value=[]
}
</script>
<style>

</style>

最终呈现效果
在这里插入图片描述

好的,下面是一个简单的示例: 1. 在 Vue 项目安装 arcgis for js 和 axios: ``` npm install --save @arcgis/core axios ``` 2. 在需要使用查询功能的组件引入模块: ```javascript import * as esriLoader from "@arcgis/core"; // 引入 arcgis for js import axios from "axios"; // 引入 axios ``` 3. 在组件的 `mounted()` 生命周期初始化地图和查询任务: ```javascript mounted() { // 初始化地图 esriLoader.loadModules(["esri/Map", "esri/views/MapView"]).then(([Map, MapView]) => { const map = new Map({ basemap: "streets-navigation-vector", }); const view = new MapView({ container: "map-container", map: map, center: [-118.80500, 34.02700], zoom: 13, }); }); // 初始化查询任务 esriLoader.loadModules(["esri/tasks/QueryTask", "esri/tasks/support/Query"]).then(([QueryTask, Query]) => { this.queryTask = new QueryTask({ url: "https://sampleserver6.arcgisonline.com/arcgis/rest/services/Census/MapServer/3", // 查询图层的 URL }); this.query = new Query(); this.query.returnGeometry = false; // 不返回几何对象 this.query.outFields = ["*"]; // 返回所有字段 }); }, ``` 4. 在查询按钮的点击事件执行查询: ```javascript methods: { onQuery() { const whereClause = "STATE_NAME = 'California'"; // 查询条件 this.query.where = whereClause; this.queryTask.execute(this.query).then((result) => { const features = result.features; const data = features.map((feature) => { return feature.attributes; }); this.tableData = data; // 将查询结果赋值给表格数据 }); }, }, ``` 5. 在模板添加查询按钮和表格: ```html <div id="map-container"></div> <button @click="onQuery">查询</button> <table> <thead> <tr> <th v-for="field in fields">{{ field }}</th> </tr> </thead> <tbody> <tr v-for="row in tableData"> <td v-for="field in fields">{{ row[field] }}</td> </tr> </tbody> </table> ``` 6. 在 `data` 定义表格数据和字段列表: ```javascript data() { return { queryTask: null, query: null, tableData: [], fields: ["STATE_NAME", "POP2000", "POP2007"], }; }, ``` 这样就可以在 Vue 使用 arcgis for js 的查询功能,并将查询结果以表格式返回。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值