需求背景:在高德地图上实现点击高亮安徽省境内的任一条高速公路且需要展示出沿途的服务区
效果图:
准备
1. 成为高德地图开发者并创建 key
先注册成为高德开放平台开发者,并申请 web 平台(JS API)的 key 和安全密钥(这一步不多做叙述,详细看高德官网)
提示:2021年12月02日以后申请的 key 需要配合你的安全密钥一起使用。
2. 引入高德地图
js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title></title>
<script type="text/javascript">
window._AMapSecurityConfig = {
securityJsCode: "您的高德安全密钥",
};
</script>
<script src="https://webapi.amap.com/maps?v=2.0&key=7c6bc75bf40b761c056521cc4434f6cd&plugin=AMap.GeoJSON,AMap.Geocoder,AMap.PlaceSearch"></script>
<script src="https://webapi.amap.com/loca?v=2.0.0&key=您的高德key"></script>
<style>
html,
body,
#map {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
.amap-logo {
display: none !important;
}
.amap-copyright {
display: none !important;
}
.custom-input-card {
width: 18rem;
}
.custom-input-card .btn:last-child {
margin-left: 1rem;
}
.content-window-card {
position: relative;
width: 23rem;
padding: 0.75rem 0 0 1.25rem;
box-shadow: none;
bottom: 0;
left: 0;
}
.content-window-card p {
height: 2rem;
}
</style>
</head>
<body>
<div id="map"></div>
<script>
let map = new AMap.Map("map", {
zoom: 8,
center: [117.227267, 31.420567],
mapStyle: "amap://styles/8694fc8103d0cbe15eb70b8308cd4171",
viewMode: "3D",
pitch: 50,
fitView: true,
labelzIndex: 111,
});
var distProvince = new AMap.DistrictLayer.Province({
zIndex: 10, //设置图层层级
zooms: [2, 15], //设置图层显示范围
adcode: "340000", //设置行政区 adcode
depth: 0, //设置数据显示层级,0:显示国家面,1:显示省级,当国家为中国时设置depth为2的可以显示市一级
});
// 设置行政区图层样式
distProvince.setStyles({
"stroke-width": 2, //描边线宽
"province-stroke": "#ff714d", //描边颜色
fill: "rgba(0,0,0,0.3)",
});
map.add(distProvince);
</script>
</body>
</html>
3. 利用overpass-turbo获取安徽省境内高速、服务区的geojson
打开overpass-turbo网站,输入查询语句(由于数据有点大,我把高速公路和服务区的查询分两次查询和导出)
3.1 查询高速公路数据
输入查询语句
sql
[out:json];
area["name"="安徽省"]->.a;
(
way["highway"="motorway"](area.a);
way["highway"="motorway_link"](area.a);
);
out body;
>;
out skel qt;
3.2 查询服务区数据
输入查询语句
sql
[out:json];
area["ISO3166-2"="CN-AH"]->.a;
(
way(area.a)[highway=services];
);
(._;>;);
out body;
什么?你不会写这个语句!没关系,有万能的chatgpt,就像这样,拷贝下来粘贴到overpass-turbo语句输入框然后执行
至此我们已经得到高速公路(highway.geojson)和高速服务区(services.geojson)两个geojson文件了,可以愉快的写代码了
实现
1. 给高德地图添加点击事件
js
map.on('click', function (ev) {})
2. 利用高德逆向地理编码能力获取点击位置道路信息
js
map.on('click', function (ev) {
let geocoder = new AMap.Geocoder({
city: '340000', //设为安徽,默认:"全国"
radius: 1000, //范围,默认:500
})
geocoder.getAddress([ev.lnglat.lng, ev.lnglat.lat], function (status, res) {
if (status === 'complete' && res.regeocode) {
console.log('道路信息:', res.regeocode.formattedAddress);
if (res.regeocode.formattedAddress.includes('高速')) {
// 过滤高速编号
const regex = /[A-Z]\d+(?=[\u4e00-\u9fa5]+高速)/ // 匹配一个大写字母后跟一个或多个数字,但要求其后必须紧跟着中文字符和"高速"字符
const result = res.regeocode.formattedAddress.match(regex)
// 匹配高速名称
const highwayRegex = /([\u4e00-\u9fa5]+)(高速)/
const match = res.regeocode.formattedAddress.match(highwayRegex)
const highwayName = match[1] + match[2]
console.log('匹配到的高速名称:', highwayName)
}
}
})
})
3. 加载高速公路geojson并匹配上面获取到的高速编号
js
// 加载高速geojson
fetch('./highway.geojson').then((response) => {
return response.json()
}).then((geoJSON) => {
// 匹配高速编号并且是LineString的要素后添加到相应的数组
const desiredHighways = []
for (let i = 0; i < geoJSON.features.length; i++) {
if (geoJSON.features[i].properties.ref) {
let wayRefs = geoJSON.features[i].properties.ref.split(';')
for (let j = 0; j < wayRefs.length; j++) {
if (wayRefs[j] == result[0] && geoJSON.features[i].geometry.type == 'LineString') {
// console.log(wayRefs[j]);
desiredHighways.push(geoJSON.features[i])
}
}
}
}
4. 添加高亮线
js
// 添加高亮线
for (let i = 0; i < desiredHighways.length; i++) {
//创建 Polyline 实例
let polyline = new AMap.Polyline({
path: desiredHighways[i].geometry.coordinates,
isOutline: false,
outlineColor: '#ffeeff',
borderWeight: 2,
strokeColor: '#1677ff',
strokeOpacity: 1,
strokeWeight: 3,
// 折线样式还支持 'dashed'
strokeStyle: 'solid',
// strokeStyle是dashed时有效
// strokeDasharray: [10, 5],
zIndex: 49,
lineJoin: 'round',
lineCap: 'round',
})
map.add(polyline)
}
至此实现了点击高亮某一条高速公路
5. 加载服务区geojson,匹配上面获取到的高速公路名称后向地图上添加服务区图形
js
// 加载高速服务区
fetch('./services.geojson').then((response) => {
return response.json()
}).then((geoJSON) => {
let geojson = new AMap.GeoJSON({
geoJSON: geoJSON,
getPolygon: function (json, lnglats) {
if (json.properties.extraData.formattedAddress.includes(highwayName)) {
let polygon = new AMap.Polygon({
path: lnglats,
strokeColor: '#ffd666',
strokeWeight: 6,
strokeOpacity: 0.4,
fillOpacity: 0.4,
fillColor: '#95de64',
zIndex: 50,
extData: json.properties,
})
polygon.on('click', function (e) {
console.log('click', this.getExtData())
polygonClick(e, this.getExtData())
})
return polygon
} else {
return []
}
},
// getPolygon: function () {
// return [];
// },
getMarker: function (geojson, lnglats) {
return []
},
})
// 添加geojson图层
map.add(geojson)
})
注意
1. 坐标转换
由于overpass-turbo上面导出的geojson坐标是坐标是WGS84,而高德是GCJ02,所以需要坐标系转换
2. 处理服务区geojson
直接导出的geojson服务区信息没有那么全,所以我使用了高德批量逆向地理编码逐个查询了geojson中的服务区详细信息,然后再将原本的geojson和从高德api获取的数据进行合并处理
完整代码
demo完整源码已经上传到github有需要的可以自取