高德地图2.0 绘制、编辑多边形覆盖物(电子围栏)

1. 安装
npm i @amap/amap-jsapi-loader --save
移步:官方文档
2. map组件封装
<script lang="ts" setup>
import AMapLoader from '@amap/amap-jsapi-loader'
import { onMounted, ref } from 'vue'
import { propTypes } from '@/utils/propTypes'defineOptions({ name: 'Map' })const props = defineProps({bindId: propTypes.string.def('mapContainer'),modelValue: propTypes.array.def([]),title: propTypes.string.def(''),width: propTypes.string.def('100%'),height: propTypes.string.def('600px'),polygonPaths: propTypes.array.def([]), // 回显多边形路径 得是<nubmer[]>类型,不然无法编辑districtCode: propTypes.string.def('140101') // 行政区划代码
})watch(() => props.districtCode,(newVal, oldVal) => {if (newVal !== oldVal) {drawBounds()}}
)window._AMapSecurityConfig = {securityJsCode: '4e6ca573a89ac3176f29813d3fcc895e'
}
const mouseTool = ref()
const mapRef = ref(null)
const overlays = ref<object[]>([])
const polyEditor = ref<any>()
const district = ref<any>()
const AMapObj = ref<any>()// 新建
const createPolygon = () => {polyEditor.value.close()polyEditor.value.setTarget()polyEditor.value.open()
}const emits = defineEmits(['update:modelValue'])
//获取
const getPolygon = () => {let overlays = mapRef.value?.getAllOverlays('polygon')let polygonPaths = overlays.map((overlay: any) => overlay.getPath())polygonPaths.shift() // 去掉第一个多边形console.log('🚀 ~ getPolygon ~ polygonPaths:', polygonPaths)const paths: object[] = []polygonPaths.forEach((item: any) => {const pathItem = item.map((cItem: any) => {const lnglat = `${cItem.lng},${cItem.lat}`return lnglat})paths.push(pathItem)})emits('update:modelValue', paths)
}defineExpose({ getPolygon })//清除绘制的多边形
const clearPolygon = () => {try {closeEditor()mapRef.value?.clearMap()drawBounds()} catch (e) {console.log(e)}
}// 开启编辑
const openEditor = () => {if (polyEditor.value) {polyEditor.value.open()}
}// 关闭编辑
const closeEditor = () => {if (polyEditor.value) {polyEditor.value.close()}
}//加载行政区划插件
const drawBounds = () => {if (!district.value) {//实例化DistrictSearchvar opts = {subdistrict: 0, //获取边界不需要返回下级行政区extensions: 'all', //返回行政区边界坐标组等具体信息level: 'district' //查询行政级别为 市}district.value = new AMap.DistrictSearch(opts)}//行政区查询district.value.setLevel('district')var keyword = props.districtCodeif (keyword === '') {console.warn('行政区划不能为空')return}let polygon = nulldistrict.value.search(keyword, function (status, result) {if (polygon) {mapRef.value?.remove(polygon) //清除上次结果polygon = null}if (!result || !result.districtList || !result.districtList[0]) {console.warn('请正确填写名称或更新其他名称')return}var bounds = result.districtList[0].boundariesif (bounds) {//生成行政区划polygonfor (var i = 0; i < bounds.length; i += 1) {//构造MultiPolygon的pathbounds[i] = [bounds[i]]}polygon = new AMap.Polygon({path: bounds,strokeColor: '#F56C6C',strokeWeight: 4,fillOpacity: 0.1,fillColor: '#F56C6C',strokeStyle: 'dashed',strokeDasharray: [12, 3],zIndex: 0})mapRef.value?.add(polygon)mapRef.value?.setFitView(polygon) //视口自适应}})
}// 编辑事件处理
const editPolygon = () => {polyEditor.value.on('add', function (data) {var polygon = data.targetpolyEditor.value.addAdsorbPolygons(polygon)polygon.on('dblclick', (e) => {polyEditor.value.setTarget(polygon)polyEditor.value.open()})})// polyEditor.value.on('adjust', function (data) {// console.log('🚀 ~ polyEditor-adjust', data)// })// polyEditor.value.on('addnode', function (data) {// console.log('🚀 ~ polyEditor-addnode', data)// })// polyEditor.value.on('end', function (data) {// console.log('🚀 ~ polyEditor-end', data)// })
}// 回显多边形
const showPolygon = (polygonArr: any) => {// const polygonArr = [[116.368904, 39.913423], [116.382122, 39.901176],[116.387271, 39.912501],[116.398258, 39.9046]]const polygon = new AMap.Polygon({path: polygonArr,strokeColor: '#1791fc',strokeOpacity: 0.9,strokeWeight: 4,fillColor: '#1791fc',fillOpacity: 0.3,strokeStyle: 'solid', //solid, 线样式还支持 'dashed',strokeDasharray: [12, 4], // strokeStyle是dashed时有效zIndex: 16})// polygon.on('click', (e) => {// console.log('🚀 ~ polygon.on ~ e:', e)// })mapRef.value?.add(polygon)polyEditor.value.addAdsorbPolygons([polygon])polygon.on('dblclick', () => {polyEditor.value.setTarget(polygon)polyEditor.value.open()})mapRef.value?.setFitView(polygon) //视口自适应
}// defineExpose({ showPolygon })// 加载高德地图
const loader = () => {AMapLoader.load({key: '947ec8e0b6869f9ef9fc6badda641a06',version: '2.0', // 使用合适的版本plugins: ['AMap.Scale', 'AMap.MouseTool', 'AMap.PolygonEditor', 'AMap.DistrictSearch']}).then((AMap) => {AMapObj.value = AMap// 初始化地图mapRef.value = new AMap.Map(props.bindId, {zoom: 15,center: [116.397428, 39.90923] // 设置地图中心点})// 加载行政区划插件drawBounds()// 编辑事件处理polyEditor.value = new AMap.PolygonEditor(mapRef.value)editPolygon()// 回显保存的多边形props.polygonPaths.forEach((item: any) => {showPolygon(item)})mapRef.value?.on('click', (e) => {console.log('🚀 ~ mapRef.value.on ~ e:', e)})}).catch((error) => {console.error('加载高德地图失败', error)})
}onMounted(() => {loader()
})
</script><template><div class="mb-14px"><el-button @click="createPolygon" type="primary">新建</el-button><!-- <el-button @click="openEditor" type="primary">编辑</el-button> --><el-button @click="closeEditor" type="primary">关闭编辑</el-button><el-button @click="clearPolygon" type="danger" plain>清空</el-button><!-- <el-button @click="getPolygon" type="primary">获取</el-button> --></div><div :id="props.bindId" :style="{ width: width, height: height }"></div>
</template>
3. 使用组件
<template><el-drawer v-model="drawer2" :direction="direction" size="75%" :show-close="false"><template #header><div><divclass="w-full flex items-center justify-between border-b border-b-[var(--el-border-color)] border-b-solid pb14px"><div class="font-size-4">服务区域</div><el-button type="primary" @click="close" size="small" circle><Icon icon="ep:close-bold" :size="16" @click="cancelClick" /></el-button></div></div></template><template #default><div class="flex items-center justify-center pb30px font-size-15px color-#666">起始地: {{ curRow.startDistrictName }}<el-icon class="mx-20px"><Switch /></el-icon>目的地: {{ curRow.endDistrictName }}</div><div class="flex justify-center"><div class="mx-10px w-50%"><div><MapbindId="startMap"v-model:modelValue="startPolygonPaths"v-if="drawer2"ref="StartMapRef":polygonPaths="startDetailPaths":districtCode="curRow.startDistrictCode"/></div></div><div class="mx-10px w-50%"><div><MapbindId="endMap"v-model:modelValue="endPolygonPaths"v-if="drawer2"ref="EndMapRef":polygonPaths="endDetailPaths":districtCode="curRow.endDistrictCode"/></div></div></div></template><template #footer><div style="flex: auto"><el-button @click="cancelClick">取消</el-button><el-button type="primary" @click="confirmClick">确认</el-button></div></template></el-drawer>
</template><script lang="ts" setup>
import { Switch } from '@element-plus/icons-vue'
import { ref, nextTick } from 'vue'
import { Map } from '@/components/Map/index'
import { getLinesServiceAreaList, saveLinesServiceArea } from '@/api/routeManage/routeList/index.ts'
const drawer2 = ref(false)
const direction = ref('rtl')
const curRow = ref<object>({})
const dcistrictCode = ref('')
const startPolygonPaths = ref([])
const endPolygonPaths = ref([])
const startDetailPaths = ref([])
const endDetailPaths = ref([])
const message = useMessage()// 打开推窗
const open = async (row) => {curRow.value = rowawait getDetail()drawer2.value = true
}
defineExpose({ open })const StartMapRef = ref(null)
const EndMapRef = ref(null)
const getDetail = async () => {const params = {...}try {const data = await getLinesServiceAreaList(params)...startDetailPaths.value =[[112.557711,37.731122],[112.625256,37.734871],[112.558896,37.680496],[112.558896,37.680496]]endDetailPaths.value = [[112.676067,36.378644],[112.79624,36.385946],[112.732752,36.29097],[112.732752,36.29097]]} finally {}
}// 获取起点数据
const getStartData = () => {const linesServiceAreaList = startPolygonPaths.value.map((item) => {if (item) {return { areaType: 1, pointList: item }}})return linesServiceAreaList || []
}// 获取终点数据
const getEndData = () => {const linesServiceAreaList = endPolygonPaths.value.map((item) => {if (item) {return { areaType: 1, pointList: item }}})return linesServiceAreaList || []
}const confirmClick = async () => {await StartMapRef.value?.getPolygon()await EndMapRef.value?.getPolygon()const params = {areaList: [...getStartData(), ...getEndData()]}try {await saveLinesServiceArea(params)message.success('保存成功')} finally {drawer2.value = false}
}function cancelClick() {drawer2.value = false
}
</script>相关文章:
高德地图2.0 绘制、编辑多边形覆盖物(电子围栏)
1. 安装 npm i amap/amap-jsapi-loader --save移步:官方文档 2. map组件封装 <script lang"ts" setup> import AMapLoader from amap/amap-jsapi-loader import { onMounted, ref } from vue import { propTypes } from /utils/propTypesdefineO…...
MySQL底层为什么选择用B+树作为索引
首先,我们来想想为什么这么多数据结构,为什么要用树这种数据结构? 众多的数据结构在逻辑层面可分为:线性结构 和 非线性结构。 线性结构有:数组、链表,基于它们衍生出的有哈希表(哈希表也称散…...
MATLAB系列05:自定义函数
MATLAB系列05:自定义函数 5. 自定义函数5.1 MATLAB函数简介5.2 在MATLAB中传递变量:按值传递机制5.3 选择性参数5.4 用全局内存分享数据5.5 在函数两次调用之间本地数据的存储5.6 函数的函数(function functions)5.7 子函数和私有函数5.8 总结 5. 自定义…...
C++速通LeetCode简单第20题-多数元素
方法一:暴力解法,放multiset中排序,然后依次count统计,不满足条件的值erase清除。 class Solution { public:int majorityElement(vector<int>& nums) {int ans 0;multiset<int> s;for(int i 0;i < nums.s…...
回收站永久删除的文件还能恢复吗?教你恢复技巧
在数字时代,电脑是我们工作、学习和娱乐的重要工具。然而,随着我们对电脑的频繁使用,误删文件的情况也时有发生。当我们在回收站中不小心永久删除了某个重要文件时,内心可能会充满焦虑和疑惑:这些文件还能恢复吗&#…...
Python Web 微服务架构全面解析与实战指南
Python Web 微服务架构全面解析与实战指南 目录 🏗️ 微服务基础概念 微服务架构与单体架构的对比微服务的优点与挑战 🔄 服务间通信 使用REST、gRPC或消息队列实现服务通信API网关的使用(如Kong、Traefik) 🔍 服务…...
SEAFARING靶场漏洞攻略
寻找漏洞 一,我们打开页面 第一个漏洞 xss漏洞 1.在登录页面显示有弹窗 第二个漏洞 sql注入漏洞 1.在输入框的地方输入-1 union select 1,2,3#我们来查看他的回显点 2.查看数据库表名 -1 union select 1,database(),3# 3.查看表名 -1 union select 1,2,group…...
ROS 编程入门的介绍
2.1 创建 ROS 功能包 ROS(Robot Operating System)是一种开源的机器人软件框架,广泛用于机器人开发中。通过使用 ROS,开发者可以轻松创建和管理机器人应用程序。在本节中,我们将介绍如何创建一个 ROS 功能包并实现一些…...
第十一章 抽象类与接口
一、抽象类和抽象方法 抽象类:使用abstract修饰的类 抽象方法:在类中没有方法体的方法,称为抽象方法,抽象方法用abstract修饰 抽象类中可以没有抽象方法,包含抽象方法的类必是抽象类 如果子类没有实现父类中的全部…...
请问企业的八大金刚系统是哪些?有什么共同点和区别?
我的理解的八大金刚包括:MES、ERP、WMS、OMS、CRM、SCM、SRM、PLM。 这些系统的主要功能及运用领域是哪些方面?他们互相之前有什么区别?选择时哪些是企业可能根据自身需求选择的必选项目或可选项目? 由于某些系统的必选性取决于企业的具体业…...
【入门】配置 Java 应用程序的完整指南
前言: Java 是一种广泛使用的编程语言,具备跨平台的特性,使得其应用程序可以在多种环境中高效运行。本文将介绍如何将 Java 应用程序从开发环境部署到生产环境,确保其能够稳定、稳定地运行运行。 确定运行环境 Java程序可以运行…...
flutter widget 设置GestureDetector点击无效
有可能是被上层的widget挡住了,虽然你看得到这个widget,但是操作不到。使用相对布局Stack要特别注意,这种布局会和Android一样,先写的布局放在下层,后写的,如果范围较大的话,会盖在之前的widget…...
基于SpringBoot的在线教育平台的设计与实现
文未可获取一份本项目的java源码和数据库参考。 选题的背景与意义: 随着互联网时代信息技术的不断发展,线下已经产生了很多IT技术的培训机构,但是价格却十分昂贵并且需要人们持续不断的去具体培训地点学习,因此更需要一个课程优…...
Django_Vue3_ElementUI_Release_004_使用nginx部署
1. nginx安装配置 1.1 下载nginx Download nginx 1.2 测试一下 1.3 进入nginx用命令操作 2. 部署 2.1 前端部署 2.1.1 修改nginx监听配置 …conf/nginx.conf http {... # 这里不进行修改server {listen 8010; # 监听 80 端口server_name 192.168.10.24; # 输入服务器 ip…...
Java抽象类的案例
抽象类的特点总结 不能实例化:抽象类不能直接创建实例。它只能被继承。即,你不能用 new 关键字创建抽象类的对象。 可以包含抽象方法:抽象类可以包含一个或多个抽象方法(没有方法体),这些方法必须在子类中…...
运维工程师面试整理-数据库
在运维工程师的面试中,数据库管理和优化是一个非常重要的环节。面试官可能会通过数据库相关的问题来评估你在数据库部署、管理、备份、性能优化以及故障排除方面的能力。以下是关于数据库部分的详细内容,帮助你更好地准备面试。 1. 数据库基础 ● 常见数据库类型 ○ 关系型数…...
comfyui一键抠图工作流:让你告别PS!
前言 本文涉及的工作流和插件,需要的朋友请扫描免费获取哦~ 在当今的数字时代,图像处理已经成为许多行业的日常需求。无论是电商产品展示、广告设计,还是个人照片编辑,去除背景都是一个常见且重要的步骤。 然而,使用…...
【Hot100】LeetCode—4. 寻找两个正序数组的中位数
目录 1- 思路题目识别二分 2- 实现⭐4. 寻找两个正序数组的中位数——题解思路 3- ACM 实现 原题链接:4. 寻找两个正序数组的中位数 1- 思路 题目识别 识别1 :给定两个数组 nums1 和 nums2 ,找出数组的中位数 二分 思路 将寻找中位数 —…...
【LLM text2sql】浅看大模型用于text2sql的综述
前言 之前笔者分享了text2sql & LLM & KG的有机结合实现KBQA的问答, 《【LLM & RAG & text2sql】大模型在知识图谱问答上的核心算法详细思路及实践》、 《【开源分享】KBQA核心技术及结合大模型SPARQL查询生成问答实践》。 我们再来看看大模型在te…...
Node js介绍
目录 概要**对Node的认识****Node的概念理解****Node和浏览器区别****Node的架构图** **Node的应用场景****Node的安装****安装Node的LTS版本****Node的版本管理工具nvm(了解)** **Node的输入和输出**Node程序传递参数Node的输出 **Node的全局对象****特殊的全局对象****其他的…...
后进先出(LIFO)详解
LIFO 是 Last In, First Out 的缩写,中文译为后进先出。这是一种数据结构的工作原则,类似于一摞盘子或一叠书本: 最后放进去的元素最先出来 -想象往筒状容器里放盘子: (1)你放进的最后一个盘子(…...
MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例
一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...
通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...
从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路
进入2025年以来,尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断,但全球市场热度依然高涨,入局者持续增加。 以国内市场为例,天眼查专业版数据显示,截至5月底,我国现存在业、存续状态的机器人相关企…...
oracle与MySQL数据库之间数据同步的技术要点
Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异,它们的数据同步要求既要保持数据的准确性和一致性,又要处理好性能问题。以下是一些主要的技术要点: 数据结构差异 数据类型差异ÿ…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...
JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作
一、上下文切换 即使单核CPU也可以进行多线程执行代码,CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短,所以CPU会不断地切换线程执行,从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...
安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖
在Vuzix M400 AR智能眼镜的助力下,卢森堡罗伯特舒曼医院(the Robert Schuman Hospitals, HRS)凭借在无菌制剂生产流程中引入增强现实技术(AR)创新项目,荣获了2024年6月7日由卢森堡医院药剂师协会࿰…...
Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
