利用高德api实现自定义区域下钻
利用高德api实现自定义区域下钻源码:https://github.com/wforguo/daily-code/blob/master/vue-app/vue2-app/src/views/AmapDrop/index.vue演示:https://forguo.cn/app/amap-drill.html
高德地图实现自定义区域下钻
源码:https://github.com/wforguo/daily-code/blob/master/vue-app/vue2-app/src/views/AmapDrop/index.vue
演示:https://forguo.cn/app/amap-drill.html
一、地图下钻
- antv区域钻取
https://l7.antv.vision/zh/examples/choropleth/drill#order-drill
- 高德区域钻取
https://lbs.amap.com/demo/amap-ui/demos/amap-ui-districtexplorer/index
上面是关于正常的省市区的一个地图下钻,现成的地图或者组件可以实现,
但是如果遇到自定义的地图层级钻取就哑火了
二、需求
先来看一下最终效果
如图,最终我们要实现科室、部门、经销商的三级钻取,类似我们所说的大区下钻;
方案大概有两个,
- a、每个区域数据都用svg勾勒出来,每次点击通过id及层级去切换
- b、通过高德地图尺量图去完成;
显然,a方案耗时,不够友好,下面就用高德来实现这个需求;
三、自定义区域
可以用来实现自定义科室,自定义部门以及自定义经销商
高德尺量图形
https://lbs.amap.com/api/jsapi-v2/documentation#polygon
翻阅高德api,发现AMap.Polygon
这个api,可以绘制构造多边形对象,通过PolygonOptions
指定多边形样式
这里有path
这个参数,就可以把自定义的每一级所对应的地区边缘经纬度坐标拿到,用尺量图渲染出来,就得到了一个自定义的区域,也就可以绘制各种想要的异形地图了
实现
参考示列:https://lbs.amap.com/demo/jsapi-v2/example/overlayers/polygon-draw
1、先拿到当前自定义区域所对应的经纬度坐标数组,这里以上海为列子
核心代码:
/**
* 需要绘制的经纬度数据源
* 三维数组,这里以上海为列子
*/
let paths = [
// 由于每个区域并非是连一起的,所以每个小的区域是去绘制的,
[
// 这里的经纬度是一个数组,由于参数 path 是这种格式,保持一致即可
[121.7789, 31.3102],
[121.5723, 31.4361],
[121.5624, 31.4864],
[121.7694, 31.3907],
[121.7789, 31.3102],
],
[
[121.9433, 31.2155],
[121.9573, 31.2304],
[122.0086, 31.221],
[121.9957, 31.1608],
[121.9596, 31.1593],
[121.9433, 31.2155],
],
];
2、遍历该区域下的坐标,绘制每个子区域矢量图
**小细节:**每个小区域都需要用尺量图绘制,一起绘制是可以的,但是后面地图的自适应就不好使了
核心代码:
/**
* 尺量图集合
*/
let polygons = [];
/**
* 构造多边形对象
* @param path 多边形轮廓线的节点坐标数组
* @param color
*/
let addPolygon = function (path, color) {
// 用于在地图上绘制线、面等矢量地图要素的类型
let polygon = new AMap.Polygon({
strokeWeight: 2, // 线条宽度,默认为 1
path: path, // 多边形轮廓线的节点坐标数组
fillOpacity: 0.4,
clickable: false,
fillColor: color, // 多边形填充颜色
strokeColor: color, // 线条颜色
lineJoin: 'round', // 折线拐点的绘制样式,默认值为'miter'尖角,其他可选值:'round'圆角、'bevel'斜角
});
polygons.push(polygon);
}
/**
* tips:小细节,
* 每个小区域都需要用尺量图绘制,一起绘制是可以的,但是后面地图的自适应就不好使了
* 遍历每个小区域并绘制
* @param path 多边形轮廓线的节点坐标数组
*/
paths.map(path => {
addPolygon(path);
});
3、添加到地图,并做自适应
**小细节:**渲染到地图之后,用
setFitView
做个地图窗口自适应
// 渲染尺量图到地图
map.add(polygons);
/**
* tips:小细节,
* 绘制完成之后,做个窗口自适应
*/
map.setFitView(polygons);
4、添加中心点Marker
https://lbs.amap.com/api/jsapi-v2/documentation#marker
自定义区域绘制好了,接下来就得将区域名称及数据展示出来
需要使用Marker
组件将对应信息绘制在这个区域的中心位置
这里的Marker
有两个用途
- 展示区域信息及相关数据
- 通过点击实现地图下钻
通过Marker
的extData
属性来携带当前层级数据,便于下一级的钻取
核心代码:
/**
* 自定义marker内容
* @param item { title: '', count: '', position: []}
* @returns {string}
*/
let renderMarker = function (item) {
const {
title = '',
count = 0,
center = [],
} = item;
// 创建纯文本标记
let marker = new AMap.Marker({
content: `<div class='area-map-marker' style='color: ${item.color || '#000'}'>
<div class='area-map-marker__title' style='font-weight: bold;'>${title}</div>
<div class='area-map-marker__title'>${count || 0}</div>
</div>`,
anchor: 'center', // 设置文本标记锚点
draggable: false,
cursor: 'pointer',
position: center,
extData: item,
zIndex: 1000,
});
markers.push(marker);
// 通过点击实现地图下钻
marker.on('mousedown', (e) => {
handleAreaClick(e);
});
marker.setMap(map);
}
这里需要将每个merker
放到一个集合markers
,用于后期的回收
// 清空markers
if (markers.length > 0) {
map.remove(markers);
markers = [];
}
5、中心位置的获取
通过当前所有的经纬度集合【原数据需要做展开处理】,计算得到中心点的经纬度
核心代码:
/**
* 获取随机数
*/
function getRandomNum (min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
/**
* @desc 返回中心点的[经度,纬度]
* @param points points = [[经度,纬度], [经度,纬度]]; 参数数组points的每一项为每一个点的:[经度,纬度]
* @returns {number[]} 返回中心点的数组[经度,纬度]
*/
function getPointsCenter (points) {
try {
let point_num = points.length; // 坐标点个数
let X = 0, Y = 0, Z = 0;
for (let i = 0; i < points.length; i++) {
if (points[i] == '') {
continue;
}
let point = points[i];
let lat, lng, x, y, z;
lng = parseFloat(point[0]) * Math.PI / 180;
lat = parseFloat(point[1]) * Math.PI / 180;
x = Math.cos(lat) * Math.cos(lng);
y = Math.cos(lat) * Math.sin(lng);
z = Math.sin(lat);
X += x;
Y += y;
Z += z;
}
X = X / point_num;
Y = Y / point_num;
Z = Z / point_num;
let tmp_lng = Math.atan2(Y, X);
let tmp_lat = Math.atan2(Z, Math.sqrt(X * X + Y * Y));
// 经纬度分别小数点后2位加随机数,防止Marker完全重叠
let x = getRandomNum(2, 12) * 0.01;
let y = getRandomNum(3, 12) * 0.01;
return [(tmp_lng * 180 / Math.PI) + x, (tmp_lat * 180 / Math.PI) + y];
} catch (e) {
console.warn('获取中心坐标失败');
console.log(e);
}
}
四、数据整合
上面的是一个简单步骤,最重要的还是数据,这里需要得到两个数据
经纬度边缘坐标
https://lbs.amap.com/api/webservice/guide/api/district/
通过省市区code看来查询全国所有的省、市、区对应的经纬度边缘坐标,并通过省市区code关联
extensions
为all
,才能得到对应的边界坐标,这个也最好让服务端来批量获取并存下来,
数据比较多,可以做稀疏处理,大概6倍即可,当然数据越多轮廓越精细
https://restapi.amap.com/v3/config/district?keywords=310000&key=56e119b97e84efd95dbca95cd2be3126&subdistrict=2&extensions=all
polyline
就是我们最终需要的经纬度边缘坐标集合了,然后整合成二位数组
将省市区code和经纬度数组整合成Object,键为省市区code,值为坐标集合
最终结构如下
最好可以放在CDN,来做一个缓存
// 对应上海,苏州和无锡
let areaPath = {
"310000": [
// 由于每个区域并非是连一起的,所以每个小的区域是去绘制的,
[
// 这里的经纬度是一个数组,由于参数 path 是这种格式,保持一致即可
[121.7789, 31.3102],
[121.5723, 31.4361],
[121.5624, 31.4864],
[121.7694, 31.3907],
[121.7789, 31.3102],
],
[
[121.627, 31.445],
[121.5758, 31.4782],
[121.635, 31.453],
[121.627, 31.445],
],
[
[121.9433, 31.2155],
[121.9573, 31.2304],
[122.0086, 31.221],
[121.9957, 31.1608],
[121.9596, 31.1593],
[121.9433, 31.2155],
],
],
"320500": [
[[121.3435, 31.51206], [121.32251, 31.49936], [121.32016, 31.50588], [121.30553, 31.50534], [121.2986,
31.49151], [121.26887, 31.48746], [121.25943, 31.47795], [121.25445, 31.48375], [121.24707, 31.47706], [
121.2419, 31.49329], [121.23524, 31.49312], [121.23036, 31.47743], [121.2138, 31.47891], [121.18082,
31.45146], [121.14734, 31.44393], [121.14783, 31.43619], [121.16434, 31.43106], [121.14625, 31.42107], [
121.15938, 31.40579], [121.14383, 31.39233], [121.14844, 31.38541], [121.11375, 31.37445], [121.12022,
31.36867], [121.10695, 31.3666], [121.10728, 31.35525], [121.11798, 31.35141], [121.11797, 31.34345], [
121.13044, 31.34421], [121.12995, 31.30259], [121.14378, 31.3097], [121.16156, 31.28299], [121.14521,
31.27529], [121.12065, 31.28703], [121.10545, 31.27365], [121.08879, 31.29208], [121.08215, 31.27154], [
121.06246, 31.26745], [121.05716, 31.24643], [121.06435, 31.24614], [121.0668, 31.19496], [121.0773,
31.16602], [121.06912, 31.1487], [121.04542, 31.15403], [121.04077, 31.13759], [121.02672, 31.14377], [
121.01849, 31.1341], [120.98391, 31.13171], [120.93034, 31.14141], [120.87874, 31.13338], [120.8568,
31.10283], [120.89235, 31.09749], [120.90472, 31.0805], [120.89462, 31.05866], [120.90136, 31.0175], [
120.89135, 31.00374], [120.8655, 30.98968], [120.8474, 30.9896], [120.82298, 31.0057], [120.80291, 31.00541],
[120.76993, 30.99662], [120.7697, 30.9773], [120.74594, 30.9625], [120.7256, 30.97154], [120.6982, 30.97079], [
120.68445, 30.95518], [120.69756, 30.95034], [120.71063, 30.92905], [120.71326, 30.88505], [120.70128,
30.88336], [120.70392, 30.87094], [120.69312, 30.87066], [120.68282, 30.88254], [120.66326, 30.86131], [
120.65869, 30.86527], [120.65488, 30.84704], [120.64356, 30.85546], [120.60862, 30.84825], [120.58896,
30.85443], [120.55949, 30.83152], [120.50444, 30.75797], [120.48909, 30.76369], [120.47553, 30.80386], [
120.45528, 30.81685], [120.4604, 30.83986], [120.4413, 30.85629], [120.45318, 30.86972], [120.4345,
30.88798], [120.44254, 30.90168], [120.435, 30.92082], [120.4201, 30.92771], [120.42422, 30.90058], [
120.37941, 30.89077], [120.36466, 30.88047], [120.35682, 30.911], [120.35967, 30.93294], [120.37129,
30.94867], [120.25066, 30.92571], [120.1979, 30.92831], [120.13252, 30.94308], [120.05245, 31.00574], [
120.00041, 31.02775], [119.98852, 31.05922], [119.94364, 31.10466], [119.93982, 31.14295], [119.9205,
31.16234], [119.9197, 31.17074], [120.11023, 31.264], [120.17372, 31.30881], [120.20961, 31.34566], [
120.41771, 31.44768], [120.47418, 31.44656], [120.52361, 31.46873], [120.54297, 31.46993], [120.55524,
31.47787], [120.54804, 31.49508], [120.5552, 31.50758], [120.59124, 31.52751], [120.59666, 31.51627], [
120.60549, 31.52402], [120.59457, 31.57601], [120.57384, 31.58595], [120.56742, 31.584], [120.57303,
31.57747], [120.55021, 31.57502], [120.54309, 31.60174], [120.56647, 31.60193], [120.57708, 31.61416], [
120.6006, 31.61712], [120.5921, 31.62504], [120.59213, 31.65028], [120.55862, 31.65733], [120.57023,
31.66932], [120.56821, 31.68546], [120.58645, 31.69071], [120.60081, 31.70885], [120.58245, 31.72117], [
120.58436, 31.73447], [120.60002, 31.74463], [120.58424, 31.78215], [120.57071, 31.79378], [120.55838,
31.78571], [120.55589, 31.7942], [120.53156, 31.78779], [120.52254, 31.80629], [120.53131, 31.82785], [
120.50328, 31.84171], [120.49088, 31.87133], [120.46882, 31.87962], [120.4665, 31.88998], [120.37867,
31.91374], [120.39126, 31.92861], [120.37353, 31.94644], [120.3707, 31.99082], [120.40376, 32.01622], [
120.46567, 32.04583], [120.5038, 32.04102], [120.62839, 32.00117], [120.76158, 32.02045], [120.78204,
32.01599], [120.80313, 31.98844], [120.86033, 31.87306], [120.91664, 31.79366], [120.9595, 31.78304], [
121.06064, 31.78306], [121.10122, 31.76252], [121.14533, 31.75392], [121.28911, 31.61628], [121.37221,
31.55321], [121.3435, 31.51206]
]
],
"320200": [
[[120.3707, 31.99082], [120.37353, 31.94644], [120.39126, 31.92861], [120.37867, 31.91374], [120.4665,
31.88998], [120.46882, 31.87962], [120.49088, 31.87133], [120.50328, 31.84171], [120.53131, 31.82785], [
120.52254, 31.80629], [120.53156, 31.78779], [120.55589, 31.7942], [120.55838, 31.78571], [120.57071,
31.79378], [120.59766, 31.75503], [120.60002, 31.74463], [120.58171, 31.72763], [120.58509, 31.71443], [
120.59995, 31.7059], [120.58645, 31.69071], [120.56821, 31.68546], [120.57023, 31.66932], [120.55858,
31.65851], [120.59213, 31.65028], [120.5921, 31.62504], [120.6006, 31.61712], [120.57708, 31.61416], [
120.56647, 31.60193], [120.54309, 31.60174], [120.55021, 31.57502], [120.57303, 31.57747], [120.56742,
31.584], [120.57384, 31.58595], [120.59457, 31.57601], [120.60502, 31.54607], [120.60235, 31.51899], [
120.59666, 31.51627], [120.59124, 31.52751], [120.5552, 31.50758], [120.54804, 31.49508], [120.55524,
31.47787], [120.54297, 31.46993], [120.52361, 31.46873], [120.47418, 31.44656], [120.41771, 31.44768], [
120.20961, 31.34566], [120.17372, 31.30881], [120.11023, 31.264], [119.91862, 31.17019], [119.87874,
31.1608], [119.82785, 31.17454], [119.82345, 31.16582], [119.82937, 31.15827], [119.80986, 31.14852], [
119.79128, 31.15661], [119.79252, 31.17146], [119.77932, 31.17878], [119.71589, 31.16958], [119.70388,
31.1519], [119.67261, 31.168], [119.64186, 31.14843], [119.63705, 31.13453], [119.61373, 31.12918], [
119.59978, 31.10917], [119.57651, 31.11027], [119.57133, 31.12901], [119.53259, 31.15909], [119.55357,
31.17916], [119.55386, 31.22105], [119.52259, 31.24217], [119.53576, 31.27566], [119.52003, 31.31824], [
119.53051, 31.33088], [119.52817, 31.36524], [119.54172, 31.39431], [119.53624, 31.40787], [119.55177,
31.4155], [119.5525, 31.4325], [119.57719, 31.43077], [119.5896, 31.44766], [119.58833, 31.46669], [
119.56515, 31.46434], [119.57515, 31.4808], [119.56716, 31.50508], [119.58362, 31.50454], [119.60787,
31.55318], [119.64445, 31.57296], [119.63938, 31.60026], [119.67303, 31.60933], [119.68512, 31.60383], [
119.69979, 31.57655], [119.70996, 31.576], [119.71482, 31.55618], [119.73326, 31.56316], [119.79218,
31.55338], [119.83272, 31.5291], [119.8478, 31.5298], [119.8622, 31.54634], [119.93563, 31.55272], [
119.97172, 31.53596], [119.97357, 31.51586], [119.99611, 31.4975], [119.99716, 31.50812], [120.04568,
31.49091], [120.03761, 31.42589], [120.02107, 31.38301], [120.0237, 31.36495], [120.04176, 31.34588], [
120.10015, 31.33533], [120.09614, 31.35249], [120.04485, 31.35881], [120.03971, 31.37813], [120.05479,
31.43429], [120.11007, 31.46153], [120.10874, 31.48099], [120.12847, 31.50547], [120.11957, 31.50834], [
120.12067, 31.51615], [120.10303, 31.51407], [120.0992, 31.54823], [120.06026, 31.55799], [120.05566,
31.5777], [120.07489, 31.59536], [120.07558, 31.60711], [120.10448, 31.62877], [120.11948, 31.6309], [
120.12853, 31.68463], [120.14327, 31.67608], [120.15253, 31.68364], [120.143, 31.68833], [120.15684,
31.70383], [120.15447, 31.75624], [120.16902, 31.76097], [120.18394, 31.74992], [120.20116, 31.75327], [
120.17425, 31.80103], [120.17923, 31.81334], [120.16392, 31.83029], [120.18425, 31.85613], [120.17589,
31.87021], [120.14423, 31.85893], [120.08547, 31.85306], [120.04498, 31.82175], [120.02877, 31.83212], [
120.01981, 31.82276], [120.00492, 31.82582], [119.99007, 31.85461], [120.00327, 31.85915], [120.01478,
31.88179], [119.99783, 31.89435], [120.00089, 31.90557], [120.02241, 31.91969], [120.0074, 31.93593], [
120.00878, 31.9513], [120.0224, 31.96775], [120.13481, 31.93938], [120.2366, 31.93291], [120.3707, 31.99082]]
],
}
每一级部门数据及对应的区域code集合
下钻的每一级区域及对应数据是已知的,这里的数据已经存有区域code,
所以就可以很好的和经纬度数据做一个关联
区域数据结构如下
这个数据一般由接口返回
let areaList = [
{
"id": "2",
"name": "华东科",
"level": 2,
"levelTitle": "科室",
"count": 100,
"areaIdList": [
"310000",
"320000",
"330000",
"370000",
"420000",
"500000"
]
},
{
"id": "3",
"name": "华南科",
"mapTier": 2,
"levelTitle": "科室",
"count": 100,
"areaIdList": [
"350000",
"360000",
"430000",
"450000",
"440000",
"420000",
"460000",
"520000",
"530000"
]
},
{
"id": "4",
"name": "西北科",
"level": 2,
"levelTitle": "科室",
"count": 100,
"areaIdList": [
"610000",
"650000",
"500000",
"620000",
]
},
{
"id": "5",
"name": "华北科",
"level": 2,
"levelTitle": "科室",
"count": 100,
"areaIdList": [
"120000",
"130000",
"140000",
"370000",
"340000"
]
},
{
"id": "7",
"name": "东北科",
"level": 2,
"levelTitle": "科室",
"count": 100,
"areaIdList": [
"110000",
"150000",
"230000",
"220000",
"210000"
]
}
]
这里的id和level根据业务需要来定,
当前level
是按照如下划分:
1:国家
2:科室
3:部门
4:经销商
5:区县
五、地图下钻
下钻其实就是获取到下一级的区域数据,并渲染到地图
在marker渲染的时候,添加了事件的处理,
// 通过点击实现地图下钻
marker.on('mousedown', (e) => {
handleAreaClick(e);
});
就可以在事件回调中来根据当前层级来获取下一层级的数据,并完成地图的渲染
let handleAreaClick = function () {
const data = e.target.De.extData;
const {
level,
levelTitle,
id,
count,
} = data;
getMapData(id, level);
}
// 默认从科室层级开始
let getMapData = function (id, level = 2) {
// 接口获取
getUnderData({
id,
level: level + 1,
}).then(res => {
console.log(res);
let areaList = res;
areaList.map(item => {
let position = [];
item.areaList.map(areaId => {
let paths = areaPath[areaId];
paths.map(path => {
position = [...position, ...path];
addPolygon(path);
});
});
// 获取中心点坐标,并渲染区域名称及数据marker
let center = getPointsCenter(position);
renderMarker({
...item,
center,
color: '#ccc',
});
});
})
}
至此,下钻功能完成,源码请移步 https://github.com/wforguo/daily-code/blob/master/vue-app/vue2-app/src/views/AmapDrop/index.vue
…
更多推荐
所有评论(0)