做gis的同学肯定知道等时圈这个东西,即:在一定时间内通过某种出行方式能到达的范围
通过一些计算,我们可以做一些好玩的事情,比如上图通过调用mapbox等时圈接口,计算在蓝色点位附近骑车17分钟可以到达哪些公司或学校。
可能由于国内交通情况更为复杂,做实时性较差的等时圈意义不大,所以高德地图提供了比较稳当的API:公交地铁到达圈,其概念和等时圈类似,你可以选择地铁或公交出行,或者两者兼可,高德接口将计算出可达范围。
基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
- 项目地址:https://github.com/YunaiV/ruoyi-vue-pro
- 视频教程:https://doc.iocoder.cn/video/
用了很久的高德,发现这个功能好像没有被很好的应用,其实高德早在1.4的API中就提供了此功能,最近正好要换个房子,突然想到这个东西正好可以拿来做一个很棒的辅助。
由于我跟我对象上班的地方离得比较远,所以找个折中的地方租房是是很重要的,我准备查询到两个到达圈后,再计算一下重合部分,在重合部分找房,就准确多了。
基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
- 项目地址:https://github.com/YunaiV/yudao-cloud
- 视频教程:https://doc.iocoder.cn/video/
首先我做了一个简单的页面
左上角的面板用来设置参数,可选地铁+公交、地铁、公交三种方式,出行耗时最大支持60分钟(超过了接口会报错),位置需要传入经纬度。
初始化地图的步骤就不用说了,我讲一下这个小应用的使用逻辑。
首先,点击地图时,拾取该位置的经纬度,并通过逆地理接口获取到位置文本
function handleMapClick(e: any) {
if (!e.lnglat) return
if (currPositionList.value.length >= 2) {
autolog.log("最多添加 2 个位置", 'error') // 你不会想三个人一起住吧?
return
}
var lnglat = e.lnglat;
geocoder.getAddress(lnglat, (status: string, result: {
regeocode: any; info: string;
}) => {
if (status === "complete" && result.info === "OK") {
currPositionList.value.push({ name: result.regeocode.formattedAddress, lnglat: [lnglat.lng, lnglat.lat] })
}
});
}
可以看到,我把点选的位置信息,暂时存放到了currPositionList
里面,比如你在西二旗上班,而你女朋友在国贸,则点选后效果是这样的
左侧面板新增了两个位置,点击查询时,我将依次查询这两个到达圈,并渲染到地图上
function getArriveRange() {
let loopCount = 0
for (let item of currPositionList.value) {
arrivalRange.search(item.lnglat, currTime.value, (_status: any, result: { bounds: any; }) => {
map.remove(polygons);
if (!result.bounds) return
let currPolygons = []
loopCount++
for (let item of result.bounds) {
let polygon = new AMap.Polygon(polygonStyle[`normal${loopCount}` as "normal1" | "normal2"]);
polygon.setPath(item);
currPolygons.push(polygon)
}
map.add(currPolygons);
polygons.push({
lnglat: item.lnglat,
polygon: currPolygons,
bounds: result.bounds
})
if (loopCount === currPositionList.value.length) {
map.setFitView();
}
}, { policy: currStrategy.value });
}
}
由于接口调用方式是以回调函数的形式返回的,所以我这里记录了一下回调次数,当次数满足后,再去调整视角。这段逻辑运行之后,将是如下结果:
很遗憾,你和你的女朋友,下班后的一个小时内见不成面了,这意味着,如果找一个折中的地方租房,你们上班单程通勤,无论如何都超过一个小时了,如果想尽量接近一个小时,那么看下两者的交汇处
大钟寺地铁站将会是个很好的选择。
这样仍需要我们手动去观察,那么能不能算一下两者的交集呢?
在上面提到的getArriveRange
函数中,我新增了这样的逻辑
if (loopCount === currPositionList.value.length) {
let poly1 = turf.multiPolygon(toNumber(polygons[0].bounds));
let poly2 = turf.multiPolygon(toNumber(polygons[1].bounds));
var intersection = turf.intersect(turf.featureCollection([poly1, poly2]));
if (intersection) {
let geojson = new AMap.GeoJSON({
geoJSON: {
type: "FeatureCollection",
features: [intersection]
},
getPolygon: (_: any, lnglats: any) => {
return new AMap.Polygon({
path: lnglats,
...polygonStyle.overlap
});
}
});
polygons.push({
lnglat: [0, 0],
polygon: geojson,
bounds: intersection.geometry.coordinates
})
map.add(geojson);
} else {
autolog.log("暂无交集,请自行查找", 'error')
}
map.setFitView();
}
由于高德地图到达圈获取到的经纬度是字符串,放到 turf 里面会报错,所以这里写了一个简单的递归,将多维数组所有的数据都转化为数字。
// 递归的将多维数组内的字符串转为数字
function toNumber(arr: any) {
return arr.map((item: any) => {
if (Array.isArray(item)) {
return toNumber(item)
} else {
return Number(item)
}
})
}
使用turf.multiPolygon
将获取到的多维数组转化为标准的 geojson 格式,以便于 turf 处理,在turf7.x中,turf.intersect
的用法稍有改变,需要turf.featureCollection([poly1, poly2])
作为参数传入。
这一步操作 turf 将计算并返回两个多多边形的交集intersection(geojson)
,但是在高德地图API2.0中,直接传入这个geojson会报错(1.4不会),看了下源码,发现高德有一个操作是直接取第 0 个features,导致它识别不了这种格式的数据,所以我们手动处理下,即:
let geojson = new AMap.GeoJSON({
geoJSON: {
type: "FeatureCollection",
features: [intersection]
},
getPolygon: (_: any, lnglats: any) => {
return new AMap.Polygon({
path: lnglats,
...polygonStyle.overlap
});
}
});
这样,高德就可以正确渲染这个数据了,这里需要注意的是,geojson 虽然也是 AMap.Polygon
构造的,但是需要一个特殊参数:path,没有的话,也不会报错,但是渲染不出来。
渲染完成后是这样的:
使用绿色代表重合部分,说明在这之中找房都是可以的。
高德提供了通过多边形区域搜索POI的接口
placeSearch = new AMap.PlaceSearch({ //构造地点查询类
pageSize: 5, // 单页显示结果条数
pageIndex: 1, // 页码
map: map, // 展现结果的地图实例
autoFitView: true // 是否自动调整地图视野使绘制的 Marker点都处于视口的可见范围
});
placeSearch.searchInBounds('小区', intersection.geometry.coordinates);
效果如上图所示,这样就可以轻松租房啦!
但是由于此接口是 get 请求,如果交集区域过大,会超出 get 请求长度限制:
这个就叫产品思维,一个简单的API可以延伸出很多有趣的应用。
此仓库已在 github 开源,地址:
https://github.com/LarryZhu-dev/amap_arrivalRange
高德云镜(高德云镜三维重建平台)目前已向企业和政府开放使用(暂未对个人开发者开放)
在web端,高德开发了 Cesium 插件用作展示,但目前来看要求配置过高
- CesiumJS引擎:CesiumJS v1.117+(建议)
- 显卡:16GB显存以上独立显卡,推荐 NVDIA RTX 4090
看起来类似谷歌地球的全量城市建模。
- 开发文档:yunjing.amap.com/#/docs