vue3使用高德地圖,自定義點標記、默認點聚合樣式、點擊點標記獲取信息
vue3使用高德地圖,自定義點標記、默認點聚合樣式、點擊點標記獲取信息
1、需求:根據(jù)不用的類型和經(jīng)緯度展示不同的自定義點標記,點標記太多,使用點聚合優(yōu)化,參考:見文末補充介紹。
2、在index.html使用CDN引入高德
<script type="module" charset="UTF-8" src="https://webapi.amap.com/loader.js"></script>
3、組件中獲取地圖實例
父組件: <template> <div class=""> <div class=""> <div class="home-map"> <div class="map-box"> <div id="map-container"></div> </div> <site-info /> </div> </div> </div> <a-modal title="Title" v-model:visible="visible" :confirm-loading="confirmLoading" @ok="handleOk" > <p>{{ modalText }}</p> </a-modal> </template> <script setup> import { onMounted, reactive, ref, toRaw, getCurrentInstance, watchEffect } from 'vue'; import { request } from '../../utils/request'; import { initMap, clickSite } from "../../utils/newMap"; import siteInfo from "./components/siteInfo.vue"; const globalProperties = getCurrentInstance().appContext.config.globalProperties; // 獲取全局掛載 const http = globalProperties.$http; const toast = globalProperties.$toast; const visible = ref(false); const confirmLoading = ref(false); const modalText = ref(''); const conObj = reactive({ mapObj: {}, map: {}, markers: [], }) // 監(jiān)聽地圖站點信息變量的值 watchEffect(() => { if(!toRaw(clickSite.siteInfo)) { } else { console.log(toRaw(clickSite.siteInfo)); visible.value = true; modalText.value = `你打開了id為${toRaw(clickSite.siteInfo).id}的點標記`; } }) // 彈窗確定按鈕 const handleOk = () => { confirmLoading.value = true; setTimeout(() => { confirmLoading.value = false; visible.value = false; }, 1000); } const getMap = async(data) => { let map = await initMap(data) if(map) { conObj.map = toRaw(map).map; // 獲取地圖map實例 } } // 獲取地圖所有站點 const getSite = async () => { request(http.GET_HOME_HOME_ALL_SITE, 'get', { ownerUnit: 1 }) .then((res) => { toast("成功獲取所有站點!"); conObj.markers = res.station; getMap(toRaw(conObj.markers)) }) .catch(() => { }) .finally(() => { }) } onMounted(async() => { await getSite(); }) </script> <style lang="less" scoped> .home-map { position: relative; height: calc(100vh - 52px); width: calc(100vw - 64px); .map-box { height: 100%; width: 100%; #map-container { width: 100%; height: 100%; } } } </style>
子組件: <template> <div class="site-info"> <div class="site-info-title" @click="$emit('bySonVal', 4692)"> 測站信息區(qū)域 </div> </div> </template> <script setup> defineEmits(['bySonVal']); // 使用defineEmits和defineProps不需要導(dǎo)入 </script> <style lang="less" scoped> .site-info { position: absolute; top: 0px; left: 0px; padding: .84rem; background: aquamarine; opacity: .8; .site-info-title { } } </style>
4、實例化地圖
newMap.js // 新窨井 import inspInline from "@/assets/imgs/maps/insp_inline.png"; import inspOffline from "@/assets/imgs/maps/insp_offline.png"; import inspAlarm from "@/assets/imgs/maps/insp_alarm.png"; import inspWarn from "@/assets/imgs/maps/insp_warn.png"; // 新內(nèi)澇 import waterLogInline from "@/assets/imgs/maps/waterLog_inline.png"; import waterLogOffline from "@/assets/imgs/maps/waterLog_offline.png"; import waterLogAlarm from "@/assets/imgs/maps/waterLog_alarm.png"; import waterLogWarn from "@/assets/imgs/maps/waterLog_warn.png"; // 新管網(wǎng) import networkInline from "@/assets/imgs/maps/network_inline.png"; import networkOffline from "@/assets/imgs/maps/network_offline.png"; import networkAlarm from "@/assets/imgs/maps/network_alarm.png"; import networkWarn from "@/assets/imgs/maps/network_warn.png"; // 新閘門 import gateInline from "@/assets/imgs/maps/gate_inline.png"; import gateOffline from "@/assets/imgs/maps/gate_offline.png"; import gateAlarm from "@/assets/imgs/maps/gate_alarm.png"; import gateWarn from "@/assets/imgs/maps/gate_warn.png"; // 新泵站 import pumpInline from "@/assets/imgs/maps/pump_inline.png"; import pumpOffline from "@/assets/imgs/maps/pump_offline.png"; import pumpAlarm from "@/assets/imgs/maps/pump_alarm.png"; import pumpWarn from "@/assets/imgs/maps/pump_warn.png"; // 新河道水體水位 import waterInline from "@/assets/imgs/maps/water_inline.png"; import waterOffline from "@/assets/imgs/maps/water_offline.png"; import waterAlarm from "@/assets/imgs/maps/water_alarm.png"; import waterWarn from "@/assets/imgs/maps/water_warn.png"; // 新視頻站 import videoInline from "@/assets/imgs/maps/video_inline.png"; import videoOffline from "@/assets/imgs/maps/video_offline.png"; import videoAlarm from "@/assets/imgs/maps/video_alarm.png"; import videoWarn from "@/assets/imgs/maps/video_warn.png"; import { reactive } from 'vue'; import { wgs_gcj_encrypts } from "./transformLonLat" window._AMapSecurityConfig = { securityJsCode: '39800a1cd4581dbcc97dcfac34bc074e', // 安全秘鑰 } let obj = reactive({ cluster: undefined }) // 獲取地圖上站點的信息 export let clickSite = reactive({ siteInfo: null }) // 地圖初始化 export const initMap = (markersList) => AMapLoader.load({ "key": "32bc822dd7ff40652fc9fd78c546729a", // 申請好的Web端開發(fā)者Key,首次調(diào)用 load 時必填 "version": "2.0", // 指定要加載的 JSAPI 的版本,缺省時默認為 1.4.15 "plugins": ["AMap.Pixel", "AMap.MarkerClusterer", "AMap.ToolBar"], // 需要使用的的插件列表,如比例尺'AMap.Scale'等 }).then((AMap) => { const map = new AMap.Map("map-container", { resizeEnable: true, viewMode: '3D', }); // 添加縮放 map.addControl(new AMap.ToolBar()); // 點聚合數(shù)據(jù) var markers = []; for (let i = 0; i < markersList.length; i++) { // 標準轉(zhuǎn)高德 let oldLat, oldLon // 防止有多個點而返回了NAN if (wgs_gcj_encrypts(markersList[i].latitude, markersList[i].longitude).lat && (wgs_gcj_encrypts(markersList[i].latitude, markersList[i].longitude).lat).length >= 9) { oldLat = (wgs_gcj_encrypts(markersList[i].latitude, markersList[i].longitude).lat).substr(0, 9); } else { oldLat = "0" } if (wgs_gcj_encrypts(markersList[i].latitude, markersList[i].longitude).lon && (wgs_gcj_encrypts(markersList[i].latitude, markersList[i].longitude).lon).length >= 10) { oldLon = (wgs_gcj_encrypts(markersList[i].latitude, markersList[i].longitude).lon).substr(0, 10); } else { oldLon = "0" } // 判斷最后一位是否為 "." if (oldLat.length && (oldLat.charAt(oldLat.length - 1) == ".")) { oldLat = oldLat.substr(0, oldLat.length - 1) } if (oldLon.length && (oldLon.charAt(oldLon.length - 1) == ".")) { oldLon = oldLon.substr(0, oldLon.length - 1) } let lat = parseFloat(oldLat) let lon = parseFloat(oldLon) markers.push({ lnglat: [lon, lat], // 必傳參數(shù),統(tǒng)一格式 code: markersList[i].hexCode, id: markersList[i].id, status: markersList[i].status }) } // 自定義點標記 var _renderMarker = function (context) { var icon // 根據(jù)hexCode與status判斷賦值點標記圖標 let code = context.data[0].code; let status = context.data[0].status; if (code == '62H' && status == 1) { icon = inspInline } else if (code == '62H' && status == 2) { icon = inspWarn } else if (code == '62H' && status == 3) { icon = inspOffline } else if (code == '62H' && status == 4) { icon = inspAlarm } else if (code == '60H' && status == 1) { icon = waterLogInline } else if (code == '60H' && status == 2) { icon = waterLogWarn } else if (code == '60H' && status == 3) { icon = waterLogOffline } else if (code == '60H' && status == 4) { icon = waterLogAlarm } else if (code == '61H' && status == 1) { icon = networkInline } else if (code == '61H' && status == 2) { icon = networkWarn } else if (code == '61H' && status == 3) { icon = networkOffline } else if (code == '61H' && status == 4) { icon = networkAlarm } else if (code == '5AH' && status == 1) { icon = gateInline } else if (code == '5AH' && status == 2) { icon = gateWarn } else if (code == '5AH' && status == 3) { icon = gateOffline } else if (code == '5AH' && status == 4) { icon = gateAlarm } else if (code == '44H' && status == 1) { icon = pumpInline } else if (code == '44H' && status == 2) { icon = pumpWarn } else if (code == '44H' && status == 3) { icon = pumpOffline } else if (code == '44H' && status == 4) { icon = pumpAlarm } else if (code == '48H' && status == 1) { icon = waterInline } else if (code == '48H' && status == 2) { icon = waterWarn } else if (code == '48H' && status == 3) { icon = waterOffline } else if (code == '48H' && status == 4) { icon = waterAlarm } else if (code == '70H' && status == 1) { icon = videoInline } else if (code == '70H' && status == 2) { icon = videoWarn } else if (code == '70H' && status == 3) { icon = videoOffline } else if (code == '70H' && status == 4) { icon = videoAlarm } // 點標記樣式 var content = `<div style="height: 30px; width: 30px;"><img style="width: 100%; height: 100%" src="${icon}"></img></div>`; var offset = new AMap.Pixel(-9, -9); context.marker.setContent(content); context.marker.setOffset(offset); // 設(shè)置標記攜帶的數(shù)據(jù),點擊事件會使用 context.marker.setExtData({ id: context.data[0].id, hexCode: context.data[0].code }); // 單個標記點擊事件 context.marker.on('click', ev => { // 當前標記居中, 第一個參數(shù)為縮放級別 map.setZoomAndCenter(18, ev.target.getPosition()); // 獲取標記攜帶的數(shù)據(jù) const extData = ev.target.getExtData(); clickSite.siteInfo = extData; // 點擊獲取當前站點的信息,觸發(fā)監(jiān)聽事件 }); } if (markers && markers.length) { obj.cluster = new AMap.MarkerClusterer(map, markers, { gridSize: 80, clusterByZoomChange: true, renderMarker: _renderMarker }); // 點擊點聚合 obj.cluster.on('click', (item) => { //此處是通過包含點的數(shù)量判斷是否是聚合點,不是聚合點就執(zhí)行上方單個點的點擊方式 if (item.clusterData.length <= 1) { return; } //這里是計算所有聚合點的中心點 let alllng = 0, alllat = 0; for (const mo of item.clusterData) { alllng += mo.lnglat.lng; alllat += mo.lnglat.lat; } const lat = alllat / item.clusterData.length; const lng = alllng / item.clusterData.length; //這里是放大地圖,此處寫死了每次點擊放大的級別,可以根據(jù)點的數(shù)量和當前大小適應(yīng)放大,體驗更佳 map.setZoomAndCenter(map.getZoom() + 2, [lng, lat]); }); } return { AMap, map } }).catch(e => { console.log(e); })
5、國標轉(zhuǎn)高德
transformLonLat.js: //轉(zhuǎn)換常數(shù) var pi = 3.14159265358979324; var a = 6378245.0; var ee = 0.00669342162296594323; function transformLon(x, y) { var ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x)); ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0; ret += (20.0 * Math.sin(x * pi) + 40.0 * Math.sin(x / 3.0 * pi)) * 2.0 / 3.0; ret += (150.0 * Math.sin(x / 12.0 * pi) + 300.0 * Math.sin(x / 30.0 * pi)) * 2.0 / 3.0; return ret; }; function transformLat(x, y) { var ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x)); ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0; ret += (20.0 * Math.sin(y * pi) + 40.0 * Math.sin(y / 3.0 * pi)) * 2.0 / 3.0; ret += (160.0 * Math.sin(y / 12.0 * pi) + 320 * Math.sin(y * pi / 30.0)) * 2.0 / 3.0; return ret; } function outOfChina(lat, lon) { if (lon < 72.004 || lon > 137.8347) return true; if (lat < 0.8293 || lat > 55.8271) return true; return false; } /* * WGS-84:是國際標準,GPS坐標(Google Earth使用、或者GPS模塊、天地圖) * GCJ-02:中國坐標偏移標準,Google Map、高德、騰訊使用 * BD-09:百度坐標偏移標準,Baidu Map使用 */ /** * wgLat 緯度 * wgLon 經(jīng)度 * WGS-84 到 GCJ-02 的轉(zhuǎn)換(即 GPS 加偏) * */ export const wgs_gcj_encrypts = (wgLat, wgLon) => { var point = {}; if (outOfChina(wgLat, wgLon)) { point.lat = wgLat; point.lon = wgLon; return point; } var dLat = transformLat(wgLon - 105.0, wgLat - 35.0); var dLon = transformLon(wgLon - 105.0, wgLat - 35.0); var radLat = wgLat / 180.0 * pi; var magic = Math.sin(radLat); magic = 1 - ee * magic * magic; var sqrtMagic = Math.sqrt(magic); dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi); dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi); var lat = wgLat + dLat; var lon = wgLon + dLon; point.lat = lat; point.lon = lon; return point; };
6、完成,效果:
補充:
高德地圖js2.0使用MarkerCluster聚合點及添加點擊事件
之前的博客中我便提過了在版本變化的趨勢下,高德地圖也在不斷迭代,目前2.0以下版本已經(jīng)在iOS15 beta版本不適用了,而且2.0以下版本由于卡頓等問題也不在符合用戶的使用需要,升級迫在眉睫,但2.0對于標記的聚和與之前版本有較大區(qū)別,在此將寫法記錄一下,也希望能幫到大家。
2.0以前對于標記的處理是將標記全部在地圖上畫出來之后通過MarkerClusterer聚合,不僅對標記的修改較為復(fù)雜,還會導(dǎo)致嚴重的卡頓問題。而在升級到2.0后,標記的聚合便是通過MarkerCluster將標記的坐標打在地圖上,然后直接渲染的方式了,如下所示:
this.cluster = new AMap.MarkerCluster(this.map, this.allLnglat, { styles: styles, renderMarker: this.renderMarker });
其中只列舉出了最重要的幾個參數(shù):
this.map:當前渲染的地圖
this.allLnglat:所有的標記坐標,例如:[{ lnglat: [117.366,36.577] }]
styles: 聚合點上顯示的圖標,例如:
[{ url: 'https://a.amap.com/jsapi_demos/static/images/blue.png', size: new AMap.Size(32, 32), offset: new AMap.Pixel(-16, -16) },{ url: 'https://a.amap.com/jsapi_demos/static/images/green.png', size: new AMap.Size(32, 32), offset: new AMap.Pixel(-16, -16) }]
renderMarker:非聚合點上顯示的標記,也就是單個點顯示的標記,和2.0版本前我們定義的marker是一個東西,可以自定義樣式添加點擊事件等,例如:
renderMarker(context) { //顯示點的經(jīng)緯度context.data[0].lnglat //此處應(yīng)有邏輯,通過遍歷所有點的經(jīng)緯度來識別當前標記對應(yīng)的數(shù)據(jù) //此處content為設(shè)置當個標記的樣式 context.marker.setContent(content); //此處為設(shè)置標記在地圖上的偏移,根據(jù)標記物大小處理 context.marker.setOffset(new AMap.Pixel(-20, -20)); //此處為設(shè)置標記是否顯示在最上方,一般只有選中的標記在最上方 context.marker.setTop(top); //此處為設(shè)置標記攜帶的數(shù)據(jù),點擊事件會使用 context.marker.setExtData({ id: id }); //此處為添加單個標記點擊事件 context.marker.on('click', ev => { //當前標記居中 this.map.setZoomAndCenter(16, ev.target.getPosition()); //獲取標記攜帶的數(shù)據(jù) const extData = ev.target.getExtData(); const id = extData.id; }); },
由于現(xiàn)在聚合點和標記點都是使用的標記,因此聚合點沒有提供點擊散開的功能,我們可以通過一下方式實現(xiàn):
this.cluster.on('click', (item) => { //此處是通過包含點的數(shù)量判斷是否是聚合點,不是聚合點就執(zhí)行上方單個點的點擊方式 if(item.clusterData.length <= 1) { return; } //這里是計算所有聚合點的中心點 let alllng = 0, alllat = 0; for(const mo of item.clusterData) { alllng += mo.lnglat.lng; alllat += mo.lnglat.lat; } const lat = alllat / item.clusterData.length; const lng = alllng / item.clusterData.length; //這里是放大地圖,此處寫死了每次點擊放大的級別,可以根據(jù)點的數(shù)量和當前大小適應(yīng)放大,體驗更佳 this.map.setZoomAndCenter(this.map.getZoom() + 2, [lng, lat]); });
renderMarker方法是通過地圖縮放實時變化持續(xù)調(diào)用的,所以我們可以直接修改數(shù)據(jù)的方式實時修改點上的數(shù)據(jù),注意修改完成后縮放一下地圖,之后可以也通過this.cluster.setData方法修改要顯示在地圖上的點
注意:這種寫法有個明顯的問題,如果出現(xiàn)兩個坐標完全相同的點,前一個坐標會被后一個頂?shù)?,如果不能保證坐標完全不同的話,建議對坐標進行去重處理或是修改重復(fù)坐標,另外,由于float浮點數(shù)加減問題及context.data[0].lnglat方法會去除一部分小數(shù),建議判斷時統(tǒng)一所有數(shù)據(jù)小數(shù)精度。
到此這篇關(guān)于vue3使用高德地圖,自定義點標記、默認點聚合樣式、點擊點標記獲取信息的文章就介紹到這了,更多相關(guān)vue3高德地圖點標記獲取信息內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vue中對<style scoped> 中的scoped屬性解析
在Vue的單文件組件中,<style scoped> 的 scoped 屬性用于實現(xiàn)?樣式作用域隔離?,下面通過實例代碼講解Vue中對<style scoped>中的scoped屬性,感興趣的朋友一起看看吧2025-03-03vue2響應(yīng)式原理之Object.defineProperty()方法的使用
這篇文章主要介紹了vue2響應(yīng)式原理之Object.defineProperty()方法的使用,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-10-10vue3+Element?Plus?v-model實現(xiàn)父子組件數(shù)據(jù)同步的案例代碼
這篇文章主要介紹了vue3+Element?Plus?v-model實現(xiàn)父子組件數(shù)據(jù)同步,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2024-01-01