标题
【vue使用高德地图api,鼠标绘图工具与点聚合插件的浅结合】
用鼠标绘制矩形,框选地图并放大显示矩形内的点聚合(拓展:点击标记点,可以拿到你的原始数据,去做对应处理)
背景
小编目前也是在学习高德api,偶然想到,如果地图上有很多的点聚合在一起,那能不能通过鼠标绘图,画一个矩形框选放大地图上的点聚合,有了这个想法就开干!百度了很久没有满意的答案,可能是我百度的姿势不对吧。于是决定自己动手做一个。
其他
我使用的数绘图工具,只是一个学习,如果不想要这么复杂的绘图,也可以试试官方的拉框缩放,效果也是一样的,官方示例
目录
一睹为快的效果图
画的矩形
这是原始数据
一、引入高德地图
1、在高德地图——控制台——获取key
传送门:高德地图
登陆后打开控制台
创建应用
添加服务,获取密钥
2、npm 安装
这里来个官方文档的传送门:高德地图api
npm i @amap/amap-jsapi-loader --save
3、版本注意,是 2.0!!!!!!
本篇文章使用的是 2.0版本,如果在官网上找示例或者查看文档的时候很容易找错了,尤其是示例,2.0示例直通车,2.0参考手册
二、上代码,完整代码也有注释,可以直接跳过这一步
1、创建一个index.vue文件
这是html部分
<template>
<div class="map_box">
<!--这个是地图容器,这个id是可以随意改写的,但是一定要记住要和js的初始化map获取的id相同-->
<div id="container"></div>
<!-- 下面的是一个单选控件按钮,是小编之前学习api的时候做的,包涵多个鼠标绘制工具的样式,也配对了对应的切换事件函数,不需要的你们可以自行删除 -->
<div class="cover">
<el-radio-group v-model="radio" size="mini" @change="draw">
<el-radio v-for="(item) of radioGroup" :key="item.value" :label="item.value" border>{{item.name}}</el-radio>
</el-radio-group>
<div class="input-item">
<el-button type="primary" round @click="clearDraw">清除</el-button>
<el-button type="primary" round @click="closeDraw">关闭绘图</el-button>
</div>
</div>
</div>
</template>
这是我用到的数据
points: [ // 这个数据是小编随便捏的,具体看后端怎么给你值
{id: 123,lnglat: [118.118547,24.475637]},
// 这里的id,是用于做点击标记点做下一步处理的,如果不需要可以删除
{id: 456,lnglat: [118.081211,24.493369]},
{lnglat: [118.08885,24.494072]},
{lnglat: [118.09503,24.48962]},
{lnglat: [118.102926,24.48837]},
{lnglat: [118.08679,24.485402]},
{lnglat: [118.081941,24.485909]},
{lnglat: [118.090137,24.476614]},
{lnglat: [118.094171,24.475598]},
{lnglat: [118.089794,24.472708]},
{lnglat: [118.094987,24.480051]},
{lnglat: [118.083614,24.47802]},
{lnglat: [118.09606,24.48298]},
{lnglat: [118.10181,24.478215]},
{lnglat: [118.083829,24.477864]},
{lnglat: [118.137158,24.451969]},
{lnglat: [118.144797,24.452047]},
{lnglat: [118.14351,24.445562]},
{lnglat: [118.114928,24.446812]},
{lnglat: [118.106879,24.453121]},
{lnglat: [118.103618,24.455934]},
{lnglat: [118.114261,24.457731]},
{lnglat: [118.12184,24.457445] },
{lnglat: [118.13044,24.456637]},
{lnglat: [118.112738,24.446725]},
{lnglat: [118.104035,24.442895]},
{lnglat: [118.12364,24.444474]},
{lnglat: [118.12673,24.443168]},
],
在你的页面中引入刚才安装的依赖,哪里用就哪里引。
import AMapLoader from "@amap/amap-jsapi-loader";
2、修改刚才从官网获取的key
除了安装那一步,直接复制第五步的完整代码黏贴,妥妥的
AMapLoader.load({
key: "这里填写你的key", //此处填入我们注册账号后获取的Key
version: "2.0", //指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
plugins: ['AMap.AutoComplete','AMap.PlaceSearch','AMap.MarkerClusterer','AMap.DistrictSearch','AMap.MouseTool'], //需要使用的的插件列表,如比例尺'AMap.Scale'等
})
3、如何获取原数据? 往下看
// 设置点聚合的点击事件,在这里你可以根据你原数据的id,去做你想做的事,我就不明说了
cluster.on("click", data => {
// console.log("click", data);
const {clusterData} = data;
console.log(clusterData); // 原始数据,这里打印的就是你的原始数据!!!!!
})
四、注意事项
我的示例中,右上角的菜单板块 有用到element-ui的单选框组件和按钮,主要是方便,不用手写一套,如果你要完整使用我的示例,需要先安装element-ui,百度一下,很多安装方法,如果你不需要,自己修改一下我的代码也可以用
还有就是
目前这个菜单板块,只有做了⚪和矩形的放大,其他只是玩玩测试而已!!!!!!!!!!!!!
小tips: 绘制时是以单击右键结束
五、完整的代码
一定要先去高德官网获取key,再安装,再复制代码黏贴,然后需修改你的key
<template>
<div class="map_box" style="z-index: 2; overflow: hidden;">
<div id="container"></div>
<div class="cover">
<el-radio-group v-model="radio" size="mini" @change="draw">
<el-radio v-for="(item) of radioGroup" :key="item.value" :label="item.value" border>{{item.name}}</el-radio>
</el-radio-group>
<div class="input-item">
<el-button type="primary" round @click="clearDraw">清除</el-button>
<el-button type="primary" round @click="closeDraw">关闭绘图</el-button>
</div>
<!-- asdasd-->
</div>
</div>
</template>
<script>
import AMapLoader from "@amap/amap-jsapi-loader";
export default {
name: "diviceMap",
data(){
return {
// 有些定义的是我之前用的的变量,看你们需不需要,不需要可以删除
map: null,
maker: null,
marker: [118.118174, 24.468552],
markers: [],
icon: null,
isShow: false,
out: true,
mapModule: null, // AMap
radioGroup: [
{name: '画点', value: 'marker'},
{name: '画折线', value: 'polyline'},
{name: '画多边形', value: 'polygon'},
{name: '画矩形', value: 'rectangle'},
{name: '画圆', value: 'circle'},
],
radio: '',
mouseTool: null, // 鼠标绘图工具
overlays: [], // 覆盖物 获取的点 / 路径参数 数组
center: [118.126078, 24.459206],
zoom: 12, //初始化地图级别
cluster: null, // 点聚合
points: [ // 这个数据是小编随便捏的,具体看后端怎么给你值
{id: 123,lnglat: [118.118547,24.475637]},
// 这里的id,是用于做点击标记点做下一步处理的,如果不需要可以删除
{id: 456,lnglat: [118.081211,24.493369]},
{lnglat: [118.08885,24.494072]},
{lnglat: [118.09503,24.48962]},
{lnglat: [118.102926,24.48837]},
{lnglat: [118.08679,24.485402]},
{lnglat: [118.081941,24.485909]},
{lnglat: [118.090137,24.476614]},
{lnglat: [118.094171,24.475598]},
{lnglat: [118.089794,24.472708]},
{lnglat: [118.094987,24.480051]},
{lnglat: [118.083614,24.47802]},
{lnglat: [118.09606,24.48298]},
{lnglat: [118.10181,24.478215]},
{lnglat: [118.083829,24.477864]},
{lnglat: [118.137158,24.451969]},
{lnglat: [118.144797,24.452047]},
{lnglat: [118.14351,24.445562]},
{lnglat: [118.114928,24.446812]},
{lnglat: [118.106879,24.453121]},
{lnglat: [118.103618,24.455934]},
{lnglat: [118.114261,24.457731]},
{lnglat: [118.12184,24.457445] },
{lnglat: [118.13044,24.456637]},
{lnglat: [118.112738,24.446725]},
{lnglat: [118.104035,24.442895]},
{lnglat: [118.12364,24.444474]},
{lnglat: [118.12673,24.443168]},
],
gridSize: 120, // 设置网格像素大小
}
},
methods: {
// 初始化地图
initMap() {
let that = this;
AMapLoader.load({
key: "这里填写你的key", //此处填入我们注册账号后获取的Key
version: "2.0", //指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
plugins: ['AMap.AutoComplete','AMap.PlaceSearch','AMap.MarkerClusterer','AMap.DistrictSearch','AMap.MouseTool'], //需要使用的的插件列表,如比例尺'AMap.Scale'等
}).then((AMap) => {
that.mapModule = AMap; // 将它赋值给定义的变量,以便后续其他方法需要使用
that.map = new AMap.Map("container", { //设置地图容器id
viewMode: "3D", //是否为3D地图模式
zoom: that.zoom, //初始化地图级别
center: that.center, //初始化地图中心点位置
});
// 地图点击事件,这是之前小编用来获取经纬度
// 要使用showInfoClick函数,要先把下面的注释代码解了
// that.map.on('click', that.showInfoClick);
// 创建鼠标绘图工具
that.mouseTool = new AMap.MouseTool(that.map);
// 添加绘制事件
that.mouseTool.on('draw', that.drawOverlays);
// 这里由于是高德2.0,升级的版本,不再像原来那样需要繁琐的先构造marker点,直接放入带有经纬度的数据就可以了,具体内容请查看官方文档的升级指南
that.cluster = new AMap.MarkerCluster(that.map,that.points,{
renderClusterMarker: that.renderClusterMarker,// 自定义聚合点样式
gridSize: that.gridSize, // 设置网格像素大小
renderMarker: that.renderMarker, // 自定义非聚合点样式
});
// 如何获取原数据? 往下看
// 设置点聚合的点击事件,在这里你可以根据你原数据的id,去做你想做的事,我就不明说了
that.cluster.on("click", data => {
// console.log("click", data);
const {clusterData} = data;
console.log(clusterData); // 原始数据,这里打印的就是你的原始数据!!!!!
})
}).catch(e => {
console.log(e);
})
},
// 自定义聚合点样式,这两个自定义样式事件看不懂没关系,反正照着抄就行,需要的改一改颜色就好了
// 但是这个事件里面有一个自定义点击事件要看一下!!!
renderClusterMarker(context) {
let that = this;
let count = that.points.length;
let factor = Math.pow(context.count / count, 1 / 18);
let div = document.createElement('div');
let Hue = 180 - factor * 180;
let bgColor = 'hsla(' + Hue + ',100%,40%,0.7)';
let fontColor = 'hsla(' + Hue + ',100%,90%,1)';
let borderColor = 'hsla(' + Hue + ',100%,40%,1)';
let shadowColor = 'hsla(' + Hue + ',100%,90%,1)';
div.style.backgroundColor = bgColor;
let size = Math.round(30 + Math.pow(context.count / count, 1 / 5) * 20);
div.style.width = div.style.height = size + 'px';
div.style.border = 'solid 1px ' + borderColor;
div.style.borderRadius = size / 2 + 'px';
div.style.boxShadow = '0 0 5px ' + shadowColor;
div.innerHTML = context.count;
div.style.lineHeight = size + 'px';
div.style.color = fontColor;
div.style.fontSize = '14px';
div.style.textAlign = 'center';
context.marker.setOffset(new that.mapModule.Pixel(-size / 2, -size / 2));
context.marker.setContent(div)
// 自定义点击事件,这边做的是点击聚合点,以聚合点中心,放大一级
context.marker.on('click', function(e) {
console.log(e)
let curZoom = that.map.getZoom();
if(curZoom < 20){
curZoom += 1;
}
that.map.setZoomAndCenter(curZoom, e.lnglat);
});
},
// 自定义非聚合点样式
renderMarker(context){
let content = '<div style="background-color: hsla(180, 100%, 50%, 0.3); height: 18px; width: 18px; border: 1px solid hsl(180, 100%, 40%); border-radius: 12px; box-shadow: hsl(180, 100%, 50%) 0px 0px 3px;"></div>';
let offset = new this.mapModule.Pixel(-9, -9);
context.marker.setContent(content)
context.marker.setOffset(offset)
},
// 绘画工具切换
draw(type){
switch(type){
case 'marker':{
this.mouseTool.marker({
//同Marker的Option设置
});
break;
}
case 'polyline':{
this.mouseTool.polyline({
strokeColor:'#80d8ff'
//同Polyline的Option设置
});
break;
}
case 'polygon':{
this.mouseTool.polygon({
fillColor:'#00b0ff',
strokeColor:'#80d8ff'
//同Polygon的Option设置
});
break;
}
case 'rectangle':{
this.mouseTool.rectangle({
fillColor:'#00b0ff',
strokeColor:'#80d8ff'
//同Polygon的Option设置
});
break;
}
case 'circle':{
this.mouseTool.circle({
fillColor:'#00b0ff',
strokeColor:'#80d8ff'
//同Circle的Option设置
});
break;
}
}
},
drawOverlays(e) {
// console.log(e);//获取路径
this.overlays.push(e.obj);
if (this.radio === 'marker') {
console.log(e.obj)
}else if(this.radio === 'circle'){
// ⚪也有 getBounds()方法
console.log(e.obj.getRadius());//获取半径
console.log(e.obj.getCenter());//获取中心点
let curZoom = this.map.getZoom();
if(curZoom > 5){
curZoom -= 1;
}
this.map.setZoomAndCenter(curZoom, e.obj.getCenter());
// this.clearDraw();
}else {
let northEast = [e.obj.getBounds().northEast.lng,e.obj.getBounds().northEast.lat];
let southWest = [e.obj.getBounds().southWest.lng,e.obj.getBounds().southWest.lat]
console.log(e.obj.getBounds())
let mybounds = new this.mapModule.Bounds(northEast, southWest);
this.map.setBounds(mybounds);
}
},
// 清除覆盖物
clearDraw(){
this.map.remove(this.overlays)
this.overlays = [];
},
//关闭,并清除覆盖物
closeDraw(){
this.mouseTool.close(true);//关闭,并清除覆盖物
this.radio= '';
},
// 获取点击时的坐标,这个函数要使用要把初始化的添加点击事件的代码取消注释
showInfoClick(e){
let lnglat = [e.lnglat.getLng(),e.lnglat.getLat()]
console.log(lnglat)
this.points.push({lnglat:lnglat})
console.log(this.points)
}
},
mounted() {
//挂载阶段调用,DOM初始化完成进行地图初始化
this.initMap();
}
}
</script>
<style scoped>
.map_box {
/*position: absolute;*/
/*top: -30px;*/
width: 100%;
/*border-radius: 50%;*/
}
#container {
padding: 0px;
margin: 0px;
width: 100%;
/*height: 100vh ;*/
height: calc(100vh - 4.4rem);
}
.cover {
position: absolute;
top: 10px;
background-color: #ffffff;
z-index: 15;
width: 300px;
padding: 10px;
/*height: 40px;*/
/*border: 2px solid #265abf;*/
right: 10px;
box-shadow: 0 0 10px 0 rgba(203, 203, 203, 0.3);
border-radius: 5px;
}
.cover .el-radio {
margin-right: 5px;
margin-bottom: 5px;
}
/deep/ .cover .el-radio.is-bordered + .el-radio.is-bordered {
margin-left: 0;
}
/deep/ .cover .input-item .el-button.is-round {
padding: 5px 10px;
}
.cover .input-item {
/*text-align: right;*/
display: flex;
justify-content: space-around;
}
</style>