前言
地图标绘是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>
最终呈现效果