⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 juejin.cn/post/7403991780512399387 「德莱厄斯」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

前言

做gis的同学肯定知道等时圈这个东西,即:在一定时间内通过某种出行方式能到达的范围

通过一些计算,我们可以做一些好玩的事情,比如上图通过调用mapbox等时圈接口,计算在蓝色点位附近骑车17分钟可以到达哪些公司或学校。

可能由于国内交通情况更为复杂,做实时性较差的等时圈意义不大,所以高德地图提供了比较稳当的API:公交地铁到达圈,其概念和等时圈类似,你可以选择地铁或公交出行,或者两者兼可,高德接口将计算出可达范围。

高德公交到达圈的好玩应用

用了很久的高德,发现这个功能好像没有被很好的应用,其实高德早在1.4的API中就提供了此功能,最近正好要换个房子,突然想到这个东西正好可以拿来做一个很棒的辅助。

由于我跟我对象上班的地方离得比较远,所以找个折中的地方租房是是很重要的,我准备查询到两个到达圈后,再计算一下重合部分,在重合部分找房,就准确多了。

使用AMap.ArrivalRange画出到达圈

首先我做了一个简单的页面

左上角的面板用来设置参数,可选地铁+公交、地铁、公交三种方式,出行耗时最大支持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 });
}
}

由于接口调用方式是以回调函数的形式返回的,所以我这里记录了一下回调次数,当次数满足后,再去调整视角。这段逻辑运行之后,将是如下结果:

很遗憾,你和你的女朋友,下班后的一个小时内见不成面了,这意味着,如果找一个折中的地方租房,你们上班单程通勤,无论如何都超过一个小时了,如果想尽量接近一个小时,那么看下两者的交汇处

大钟寺地铁站将会是个很好的选择。

这样仍需要我们手动去观察,那么能不能算一下两者的交集呢?

在高德地图中使用 turf.js 计算多多边形交集

在上面提到的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,没有的话,也不会报错,但是渲染不出来。

渲染完成后是这样的:

使用绿色代表重合部分,说明在这之中找房都是可以的。

通过 AMap.PlaceSearch 搜索交集区域的小区

高德提供了通过多边形区域搜索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+(建议)
  • 浏览器:Chrome v126+(建议)
  • 显卡:16GB显存以上独立显卡,推荐 NVDIA RTX 4090
  • CPU:2.5 GHz 以上(建议)
  • 内存:32GB 以上(建议)

看起来类似谷歌地球的全量城市建模。

  • 官网:yunjing.amap.com/#/
  • 开发文档:yunjing.amap.com/#/docs
文章目录
  1. 1. 前言
  2. 2. 高德公交到达圈的好玩应用
  3. 3. 使用AMap.ArrivalRange画出到达圈
  4. 4. 在高德地图中使用 turf.js 计算多多边形交集
  5. 5. 通过 AMap.PlaceSearch 搜索交集区域的小区
  6. 6. 结语
  7. 7. 番外