寫在前面的話
公司項目需求要在微信小程序上實現地圖marker點聚合的功能,百度苦尋無果,故自己實現。
最終效果
核心思路
- marker標籤中的callout屬性用來顯示聚合點的數量
- 點擊聚合點時,以聚合點爲中心放大地圖,不需要再次請求後臺接口
- 根據map組件scale縮放級別將地圖分成一個一個的小格子,計算小格子中marker點的數量
- 由於小程序無法像網頁端那樣處理龐大的數據量,爲了提高用戶體驗,地圖狀態爲縮放或者範圍縮小時緩存上次marker數據,無需請求後臺接口
關鍵js文件
- MapUtil.js (判斷是否爲縮放狀態,根據後臺接口數據格式化成地圖聚合類型marker數據等)
- ZjMarker.js(maker點基類,根據簡單的參數構建單個marker及聚合marker)
具體實現
MapUtil.js
import {
zjMarker
} from '../../utils/mapUtil/ZjMarker';
import {
transformFromWGSToGCJ
} from '../WSCoordinate';
export class MapUtil {
constructor(northeast, southwest, scale) {
this.northeast = northeast;
this.southwest = southwest;
this.scale = scale;
console.log('初始化mapUtil成功');
console.log(this.northeast, 'northeast');
}
//設置初始化範圍
setInitData(northeast, southwest, scale) {
this.northeast = northeast;
this.southwest = southwest;
this.scale = scale;
console.log('刷新mapUtil成功');
}
//判斷是否爲縮放,
checkRefresh(northeast, southwest) {
console.log('檢測是否可以刷新接口');
console.log(this.northeast, 'northeast');
console.log(northeast, 'currentNortheast');
let result = true;
if (this.northeast.latitude > northeast.latitude) {
console.log('東北緯度縮小');
}
if (this.southwest.latitude < southwest.latitude) {
console.log('西南緯度增高');
}
if (this.northeast.longitude > northeast.longitude) {
console.log('東北經度增大');
}
if (this.southwest.longitude < southwest.longitude) {
console.log('西南經度縮小');
}
if (this.northeast.latitude > northeast.latitude && this.southwest.latitude < southwest.latitude &&
this.northeast.longitude > northeast.longitude && this.southwest.longitude < southwest.longitude) {
console.log('地圖縮放,不請求接口');
result = false
}
return result;
}
//根據東北 西南經緯度 以及後臺返回標記點 格式化成小程序marker點
getFortMatMarkerList(northeast, southwest, scale, backendMarkerList) {
//屏幕中顯示的經度的長度和緯度的長度
let mapWidth = southwest.longitude - northeast.longitude;
let mapHeight = northeast.latitude - southwest.latitude;
//將屏幕中地圖分割的橫向 格子數和 縱向格子數
let widthSize = scale;
let heightSize = widthSize + parseInt(scale / 2);
//計算每個格子的經緯度的長度
let unitWidth = mapWidth / widthSize;
let unitHeight = mapHeight / heightSize;
let pointData = {};
backendMarkerList.forEach(latLng => {
//如果點在顯示範圍內
if (latLng.latitude < northeast.latitude && latLng.latitude > southwest.latitude &&
latLng.longitude < northeast.longitude && latLng.longitude > southwest.longitude) {
let relativeX = latLng.longitude - northeast.longitude;
let relativeY = latLng.latitude - southwest.latitude;
//計算出這個點,處於哪個格子
let x = parseInt(Math.floor(relativeX / unitWidth));
let y = parseInt(Math.floor(relativeY / unitHeight));
if (x < 0 || y < 0) {
console.log('點位不在格子內', '失敗');
}
//生成單元格點位數據
let pointKey = x + ',' + y;
if (pointData[pointKey] == undefined) {
pointData[pointKey] = [];
}
pointData[pointKey].push(latLng);
}
});
let resultMapArray = [];
for (let y = 0; y < heightSize; y++) {
for (let x = 0; x < widthSize; x++) {
let pointKey = x + ',' + y;
//篩選沒有充電站點位的格子
if (pointData[pointKey] != undefined) {
let markerItem = {};
//聚合點與單點共存 , 長度等於一 不聚合點
if (pointData[pointKey].length == 1) {
let iconPath = pointData[pointKey][0].ScanAndCharge == 1 ? '/img/scanMarkerIcon.png' : '/img/markerIcon.png';
markerItem = new zjMarker(pointData[pointKey][0].longitude, pointData[pointKey][0].latitude, pointData[pointKey][0].StationID, {
iconPath: iconPath
})
//長度大於一 聚合點
} else if (pointData[pointKey].length > 1) {
let iconPath = pointData[pointKey][0].ScanAndCharge == 1 ? '/img/cluScanMarkerIcon.png' : '/img/cluMarkerIcon.png';
markerItem = new zjMarker(pointData[pointKey][0].longitude, pointData[pointKey][0].latitude, pointData[pointKey][0].StationID, {
type: 'cluster',
iconPath: iconPath,
num: pointData[pointKey].length
})
}
resultMapArray.push(markerItem);
}
}
}
console.log(resultMapArray, 'resultMapArray');
return resultMapArray;
}
//獲取中心緯度
getCenterLocation(northeast, southwest) {
let mapWidth = southwest.longitude - northeast.longitude;
let mapHeight = northeast.latitude - southwest.latitude;
let longitude = northeast.longitude + mapWidth;
let latitude = southwest.latitude + mapHeight;
return {
latitude: latitude,
longitude: longitude
}
}
}
ZjMarker.js
//地圖marker標記點基類 (單點`聚合)
export class zjMarker {
constructor(longitude, latitude, id, options = {}) {
this.longitude = longitude;
this.latitude = latitude;
this.id = id;
this.width = options.width ? options.width : 30;
this.height = options.height != undefined ? options.height : 36;
let type = options.type == undefined ? 'single' : options.type;
this.iconPath = options.iconPath == undefined ? '/img/markerIcon.png' : options.iconPath;
if (type != 'single') {
this.callout = {
content: options.num, //文本
color: '#000', //文本顏色
borderRadius: 3, //邊框圓角
borderWidth: 0, //邊框寬度
bgColor: 'transparent', //背景色
padding: 0, //文本邊緣留白,
display: 'ALWAYS',
textAlign: 'center', //文本對齊方式。有效值: left, right, center,
anchorY: 62 //可能需要根據各個手機做出相應的適配
}
}
}
}
最後,在map組件中實現bindregionchange方法獲取東北以及西南經緯度,根據經緯度範圍判斷是否需要請求後臺接口重新獲取點位信息
if (mapUtil.checkRefresh(res.northeast, res.southwest)) {
console.log('開始刷新接口');
let location = mapUtil.getCenterLocation(res.northeast, res.southwest);
that.loadMapData(location.latitude, location.longitude, markerList => {
markerList.forEach(item => {
item.longitude = item.StationLng;
item.latitude = item.StationLat;
});
oriMarkerList = markerList;
that.setData({
markerList: mapUtil.getFortMatMarkerList(res.northeast, res.southwest, scale, markerList)
});
});
mapUtil.setInitData(res.northeast, res.southwest, scale);
} else {
that.setData({
markerList: mapUtil.getFortMatMarkerList(res.northeast, res.southwest, scale, oriMarkerList)
});
}
詳情請諮詢我 vx:a21544182123