• 作者:老汪软件技巧
  • 发表时间:2024-08-20 17:03
  • 浏览量:

前言

做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函数中,我新增了这样的逻辑

_微信高德地图api_高德地图租房子

      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 开源,地址:

/LarryZhu-de…

番外

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

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

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

官网:

开发文档:


上一条查看详情 +传知代码 ⌋ DETR[端到端目标检测]
下一条 查看详情 +没有了