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

news2025/2/23 6:58:35

在这里插入图片描述

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) {
    //实例化DistrictSearch
    var opts = {
      subdistrict: 0, //获取边界不需要返回下级行政区
      extensions: 'all', //返回行政区边界坐标组等具体信息
      level: 'district' //查询行政级别为 市
    }
    district.value = new AMap.DistrictSearch(opts)
  }
  //行政区查询
  district.value.setLevel('district')
  var keyword = props.districtCode
  if (keyword === '') {
    console.warn('行政区划不能为空')
    return
  }
  let polygon = null
  district.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].boundaries
    if (bounds) {
      //生成行政区划polygon
      for (var i = 0; i < bounds.length; i += 1) {
        //构造MultiPolygon的path
        bounds[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.target
    polyEditor.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>
        <div
          class="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>
            <Map
              bindId="startMap"
              v-model:modelValue="startPolygonPaths"
              v-if="drawer2"
              ref="StartMapRef"
              :polygonPaths="startDetailPaths"
              :districtCode="curRow.startDistrictCode"
            />
          </div>
        </div>
        <div class="mx-10px w-50%">
          <div>
            <Map
              bindId="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 = row
  await 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>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2143498.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【自动驾驶】决策规划算法 | 数学基础(三)直角坐标与自然坐标转换Ⅱ

写在前面&#xff1a; &#x1f31f; 欢迎光临 清流君 的博客小天地&#xff0c;这里是我分享技术与心得的温馨角落。&#x1f4dd; 个人主页&#xff1a;清流君_CSDN博客&#xff0c;期待与您一同探索 移动机器人 领域的无限可能。 &#x1f50d; 本文系 清流君 原创之作&…

以root用户登陆ubuntu的桌面环境

前言 在学习Linux的时候&#xff0c;经常都需要使用sudo权限来对配置文件进行修改&#xff0c;常用的方法就是用vim编辑器在命令行界面进行修改&#xff0c;比如sudo vim /etc/profile&#xff0c;但我觉得每次都用命令行挺麻烦的&#xff0c;于是&#xff01;&#x1f913;我…

【零散技术】Odoo17通过Controller下载PDF

序言:时间是我们最宝贵的财富,珍惜手上的每个时分 Odoo作为一款开源ERP&#xff0c;拥有极佳的拓展性&#xff0c;Odoo的Controller框架也让它具备了作为微信小程序后端的能力&#xff0c;那么就存在 需要通过小程序来下载PDF的业务情况。 目录 1.功能代码 1.1 manifest 设置 …

neo4j 图数据库使用教程

文章目录 neo4j 图数据库使用教程1&#xff09; 下载2&#xff09;安装3&#xff09; 创建数据4&#xff09;查询数据5&#xff09; 更新数据6&#xff09; 建立索引 neo4j 图数据库使用教程 1&#xff09; 下载 neo4j下载地址 http://dist.neo4j.org/neo4j-community-3.5.2…

【C++前后缀分解】1888. 使二进制字符串字符交替的最少反转次数|2005

本文涉及知识点 C前后缀分解 LeetCode1888. 使二进制字符串字符交替的最少反转次数 给你一个二进制字符串 s 。你可以按任意顺序执行以下两种操作任意次&#xff1a; 类型 1 &#xff1a;删除 字符串 s 的第一个字符并将它 添加 到字符串结尾。 类型 2 &#xff1a;选择 字符…

redis群集三种模式:主从复制、哨兵、集群

redis群集有三种模式 redis群集有三种模式&#xff0c;分别是主从同步/复制、哨兵模式、Cluster&#xff0c;下面会讲解一下三种模式的工作方式&#xff0c;以及如何搭建cluster群集 ●主从复制&#xff1a;主从复制是高可用Redis的基础&#xff0c;哨兵和集群都是在主从复制…

合宙Air201模组LuatOS扩展功能:温湿度传感器篇!

通过前面几期的学习&#xff0c;同学们的学习热情越来越高。 合宙Air201模组除了支持3种定位方式外&#xff0c;还具有丰富的扩展功能&#xff0c;比如&#xff1a;通过外扩BTB链接方案&#xff0c;最多可支持21个IO接口&#xff1a;SPI、I2C、UART等多种接口全部支持。 本期…

【时时三省】(C语言基础)指针进阶 例题8

山不在高&#xff0c;有仙则名。水不在深&#xff0c;有龙则灵。 ----CSDN 时时三省 第一个打印2 a6不管它是多大 前面是&#xff1d;s 都得变成两个字节 所以打印2 第二个打印5 sizeof里面的表达式是不参与运算的 所以打印5 上面所有例题总结…

从边缘设备到云端平台,合宙DTURTU打造无缝物联网解决方案

如今&#xff0c;物联网&#xff08;IoT&#xff09;技术飞速发展&#xff0c;万物互联的时代已然到来&#xff0c;那么&#xff0c;高效、稳定地连接边缘设备与云端平台&#xff0c;实现数据的实时采集、传输与处理&#xff0c;就成为了推动物联网应用落地的关键。 DTU&#…

硬件工程师笔试面试——无线通讯模块

目录 15、无线通讯模块 15.1 基础 无线通讯模块实物图 15.1.1 概念 15.1.2 常见的无线通讯模块及其特点 15.1.3 无线通讯模块参数 15.1.4 无线通讯模块工作原理 15.2 相关问题 15.2.1 如何根据项目需求选择合适的无线通讯模块? 15.2.2 无线通讯模块的安全性如何,如…

合宙Air201模组LuatOS:点点鼠标就搞定的FOTA远程升级,你知道吗?

你是不是也经常遇到小伙伴吐槽&#xff1a;开发是个苦差事&#xff01;做项目倒还好&#xff0c;就怕遇到项目升级&#xff0c;那简直让人头大。。。 如果你也有这种困惑&#xff0c;就多了解一下合宙的开发工具&#xff0c;简单实用又高效&#xff0c;甚至只需点点鼠标&#…

【变化检测】基于ChangeStar建筑物(LEVIR-CD)变化检测实战及ONNX推理

主要内容如下&#xff1a; 1、LEVIR-CD数据集介绍及下载 2、运行环境安装 3、ChangeStar模型训练与预测 4、Onnx运行及可视化 运行环境&#xff1a;Python3.8&#xff0c;torch1.12.0cu113&#xff0c;onnxruntime-gpu1.12.0 likyoo变化检测源码&#xff1a;https://github.c…

【楚怡杯】职业院校技能大赛 “云计算应用” 赛项样题四

某企业根据自身业务需求&#xff0c;实施数字化转型&#xff0c;规划和建设数字化平台&#xff0c;平台聚焦“DevOps开发运维一体化”和“数据驱动产品开发”&#xff0c;拟采用开源OpenStack搭建企业内部私有云平台&#xff0c;开源Kubernetes搭建云原生服务平台&#xff0c;选…

动手学深度学习(四)卷积神经网络-下

全连接层存在的问题&#xff1a;参数过大&#xff0c;计算成本过高。 一、网络中的网络&#xff08;NiN&#xff09; 1、NiN块 ①NiN块的结构 NiN串联多个由卷积层和“全连接”层构成的小网络来构建一个深层网络。这种由卷积层和“全连接”层构成的小网络就是NiN块。 &#…

线程池夺命十四问

目录 一&#xff1a;什么是线程池 二&#xff1a;线程池有什么好处 三&#xff1a;如何创建一个线程池 Executors ThreadPoolExecutors 四&#xff1a;创建一个线程池为什么不推荐使用Executors 五&#xff1a;如何设置线程池的大小 六&#xff1a;线程池有哪些参数 …

(CS231n课程笔记)深度学习之损失函数详解(SVM loss,Softmax,熵,交叉熵,KL散度)

学完了线性分类&#xff0c;我们要开始对预测结果进行评估&#xff0c;进而优化权重w&#xff0c;提高预测精度&#xff0c;这就要用到损失函数。 损失函数&#xff08;Loss Function&#xff09;是机器学习模型中的一个关键概念&#xff0c;用于衡量模型的预测结果与真实标签…

【数据结构篇】~链表算法题3(环形链表)

链表算法题3&#xff08;环形链表&#xff09; 环形链表的证明1. 环形链表I​1) 思路2&#xff09;代码实现 2. 环形链表II​1) 思路11) 思路22&#xff09;代码实现 环形链表的证明 1. 环形链表I​ https://leetcode.cn/problems/linked-list-cycle/description/ 1) 思路 判断…

拥控算法BBR入门1

拥塞控制算法只与本地有关 一个TCP会话使用的拥塞控制算法只与本地有关。 两个TCP系统可以在TCP会话的两端使用不同的拥塞控制算法 Bottleneck Bandwidth and Round-trip time Bottleneck 瓶颈 BBR models the network to send as fast as the available bandwidth and is 2…

Qt容器类控件——QGroupBox和QTabWidget

文章目录 QGroupBox又来点餐QTabWidget使用演示 QGroupBox 容器类控件即里面可以容纳其他的控件 QGroupBox叫做分组框&#xff0c;可以把其他控件放在里面作为一组 QGroupBox的存在&#xff0c;只是为了让界面更好看一点&#xff0c;并不实现实质性的功能。 当界面较复杂的时候…

C++ 类的默认成员函数-构造函数

个人主页&#xff1a;Jason_from_China-CSDN博客 所属栏目&#xff1a;C系统性学习_Jason_from_China的博客-CSDN博客 所属栏目&#xff1a;C知识点的补充_Jason_from_China的博客-CSDN博客 概念概述 默认成员函数就是用户没有显式实现&#xff0c;编译器会自动生成的成员函数称…