当前位置: 首页 > news >正文

高德地图实现经纬度及获取编码、所属行政区、GIS

安装就不讲了,直接上代码(填上自己的密钥、Key就可以使用)

父级调用

<el-col :span="24" class="p-4 text-align-center"><el-form-item label=" "><el-button type="primary" @click="openMapLocation"><SvgIcon size="small" iconClass="el-icon-location"></SvgIcon><span>获取经纬度</span></el-button></el-form-item><map-location ref="mapLocationRef" @confirm="handleLocationConfirm"></map-location><script setup name="formData">
import MapLocation from '@/components/Map/index.vue' // 引入// 打开弹窗获取经纬度
const mapLocationRef = ref(null);
const openMapLocation = () => {mapLocationRef.value.open();
}// 经纬度确认
const handleLocationConfirm = (locationData) => {formData.value.longitude = locationData.lngformData.value.latitude = locationData.lat
}
</script>

地图封装

<template><el-dialogv-model="mapDialogFlg"title="选择位置与影响范围"width="70vw":before-close="handleClose":close-on-click-modal="false"draggabledestroy-on-closestyle="border-radius: 5px;"align-center@open="handleDialogOpen"><!-- 内容结构保持不变 --><!-- 1. 功能切换与搜索区域 --><div class="top-container mb-4"><el-radio-group v-model="selectMode" class="mb-3" @change="handleModeChange"><el-radio label="point">单点选址(经纬度)</el-radio><el-radio label="circle">圆形范围(中心+半径)</el-radio><el-radio label="polygon">多边形范围(顶点坐标)</el-radio></el-radio-group><div class="search-container flex gap-2"><el-inputv-model="searchKeyword"placeholder="请输入地址搜索"clearableclass="flex-1"@keyup.enter="handleSearch":disabled="searchDisabled"><template #append><el-buttontype="primary"@click="handleSearch":disabled="searchDisabled || !searchKeyword.trim()">搜索</el-button></template></el-input><el-buttonv-if="selectMode !== 'point' && (selectedRange || rangeShapes.length > 0)"type="text"color="danger"@click="clearRange">清除范围</el-button></div></div><!-- 2. 搜索次数超限提示 --><el-alertv-if="showLimitAlert"title="今日搜索次数已达上限,请明日再试或联系管理员升级服务"type="error"show-iconclass="mb-4"/><!-- 3. 地图容器 + 绘图提示 --><div class="map-wrapper"><div id="container"></div><el-tooltipv-if="selectMode === 'circle'"content="点击地图确定圆心,拖拽鼠标调整半径(双击结束)"placement="top"effect="light"class="map-tip"><el-tag size="small">圆形绘制提示</el-tag></el-tooltip><el-tooltipv-if="selectMode === 'polygon'"content="点击地图添加顶点(至少3个),双击结束绘制"placement="top"effect="light"class="map-tip"><el-tag size="small">多边形绘制提示</el-tag></el-tooltip></div><!-- 4. 选中数据显示区域 --><div class="selected-info mt-4"><el-descriptions :column="1" border><el-descriptions-item label="选中经纬度"><span v-if="selectedLngLat">{{ selectedLngLat.lng }}, {{ selectedLngLat.lat }}</span><span v-else class="text-gray-400">未选择位置</span></el-descriptions-item><el-descriptions-item label="行政区编码"><span v-if="selectedDistrict.adcode">{{ selectedDistrict.adcode }}</span><span v-else class="text-gray-400">未获取到编码</span></el-descriptions-item><!-- 新增:行政区信息显示 --><el-descriptions-item label="所属行政区"><span v-if="selectedDistrict.district">{{ selectedDistrict.province }} > {{ selectedDistrict.city }} > {{ selectedDistrict.district }}</span><span v-else class="text-gray-400">未获取到行政区信息</span></el-descriptions-item><el-descriptions-itemv-if="selectMode === 'circle' && selectedRange"label="圆形范围信息"><div>中心经纬度:{{ selectedRange.center.lng }}, {{ selectedRange.center.lat }}</div><div>半径:{{ (selectedRange.radius / 1000).toFixed(2) }} 公里({{ selectedRange.radius }} 米)</div></el-descriptions-item><el-descriptions-itemv-if="selectMode === 'polygon' && selectedRange"label="多边形范围信息"><div>顶点数量:{{ selectedRange.path.length }} 个</div><div>顶点坐标:<el-tagv-for="(point, idx) in selectedRange.path":key="idx"size="mini"class="mr-1 mb-1">{{ point.lng.toFixed(6) }},{{ point.lat.toFixed(6) }}</el-tag></div></el-descriptions-item></el-descriptions></div><!-- 5. 底部按钮 --><template #footer><el-button @click="handleClose">取消</el-button><el-buttontype="primary"@click="confirmSelection":disabled="!selectedLngLat || (selectMode !== 'point' && !selectedRange)">确认选择</el-button></template></el-dialog>
</template><script setup>
import { ref, defineExpose, defineEmits, onUnmounted } from 'vue'
import AMapLoader from '@amap/amap-jsapi-loader'
import { ElMessage, ElTag } from 'element-plus'const emit = defineEmits(['confirm', 'update:visible'])// 响应式变量
const mapDialogFlg = ref(false)
const searchKeyword = ref('')
const selectedLngLat = ref(null)
const selectedRange = ref(null)
const selectMode = ref('point')
const map = ref(null)
const marker = ref(null)
const mouseTool = ref(null)
const placeSearch = ref(null)
const searchDisabled = ref(false)
const showLimitAlert = ref(false)
const rangeShapes = ref([])// 新增:存储行政区信息(省/市/区/县)
const selectedDistrict = ref({province: '', // 省份city: '',     // 城市district: '',  // 区/县adcode: '' // 新增:行政区编码
})// 图标配置 - 增加备用图标和默认样式
const markerIconConfig = {// 主图标地址(使用HTTPS确保兼容性)mainUrl: 'https://a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-default.png',// 备用图标地址(如果主图标失效)fallbackUrl: 'https://a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-red.png',// 图标尺寸size: [36, 42],// 图标偏移offset: [-18, -38]
}// 检查图标是否可访问的工具函数
const checkIconUrl = (url) => {return new Promise((resolve) => {const img = new Image()img.src = urlimg.onload = () => resolve(true)img.onerror = () => resolve(false)})
}// 弹窗关闭逻辑
const handleClose = () => {mapDialogFlg.value = falsesearchKeyword.value = ''selectedDistrict.value = {province: '',city: '',district: '',adcode: ''}if (mouseTool.value) {mouseTool.value.close()mouseTool.value = null}clearRangeShapes()
}// 打开弹窗
const open = (initData = {}) => {mapDialogFlg.value = trueif (initData.lng && initData.lat) {selectedLngLat.value = { lng: initData.lng, lat: initData.lat }}if (initData.range) {selectedRange.value = initData.rangeselectMode.value = initData.range.type}
}// 模式切换处理
const handleModeChange = (newMode) => {if (mouseTool.value) {mouseTool.value.close()mouseTool.value = null}selectedRange.value = nullclearRangeShapes()if (newMode !== 'point' && map.value) {initMouseTool(newMode)}
}// 初始化绘图工具
const initMouseTool = (mode) => {AMapLoader.load({key: '4e4fc1871d4ec68a716f29fd52436bda',version: '2.0',plugins: ['AMap.MouseTool']}).then((AMap) => {mouseTool.value = new AMap.MouseTool(map.value)mouseTool.value.on('draw', (e) => {const { obj } = erangeShapes.value.push(obj)if (mode === 'circle') {selectedRange.value = {type: 'circle',center: { lng: obj.getCenter().lng, lat: obj.getCenter().lat },radius: obj.getRadius(),path: []}selectedLngLat.value = selectedRange.value.centeraddMarker([selectedLngLat.value.lng, selectedLngLat.value.lat])// 新增:调用逆地理编码getDistrictByLngLat(selectedLngLat.value.lng, selectedLngLat.value.lat)} else if (mode === 'polygon') {const path = obj.getPath().map((lnglat) => ({lng: lnglat.lng,lat: lnglat.lat}))selectedRange.value = {type: 'polygon',path: path,center: {},radius: 0}// 计算多边形中心点(优化方案)const center = calculatePolygonCenter(path)selectedLngLat.value = centeraddMarker([center.lng, center.lat])// 新增:调用逆地理编码getDistrictByLngLat(center.lng, center.lat)}mouseTool.value.close()})if (mode === 'circle') {mouseTool.value.circle({strokeColor: '#2f54eb',fillColor: 'rgba(47, 84, 235, 0.2)',strokeWeight: 2})} else if (mode === 'polygon') {mouseTool.value.polygon({strokeColor: '#2f54eb',fillColor: 'rgba(47, 84, 235, 0.2)',strokeWeight: 2})}}).catch((e) => {console.error('绘图工具初始化失败:', e)ElMessage.error('范围绘制功能加载失败,请稍后重试')})
}// 计算多边形几何中心点
const calculatePolygonCenter = (path) => {let totalX = 0let totalY = 0for (const point of path) {totalX += point.lngtotalY += point.lat}return {lng: totalX / path.length,lat: totalY / path.length}
}// 清除范围
const clearRange = () => {selectedRange.value = nullclearRangeShapes()if (mouseTool.value) {mouseTool.value.close()mouseTool.value = nullinitMouseTool(selectMode.value)}
}// 清除地图上所有范围图形
const clearRangeShapes = () => {if (map.value && rangeShapes.value.length > 0) {map.value.remove(rangeShapes.value)rangeShapes.value = []}
}// 地址搜索逻辑
const handleSearch = () => {if (!searchKeyword.value.trim() || !placeSearch.value || searchDisabled.value) returnplaceSearch.value.search(searchKeyword.value, (status, result) => {if (status === 'complete') {if (result.info === 'OK') {if (result.poiList?.pois.length > 0) {const firstResult = result.poiList.pois[0]const { lng, lat } = firstResult.locationmap.value.setCenter([lng, lat])addMarker([lng, lat])selectedLngLat.value = { lng, lat }if (selectMode.value === 'circle' && !selectedRange) {clearRangeShapes()const AMap = window.AMapconst circle = new AMap.Circle({center: [lng, lat],radius: 1000,strokeColor: '#2f54eb',fillColor: 'rgba(47, 84, 235, 0.2)',strokeWeight: 2,map: map.value})rangeShapes.value.push(circle)selectedRange.value = {type: 'circle',center: { lng, lat },radius: 1000,path: []}}} else {ElMessage.warning('未找到匹配的地址,请尝试其他关键词')}} else if (result.info === 'DAILY_QUERY_OVER_LIMIT') {handleSearchLimit()} else {ElMessage.error(`搜索失败: ${result.info}`)}} else if (status === 'error') {result.code === 10002 ? handleSearchLimit() : ElMessage.error(`搜索出错: ${result?.message || '未知错误'}`)}})
}// 搜索超限处理
const handleSearchLimit = () => {searchDisabled.value = trueshowLimitAlert.value = trueElMessage.error('今日搜索次数已达上限,请明日再试')
}// 修复的单点标记方法 - 增加图标检查和容错
const addMarker = async (position) => {// 先移除旧标记if (marker.value) {map.value.remove(marker.value)marker.value = null}if (!window.AMap) return// 检查图标可用性,优先使用可用图标let iconUrl = markerIconConfig.mainUrlconst isMainIconValid = await checkIconUrl(markerIconConfig.mainUrl)if (!isMainIconValid) {console.warn('主标记图标不可用,尝试使用备用图标')const isFallbackValid = await checkIconUrl(markerIconConfig.fallbackUrl)if (isFallbackValid) {iconUrl = markerIconConfig.fallbackUrl} else {console.warn('备用图标也不可用,将使用默认样式')// 如果所有图标都不可用,使用默认样式marker.value = new window.AMap.Marker({position: position,map: map.value,// 使用默认样式color: '#2f54eb',size: new window.AMap.Size(16, 16),shape: 'circle'})return}}// 创建图标并添加标记marker.value = new AMap.Marker({position: position,map: map.value,icon: new AMap.Icon({size: new AMap.Size(36, 42),image: '//a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-default.png',imageSize: new AMap.Size(36, 42),imageOffset: new AMap.Pixel(0, 0)}),offset: new AMap.Pixel(-18, -38) // 图标偏移,使图标底部指向坐标点})
}// 地图初始化
const handleDialogOpen = () => {window._AMapSecurityConfig = {securityJsCode: '' // 密钥}AMapLoader.load({key: '', // 高德keyversion: '2.0',plugins: ['AMap.Scale', 'AMap.OverView', 'AMap.Geolocation','AMap.PlaceSearch', 'AMap.ToolBar', 'AMap.MouseTool', 'AMap.Geocoder']}).then((AMap) => {window.AMap = AMapmap.value = new AMap.Map('container', {mapStyle: 'amap://styles/macaron',viewMode: '3D',zoom: 13,center: selectedLngLat.value ? [selectedLngLat.value.lng, selectedLngLat.value.lat] : [117.000923, 36.651242]})map.value.addControl(new AMap.ToolBar({ position: 'RB' }))map.value.addControl(new AMap.Scale())placeSearch.value = new AMap.PlaceSearch({pageSize: 10,pageIndex: 1,city: '',map: map.value,panel: ''})// 回显已选单点标记if (selectedLngLat.value) {addMarker([selectedLngLat.value.lng, selectedLngLat.value.lat])}// 回显已选范围if (selectedRange.value) {renderRange(selectedRange.value)}// 单点模式:地图点击选点map.value.on('click', (e) => {if (selectMode.value !== 'point') returnconst { lng, lat } = e.lnglataddMarker([lng, lat])selectedLngLat.value = { lng, lat }// 新增:点击后获取行政区信息getDistrictByLngLat(lng, lat)})// 范围模式:初始化绘图工具if (selectMode.value !== 'point') {initMouseTool(selectMode.value)}}).catch((e) => {console.error('地图加载失败:', e)ElMessage.error('地图加载失败,请稍后重试')})
}// 回显已保存的范围
const renderRange = (rangeData) => {const AMap = window.AMapif (!AMap || !map.value) returnclearRangeShapes()let shapeif (rangeData.type === 'circle') {shape = new AMap.Circle({center: [rangeData.center.lng, rangeData.center.lat],radius: rangeData.radius,strokeColor: '#2f54eb',fillColor: 'rgba(47, 84, 235, 0.2)',strokeWeight: 2,map: map.value})}if (rangeData.type === 'polygon') {shape = new AMap.Polygon({path: rangeData.path.map((p) => [p.lng, p.lat]),strokeColor: '#2f54eb',fillColor: 'rgba(47, 84, 235, 0.2)',strokeWeight: 2,map: map.value})}if (shape) {rangeShapes.value.push(shape)}const center = rangeData.center || rangeData.path[0]map.value.setCenter([center.lng, center.lat])
}/*** 通过经纬度获取行政区信息* @param {Number} lng - 经度* @param {Number} lat - 纬度*/
// 修改后的 getDistrictByLngLat 函数
const getDistrictByLngLat = async (lng, lat) => {if (!window.AMap || !lng || !lat) return;// 初始化逆地理编码实例(新增 extensions: 'all')const geocoder = new window.AMap.Geocoder({radius: 1000, // 搜索半径(米)extensions: 'all' // 返回详细地址信息(含 adcode)});geocoder.getAddress([lng, lat], (status, result) => {if (status === 'complete' && result.info === 'OK') {const addressComponent = result.regeocode.addressComponent;// 解析行政区信息和编码selectedDistrict.value = {province: addressComponent.province || '',city: addressComponent.city || addressComponent.province,district: addressComponent.district || '',adcode: addressComponent.adcode // 新增:获取行政区编码};console.log('行政区编码:', selectedDistrict.value.adcode);} else {// 处理失败逻辑selectedDistrict.value = { province: '', city: '', district: '', adcode: '' };}});
};// 确认选择
const confirmSelection = () => {if (!selectedLngLat) returnconst result = {lng: selectedLngLat.value.lng,lat: selectedLngLat.value.lat,range: selectedRange.value,region: selectedDistrict.value}emit('confirm', result)handleClose()
}// 组件卸载
onUnmounted(() => {if (map.value) {map.value.destroy()map.value = null}if (mouseTool.value) {mouseTool.value.close()mouseTool.value = null}window.AMap = nullrangeShapes.value = []
})// 暴露方法给父组件
defineExpose({open
})
</script><style scoped lang="scss">
#container {width: 100%;height: 500px;border-radius: 4px;overflow: hidden;
}.top-container {.search-container {display: flex;align-items: center;}
}.map-wrapper {position: relative;.map-tip {position: absolute;top: 10px;left: 10px;z-index: 100;}
}.selected-info {font-size: 14px;.el-descriptions-item__content {line-height: 1.8;}
}:deep(.amap-logo) {display: none !important;
}:deep(.amap-copyright) {opacity: 0 !important;
}:deep(.el-input.is-disabled .el-input__inner) {background-color: #f5f7fa;cursor: not-allowed;
}
</style>

http://www.dtcms.com/a/423771.html

相关文章:

  • wordpress 扁平化新站seo快速排名 排名
  • 2025年在招投标及竞品信息采集机器人领域,主流RPA全面解析
  • 电子商务网站建设与管理期末考试网站开发方案案例
  • Node.js命令行工具开发
  • 《面向物理交互任务的触觉传感阵列仿真》2020AIM论文解读
  • 未来最紧缺的十大专业seo优化师
  • OCP证书考试难度怎么样?
  • Vue3 defineModel === 实现原理
  • 唐山营销型网站建设2023新闻头条最新消息今天
  • 计算机网络---传输层
  • 如何在阿里云上做网站制作软件的手机软件
  • 深入理解 Java 虚拟机:从原理到实践的全方位剖析
  • 网站谷歌seo做哪些凌点视频素材网
  • 手机app应用网站C语言做网站需要创建窗口吗
  • uniapp 安卓FTP上传下载操作原生插件
  • 国外知名平面设计网站黄骅打牌吧
  • C++ I/O流与文件操作速查
  • 网站制作哪家好又便宜做电商网站的流程
  • 网络边界突围:运营商QoS限速策略
  • 【笔记】在WPF中Decorator是什么以及何时优先考虑 Decorator 派生类
  • [算法练习]Day 4:定长滑动窗口
  • 外汇交易网站开发做网站前端后台
  • 小红书网站建设目的优化师简历
  • 集群的概述和分类和负载均衡集群
  • 专业的商城网站开发搜索引擎优化不包括
  • 哈尔滨市延寿建设局网站wordpress 主题添加
  • 技术实践指南:多模态RAG从数据预处理到生成响应的完整流程
  • 新中地三维GIS开发智慧城市效果和应用场景
  • 做产品封面的网站赵公口网站建设公司
  • Redis开发07:使用stackexchange.redis库实现简单消息队列