哈喽,各位高贵的倔友们你们好呀(✪ω✪),今天继续分享关于高德地图内容的文章,在观看此篇文章之前,你也可以先看看上一篇文章,这样能更好理解也能学到更多东西唷。那么,如果各位看官大大觉得还不错,期望点个赞呗,我们话不多说,直接进入正题。
写在开头
高德地图的申请、安装与初始化我们在上一篇文章讲过,这里就不多说啦,直接来进入本章的主题内容。
数据准备
这里小编先准备了一些测试数据,下面会以这种数据结构来编写逻辑,如果你真实的数据结构与此不同,可以自行再转换一下或者适当调整一下代码逻辑。
创建 data.js
文件:
javascript
const data = [
{
"province": "北京市",
"city": "北京城区",
"area": "西城区",
"append": "北京市北京城区西城区3",
"lng": "116.365868",
"lat": "39.912289",
},
{
"province": "上海市",
"city": "上海城区",
"area": "静安区",
"append": "上海市上海城区静安区5",
"lng": "121.459384",
"lat": "31.247105",
},
{
"province": "广东省",
"city": "广州市",
"area": "黄埔区",
"append": "广东省广州市黄埔区4",
"lng": "113.459749",
"lat": "23.106402",
},
{
"province": "四川省",
"city": "成都市",
"area": "青羊区",
"append": "四川省成都市青羊区6",
"lng": "104.062499",
"lat": "30.674406",
},
{
"province": "河北省",
"city": "石家庄市",
"area": "桥西区",
"append": "河北省石家庄市桥西区2",
"lng": "114.461154",
"lat": "38.004043",
},
{
"province": "重庆市",
"city": "重庆城区",
"area": "涪陵区",
"append": "重庆市重庆城区涪陵区1",
"lng": "107.389298",
"lat": "29.703113",
},
];
export default data;
绘制散点
在地图上绘制散点标记,这次小编使用的是高德地图提供的 AMap.CircleMarker 对象。
它能通过经纬度坐标帮我们在地图上快速绘制出一个点形状的圆形,非常的方便,你可以先自己瞅瞅官方的示例,传送门。
当然,高德地图官方提供了很多与散点绘制相关的 API
,这里小编之所以选择这个 API
对象,主要出于以下三点考虑衡量:
- 支持自定义样式,比如每个散点的背景色、边框颜色、散点大小等。
- 支持多种交互事件绑定,比如点击事件、双击事件、鼠标移入移出事件等。
- 通过经纬度坐标绘制标记点,由于小编的业务中,每条数据是携带有各自的经纬度坐标的,所以使用起来比较方便,不过,下面小编也会兼容没有经纬度坐标的数据,没有就让它们用地址信息去高德那边查。
接下来,我们来看看具体的绘制过程:
javascript
<template>
<div id="container" />
</template>
<script>
import AMapLoader from '@amap/amap-jsapi-loader';
import data from './data.js';
export default {
data() {
return {
// map:null,
}
},
async mounted() {
await this.initMap();
this.drawScatterMap();
},
methods: {
/** @name 初始化地图 **/
initMap() {
return new Promise(resolve => {
window._AMapSecurityConfig = {
securityJsCode: '你的安全密钥',
}
AMapLoader.load({
key: '你的Key',
version: '2.0',
plugins: [],
}).then((AMap)=>{
this.map = new AMap.Map('container', {
center: [110.17293, 35.20173],
zoom: 3.8,
});
this.map.on('complete', () => {
resolve();
});
}).catch(e=>{
console.log(e);
})
})
},
/** @name 获取随机颜色 **/
getRandomColor() {
let color = '#';
for(let i = 0; i < 6; i++){
color += (Math.random() * 16 | 0).toString(16);
}
return color;
},
/** @name 绘制散点地图 **/
drawScatterMap() {
for (const [index, address] of data.entries()) {
let center = [address.lng, address.lat];
if (center[0] && center[1]) {
const color = this.getRandomColor();
const scatterInstance = new window.AMap.CircleMarker({
center, // 经纬度坐标
radius: 5, // 散点半径: 0~64
fillColor: color, // 填充颜色, 仅支持16进制颜色值, 默认值为#00D3FC
fillOpacity: 0.8, // 填充颜色透明度
strokeColor: color, // 边框颜色
strokeWeight: 1, // 边框大小
});
scatterInstance.setMap(this.map);
}
}
},
}
}
</script>
<style scoped>
#container {
width: 740px;
height: 500px;
}
</style>
具体效果:
查询经纬度
经纬度坐标是在地图上标记 的前提,但对于一些只有地址信息没有经纬度坐标的数据,高德地图官方也提供了相关的转换 API
,它主要就是 AMap.Geocoder 对象,该对象支持用于地址信息描述与经纬度坐标之间的转换。
来看看使用示例:
javascript
const geocoder = new window.AMap.Geocoder();
geocoder.getLocation('广东省广州市白云区白云山', (status, result) => {
console.log(status, result)
});
需要注意,每个平台地图所拥有的经纬度坐标体系是不同的,所产生出来的经纬度坐标也是有差异的,例如你用百度地图的经纬度坐标来高德地图上绘制标记点,实际可能会产生一些偏差,这点是需要我们注意的!
那么,我们把它加入到绘制散点的编码中,具体如下:
javascript
<script>
import AMapLoader from '@amap/amap-jsapi-loader';
import data from './data.js';
let geocoder = null;
export default {
...
methods: {
...,
/** @name 初始化地图 **/
initMap() {
return new Promise(resolve => {
...
AMapLoader.load({
key: '你的Key',
version: '2.0',
plugins: [
'AMap.Geocoder', // 配置额外的插件
],
}).then((AMap)=>{
...
})
},
/** @name 根据地址从高德获取地址的经纬度信息 **/
geoCode(address) {
return new Promise(resolve => {
if (!geocoder) {
geocoder = new window.AMap.Geocoder();
}
geocoder.getLocation(address, (status, result) => {
if (status === 'complete' && result.geocodes.length) {
const lnglat = result.geocodes[0].location;
resolve(lnglat);
} else {
console.warn('根据地址查询位置失败:' + address);
}
});
});
},
/** @name 绘制散点地图 **/
drawScatterMap() {
for (const [index, address] of data.entries()) {
let center = [address.lng, address.lat];
// 兼容缺失经纬度坐标的数据
if (!center[0] || !center[1]) {
// 前端通过高德API自行查经纬度
const result = await this.geoCode(address.append);
center = [result.lng, result.lat];
// 如果还是查不到经纬度,就放弃这个点
if (!center[0] || !center[1]) continue;
}
if (center[0] && center[1]) {
const color = this.getRandomColor();
const scatterInstance = new window.AMap.CircleMarker({
center,
radius: 5,
fillColor: color,
fillOpacity: 0.8,
strokeColor: color,
strokeWeight: 1,
});
scatterInstance.setMap(this.map);
}
}
},
}
}
</script>
你可以尝试把数据中的经纬度坐标删除进行测试。如果你的地址信息越详情,散点标记的位置就越准确,如果缺失除省级中的"任何一级"的信息,会直接往省会城市标记。
注意,由于"地址信息转经纬度坐标"这个过程本质也是发送请求到高德那边去查,如果数据量很大的话,而且数据都要转换,那么这个转换过程需要大量的时间。
这里小编建议,如果是海量数据标点,可以让后端写个接口先把数据挨个发去高德那边查,再把查到的经纬度坐标同步到数据库中,这样子能避免前端渲染性能问题。
当然,最好的方式就是在用户产生地址信息数据的时候,就顺便存储经纬度坐标,这是最好的了(✪ω✪)。
手动控制散点大小
接下来,我们继续来完成一个"控制散点大小"的功能,本质就是拿 radius
半径字段做文章,高德地图官方对于 radius
的限制范围是:0~64。
其实在实际业务中,散点的大小很多时候都表示一条数据相比于另一条数据的大小,这个"大小"它可以是一条数据中的某个字段比较,也可以是某几个字段之和比较。但是,最终每条数据都会有产生一个值,用来控制这个半径的大小,而这个值往往并不能直接赋值给到半径,它需要经过相关的公式转换。
下面我们先来看一个效果:
小编通过一个进度条来控制散点的大小,这个进度条的最小值为 0
,最大值为 100
,而且小编期望散点的自身最小值限制为 5
,这样不至于整个点都消失了。
那么应该如何来完成这个转换过程呢?公式如下:
半径大小 = 最小值 + (最大值 - 最小值) * (当前值 / 100)
具体编码过程:
javascript
<template>
<div class="map">
<div id="container" />
<div>
<input v-model="size" @input="sizeChange" type="range" min="0" max="100" />
{{ size }}
</div>
</div>
</template>
<script>
import AMapLoader from '@amap/amap-jsapi-loader';
import data from './data.js';
let geocoder = null;
let scatterInstanceAll = [];
export default {
data() {
return {
// map:null,
size: 0,
}
},
...,
methods: {
...,
/** @name 散点大小 **/
sizeChange() {
this.drawScatterMap();
},
/** @name 绘制散点地图 **/
async drawScatterMap() {
// 清空,否则会重复标记
if (scatterInstanceAll.length > 0) {
this.map.remove(scatterInstanceAll);
scatterInstanceAll = [];
}
for (const [index, address] of data.entries()) {
let center = [address.lng, address.lat];
if (!center[0] || !center[1]) {
const result = await this.geoCode(address.append);
center = [result.lng, result.lat];
if (!center[0] || !center[1]) continue;
}
if (center[0] && center[1]) {
// 计算半径大小
let radius = 5 + (64 - 5) * (this.size / 100);
const color = this.getRandomColor();
const scatterInstance = new window.AMap.CircleMarker({
center,
radius,
fillColor: color,
fillOpacity: 0.8,
strokeColor: color,
strokeWeight: 1,
});
scatterInstance.setMap(this.map);
scatterInstanceAll.push(scatterInstance);
}
}
},
}
}
</script>
<style scoped>
.map {
width: 740px;
height: 500px;
position: relative;
display: flex;
justify-content: flex-start;
align-items: flex-start;
}
#container {
width: 100%;
height: 100%;
margin-right: 10px;
}
</style>
Em...在小编实际的业务中,这个半径的控制其实考虑得挺复杂的,小脑瓜想了半天就想到这么一个小例子来表达所想,感觉还是欠缺一点意思,但是抓住核心就是 转换。
绑定交互事件
在高德地图官方文档中,对于每个"对象"所能绑定的事件都有明确的列举。
但是,小编想说,文档中好像也不全面,像有些对象文档中并没有写支持点击事件,但实际添加监听,还是可以触发,这....很迷(✪ω✪)。
反正就那样吧,我们继续来看看如何给每个散点绑定事件,直接来看代码:
javascript
export default {
...,
methods: {
...,
/** @name 绘制散点地图 **/
async drawScatterMap() {
...
for (const [index, address] of data.entries()) {
...
if (center[0] && center[1]) {
let radius = 5 + (64 - 5) * (this.size / 100);
const color = this.getRandomColor();
const scatterInstance = new window.AMap.CircleMarker({
center,
radius,
fillColor: color,
fillOpacity: 0.8,
strokeColor: color,
strokeWeight: 1,
zIndex: index, // 层级: 层级越高越靠上方
cursor: 'pointer', // 指定鼠标悬停时的鼠标样式
clickable: true, // 点标记是否可点击
extData: { // 额外自定义属性
index,
address,
},
});
scatterInstance.setMap(this.map);
scatterInstanceAll.push(scatterInstance);
// 绑定交互事件
scatterInstance.on('click', e => {
const extData = e.target._opts.extData;
console.log(extData);
});
}
}
},
}
}
</script>
我们随便点击两个散点,可以获取到绘制时额外携带的属性,这就能方便我们扩展后续的一系列功能了。
你以为到这里就完了?当然还没(✪ω✪),我们再来实现一个小功能,当两个散点叠加再一起的时候,我们期望鼠标移入到哪个点身上,这个点层级能在最上方,移除的时候又恢复成原样。
效果如下:
完成这个功能我们需要绑定 mouseover
与 mouseout
事件,具体编码过程:
javascript
export default {
...,
methods: {
...,
/** @name 绘制散点地图 **/
async drawScatterMap() {
...
for (const [index, address] of data.entries()) {
...
if (center[0] && center[1]) {
let radius = 5 + (64 - 5) * (this.size / 100);
const color = this.getRandomColor();
const scatterInstance = new window.AMap.CircleMarker({
center,
radius,
fillColor: color,
fillOpacity: 0.8,
strokeColor: color,
strokeWeight: 1,
zIndex: index, // 层级: 层级越高越靠上方
cursor: 'pointer',
clickable: true,
extData: {
index, // 把层级携带下去
address,
},
});
scatterInstance.setMap(this.map);
scatterInstanceAll.push(scatterInstance);
// 绑定交互事件
scatterInstance.on('click', e => {});
scatterInstance.on('mouseover', (e) => {
const extData = e.target._opts.extData;
// 散点层级变大
scatterInstanceAll[extData.index].setOptions({
zIndex: data.length + 10,
});
});
scatterInstance.on('mouseout', (e) => {
const extData = e.target._opts.extData;
// 散点层级恢复
scatterInstanceAll[extData.index].setOptions({
zIndex: extData.index,
});
});
}
}
},
}
}
</script>
不过,这些都是比较简单的操作,相信难不倒各位优秀的倔友(✪ω✪),那么小编给各位铁汁们准备了一个小作业,完成以下这么一个功能:
- 鼠标移动到某个散点,能展示出一个"详情列表"。
- 鼠标快速移入移出某个散点、或者两个散点之间移动切换,如何更好的展示该浮层列表?
- 实现移入散点展示,移出散点关闭,移入浮层展示,移出浮层关闭。
- 因为可能存在多条数据标记在同一个点,所以同一个点可能会有多条数据,故该浮层列表需要做分页处理,这又该如何处理?
- 实现浮层列表的数据可点击。
就这样子,应该不难,感兴趣的小伙伴们可以试试,加油加油。
完整源码
至此,本篇文章就写完啦,撒花撒花。
希望本文对你有所帮助,如有任何疑问,期待你的留言哦。
老样子,点赞+评论=你会了,收藏=你精通了。