从零开始的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>

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

你可以使用 Vue 3 和 ArcGIS API for JavaScript 4 来打印地图。下面是一个简单的示例代码,展示了如何在 Vue 3 中使用 ArcGIS API for JavaScript 4 来打印地图: 首先,在你的 Vue 3 项目中安装 ArcGIS API for JavaScript 4 的依赖: ```bash npm install --save @arcgis/core ``` 然后,在你的 Vue 组件中引入 ArcGIS API for JavaScript 4 的模块: ```javascript import { loadModules } from '@esri/arcgis-rest-loader'; export default { name: 'MapPrint', data() { return { map: null }; }, mounted() { this.initMap(); }, methods: { async initMap() { // 加载 ArcGIS API for JavaScript 4 的模块 const [Map, MapView] = await loadModules(['esri/Map', 'esri/views/MapView']); // 创建地图实例 this.map = new Map({ basemap: 'streets' }); // 创建地图视图 const mapView = new MapView({ container: this.$refs.mapViewContainer, map: this.map, zoom: 12, center: [-118.2437, 34.0522] // 设置初始地图中心点坐标 }); }, printMap() { // 打印地图 this.$refs.mapViewContainer.print(); } } }; ``` 在上面的代码中,我们创建了一个名为 `MapPrint` 的 Vue 组件,并通过 `loadModules` 方法加载了 `esri/Map` 和 `esri/views/MapView` 模块。然后,我们在 `mounted` 钩子函数中初始化了地图,并在 `printMap` 方法中调用了 `print` 方法来实现地图的打印功能。 最后,在你的 Vue 组件模板中添加一个地图容器和一个按钮来触发打印地图的操作: ```html <template> <div> <div ref="mapViewContainer" style="width: 100%; height: 400px;"></div> <button @click="printMap">Print Map</button> </div> </template> ``` 这样,当你在浏览器中打开该 Vue 组件页面时,你将看到一个包含地图的容器和一个打印地图的按钮。点击按钮后,地图将被打印出来。 希望这个示例能对你有所帮助!如果你还有其他问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值