前言

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

高德开源API花式玩法:租房辅助工具_API

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

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

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

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

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

使用AMap.ArrivalRange画出到达圈

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

高德开源API花式玩法:租房辅助工具_json_02

左上角的面板用来设置参数,可选地铁+公交、地铁、公交三种方式,出行耗时最大支持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里面,比如你在西二旗上班,而你女朋友在国贸,则点选后效果是这样的

高德开源API花式玩法:租房辅助工具_高德地图_03

左侧面板新增了两个位置,点击查询时,我将依次查询这两个到达圈,并渲染到地图上

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 });
  }
}

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

高德开源API花式玩法:租房辅助工具_json_04

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

高德开源API花式玩法:租房辅助工具_json_05

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

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

在高德地图中使用 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,没有的话,也不会报错,但是渲染不出来。

渲染完成后是这样的:

高德开源API花式玩法:租房辅助工具_json_06

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

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

高德提供了通过多边形区域搜索POI的接口

placeSearch = new AMap.PlaceSearch({ //构造地点查询类
   pageSize: 5, // 单页显示结果条数
   pageIndex: 1, // 页码
   map: map, // 展现结果的地图实例
   autoFitView: true // 是否自动调整地图视野使绘制的 Marker点都处于视口的可见范围
 });
 placeSearch.searchInBounds('小区', intersection.geometry.coordinates);

高德开源API花式玩法:租房辅助工具_API_07

效果如上图所示,这样就可以轻松租房啦!

但是由于此接口是 get 请求,如果交集区域过大,会超出 get 请求长度限制:

高德开源API花式玩法:租房辅助工具_json_08

结语

这个就叫产品思维,一个简单的API可以延伸出很多有趣的应用。

此仓库已在 github 开源,地址:

https://github.com/LarryZhu-dev/amap_arrivalRange

番外

高德云镜(高德云镜三维重建平台)目前已向企业和政府开放使用(暂未对个人开发者开放)

高德开源API花式玩法:租房辅助工具_API_09

在web端,高德开发了 Cesium 插件用作展示,但目前来看要求配置过高

  • CesiumJS引擎:CesiumJS v1.117+(建议)
  • 浏览器:Chrome v126+(建议)
  • 显卡:16GB显存以上独立显卡,推荐 NVDIA RTX 4090
  • CPU:2.5 GHz 以上(建议)
  • 内存:32GB 以上(建议)

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