需求分析
目前项目中预实现以下功能:
- 分级(省、市、区、海量点)显示点位数据,区县及以上显示聚合信息,包括聚合信息名(省、市、区)和点位数量
- 海量点层级下根据设备状态显示不同颜色图标。
- 聚合点可点击,省市聚合点点击显示下一级聚合情况,区级聚合点点击后显示区县范围。
功能实现
- 搭建地图,初始化省市区数据及点位数据(由后端接口获取,代码省略)。
<template>
<div id="amapcontainer" class="map-container">
</div>
</template>
<script>
import AMapLoader from '@amap/amap-jsapi-loader';
window._AMapSecurityConfig = {
securityJsCode: '「申请的安全密钥」',
}
export default {
name: "amap",
data: function() {
return {
points:[],
areaData:[],
clusterIndexSet:{
province:{
minZoom: 2,
maxZoom: 5,
},
city: {
minZoom: 5,
maxZoom: 10,
},
district: {
minZoom: 10,
maxZoom: 12,
},
},
polygon:null,
pointStyles:[
{
url: require('../../assets/online.png') ,
anchor: [25, 50],
size: [50, 50],
zIndex: 1,
},{
url: require('../../assets/offline.png') ,
anchor: [25, 50],
size: [50, 50],
zIndex: 1,
},{
url:require('../../assets/abnormal.png') ,
anchor: [25, 50],
size: [50, 50],
zIndex: 1,
},{
url: require('../../assets/onlineStar.png') ,
anchor: [15, 15],
size: [30, 30],
zIndex: 1,
}
],
};
},
mounted(){
this.initAMap();
},
methods: {
initAMap(){
AMapLoader.load({
key: "申请好的Web端开发者Key", // 申请好的Web端开发者Key,首次调用 load 时必填
version: "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
plugins: ["AMap.Scale", "AMap.ToolBar", "AMap.ControlBar", 'AMap.Geocoder', 'AMap.Marker','AMap.CitySearch', 'AMap.Geolocation', 'AMap.AutoComplete', 'AMap.InfoWindow','AMap.IndexCluster','AMap.DistrictSearch'], // 需要使用的的插件列表,
}).then((AMap) => {
// 获取到作为地图容器的DOM元素,创建地图实例
window.map = new AMap.Map("amapcontainer", { //设置地图容器id
resizeEnable: true, //是否监控地图容器尺寸变化
zoomEnable: true, // 地图是否可缩放,默认值为true
dragEnable: true, // 地图是否可通过鼠标拖拽平移,默认为true
doubleClickZoom: true, // 地图是否可通过双击鼠标放大地图,默认为true
zoom: 11, //初始化地图级别
center: [121.59996, 31.197646], // 初始化中心点坐标 上海
mapStyle: "amap://styles/dark", // 设置颜色底层
})
this.initDistrict();
this.initPoints();
this.executeConditionRender();
//监听地图缩放或者地图平移事件,当缩放或平移时判断层级展示聚合点或海量点
window.map.on('zoomend', () => { // 监听地图缩放结束后的等级
this.executeConditionRender();
});
window.map.on('moveend', () => { // 监听地图中心点的位置变化
this.executeConditionRender();
})
}).catch(e => {
console.log(e)
})
},
//调用后端接口初始化省市区数据返回this.areaData;
/*
返回的数据格式如下
areaData:[
'上海':{
adcode:'310000', //高德提供的adcode数据
center:'121.473667,31.230525',
... //其他隐藏
},
...//其他省市区类似
]
*/
initDistrict(){
...
return this.areaData
},
//调用后端接口初始化省市区数据返回this.points;
/*
返回的数据格式如下
points:[
{
lnglat:"121.59996, 31.197646", //经纬度数据
style:0, //用于海量点样式数组中使用哪一个数据
id:'1', //数据ID
... //其他隐藏
},
...//其他点位类似
]
*/
initPoints(){
...
return this.points;
}
}
}
- 自定义索引聚合及其样式,并且聚合点点击效果,参考高德示例按索引聚合
//初始化聚合对象
initIndexCluster(){
var Vuethis=this;
window.indexCluster?(window.indexCluster.setMap(null),window.indexCluster =null):"";
window.map.plugin(['AMap.IndexCluster'],function(){
window.indexCluster = new AMap.IndexCluster(window.map, Vuethis.points,{
renderClusterMarker: Vuethis._renderClusterMarker,
clusterIndexSet: Vuethis.clusterIndexSet
});
})
},
//聚合效果样式
getStyle(context) {
var clusterData = context.clusterData; // 聚合中包含数据
var index = context.index; // 聚合的条件
var count = context.count; // 聚合中点的总数
var marker = context.marker; // 聚合绘制点 Marker 对象
var color = [
'8,60,156',
'66,130,198',
'107,174,214',
'78,200,211',
];
var indexs = ['province','city','district'];
var i = indexs.indexOf(index['mainKey']);
var text = clusterData[0][index['mainKey']];
var size =85
if(i <= 2){
text = '<span class="showName">'+ text+'('+context.count +')'+'</span>';
} else {
size = 12 * text.length + 20;
}
var style = {
bgColor: 'rgba(' + color[i] + ',.8)',
borderColor: 'rgba(' + color[i] + ',1)',
text: text,
size: size,
index: i,
color: '#ffffff',
textAlign: 'center',
boxShadow: '0px 0px 5px rgba(0,0,0,0.8)'
}
return style;
},
//聚合显示位置
getPosition(context) {
var key = context.index.mainKey;
var dataItem = context.clusterData && context.clusterData[0];
var districtName = dataItem[key];
if(!this.areaData[districtName]) {
return null;
}
var center = this.areaData[districtName].center.split(',');
var centerLnglat = new AMap.LngLat(center[0], center[1]);
return centerLnglat;
},
// 自定义聚合点样式
_renderClusterMarker (context) {
var Vuethis=this;
var clusterData = context.clusterData; // 聚合中包含数据
var index = context.index; // 聚合的条件
var count = context.count; // 聚合中点的总数
var marker = context.marker; // 聚合点标记对象
var styleObj = this.getStyle(context);
// 自定义点标记样式
var div = document.createElement('div');
div.className = 'amap-cluster';
div.style.backgroundColor = styleObj.bgColor;
div.style.width = styleObj.size + 'px';
if(styleObj.index <= 2) {
div.style.height = styleObj.size + 'px';
//添加聚合点点击事件,省市聚合点点击显示下一级聚合情况,区级聚合点点击后显示区县范围。
context.marker.on('click', function(e) {
var curZoom = window.map.getZoom();
switch(index.mainKey){
case 'province':
curZoom=6;
break;
case 'city':
curZoom=9;
break;
default:
curZoom=11;
break;
}
window.map.setZoomAndCenter(curZoom, e.lnglat);
if(index.mainKey=='district'){
Vuethis.getDistrictBoundary(clusterData[0]['district'])
}
});
}
div.style.display='flex';
div.style.justifyContent="center";
div.style.alignItems="center";
div.style.border = 'solid 1px ' + styleObj.borderColor;
div.style.borderRadius = styleObj.size + 'px';
div.innerHTML = styleObj.text;
div.style.color = styleObj.color;
div.style.textAlign = styleObj.textAlign;
div.style.boxShadow = styleObj.boxShadow;
context.marker.setContent(div)
// 自定义聚合点标记显示位置
var position = this.getPosition(context);
if(position){
context.marker.setPosition(position);
}
context.marker.setAnchor('center');
},
//通过区县名查询区县范围并展示
getDistrictBoundary(name){
var Vuethis=this;
var opts = {
level :'country', //关键字对应的行政区级别, country表示全国
extensions: 'all', //返回行政区边界坐标组等具体信息
level: 'district' //查询行政级别为 区
};
var district = new AMap.DistrictSearch(opts);//注意:需要使用插件同步下发功能才能这样直接使用
district.search(name, function (status, result) {
if (Vuethis.polygon) {
window.map.remove(Vuethis.polygon)//清除上次结果
Vuethis.polygon = null;
}
if (!result || !result.districtList || !result.districtList[0]) {
Vuethis.$message({ message: '请确认区县名称是否正确', type: "error" });
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]]
}
Vuethis.polygon = new AMap.Polygon({
strokeWeight: 1,
path: bounds,
fillOpacity: 0.4,
fillColor: '#80d8ff',
strokeColor: '#0091ea'
});
window.map.add(Vuethis.polygon)
window.map.setFitView(Vuethis.polygon);//视口自适应
}
})
},
- 初始化海量点及其样式
//初始化海量点对象
initMassMarks(points,style,zooms){
var mass= new AMap.MassMarks(points, {
opacity:1,
style:style,
zooms:zooms
})
mass.on('click',function(e){
Vuethis.showWindow(e.data); //点击海量点展示数据窗口,代码省略。
})
mass.setMap(window.map);
},
- 实现分级显示聚合点或海量点
思路:判断当前层级,如果层级<10(省市区层级),则初始化聚合对象病展示在地图上。若层级>10则初始化海量点,这里出现过卡顿,故而选择分片渲染方式。具体可见高德地图海量点MassMarks使用卡顿问题记录及解决办法
executeConditionRender() {
let zoom=window.map.getZoom();
this.currentZoom=zoom;
if(zoom<10){ //省市区层级
if(window.indexCluster==null){
this.initIndexCluster()
}
if(zoom<8 && window.polygon){ //省市层级
window.map.remove(window.polygon)//清除上次结果
window.polygon = null;
}
}else{ //设备层级
let screenCoordinateRange = window.map.getBounds()
let northEast = [screenCoordinateRange.northEast.lng, screenCoordinateRange.northEast.lat]
let southEast = [screenCoordinateRange.southWest.lng, screenCoordinateRange.northEast.lat]
let southWest = [screenCoordinateRange.southWest.lng, screenCoordinateRange.southWest.lat]
let northWest = [screenCoordinateRange.northEast.lng, screenCoordinateRange.southWest.lat]
let screenList = this.points.filter(item => { //选择在浏览器视框区域内点位数据
return item.lnglat!=undefined?AMap.GeometryUtil.isPointInRing(item.lnglat, [northEast, southEast, southWest, northWest]):false
})
window.mass!=undefined&&window.mass!=null?window.mass.clear():"" //海量点存在则清除
window.mass=this.initMassMarks(screenList,this.pointStyles,[10,20])
}
},
实现效果
-
省级截图
-
市级截图
-
区级截图
区级点击效果
-
海量点截图
小结
1.记录开发过程中遇到的问题,多研究开发手册。
2.样式效果有些不好看,初始版暂定这个,其他样式效果在此版本上更新。