本地百度瓦片+GraphHopper搭建离线路径规划

2,562 阅读2分钟

WX20210427-154148@2x.png 上面是最终效果图。

一、爬取百度地图底图

OpenStreetMap提供的开源地图实在是不忍直视,所以爬取了百度地图的底图。网上爬取工具很多,就不过多赘述,我这里是使用node爬取,小巧快速。建议大家尝试。高清屏幕上展示建议爬取二倍图,以免显示效果不理想。百度服务器上的地图底图图片资源:

  //百度服务器上的图片资源,尾部时间可根据需要修改,最好下载最新的
  // tyles=pl,大字体ph,正常字体pl
  // customid,地图样式,可选值:dark,midnight,grayscale,hardedge,light,redalert,googlelite,grassgreen,pink,darkgreen,bluish
  let url = `http://online0.map.bdimg.com/tile/?qt=vtile&x=${i}&y=${j}&z=${z}&styles=pl&scaler=2&udt=20210411`;

我这里简单爬取公司附近的一小块区域:

WX20210427-160929@2x.png

二、在leaflet上显示百度地图瓦片

注意:leaflet和百度坐标系不一致,在加载百度瓦片时纠正百度地图瓦片坐标系为wgs84。 加载本地瓦片:

      var normalMapUrl = 'http://localhost:8080/map/tiles1/{z}/{x}/{y}.png';
      var normalMap = L.tileLayer(normalMapUrl, {
        // subdomains: '0123456789',
        maxZoom: 21,
        minZoom: 3,
        coordType: 'bd09',
        tms: true,
      });
      //注意将map的crs赋值 crs: L.CRS.Baidu 详情请阅读示例页面
      const map = L.map('map-container', {
        crs: L.CRS.Baidu,
        minZoom: 3,
        maxZoom: 18,
        attributionControl: false,
        center: this.centerPos,
        zoom: 18,
        detectRetina: false,
        layers: [normalMap],
        contextmenu: true,
        contextmenuItems: [
          {
            text: '设置为起点',
            callback: this.setStartPoint,
          },
          {
            text: '设置为中间点',
            callback: this.setWaypoints,
          },
          {
            text: '设置为终点',
            callback: this.setStopPoint,
          },
          '-',
          {
            text: '开始规划',
            callback: this.calcRoute,
          },
        ],
      });

三、本地搭建GraphHopper服务

  1. 前提:安装jdk1.8,java -version,查看是否安装成功
  2. 下载一个jar包,一个配置文件
wget https://graphhopper.com/public/releases/graphhopper-web-2.3.jar https://raw.githubusercontent.com/graphhopper/graphhopper/stable/config-example.yml
  1. 下载一份pbf格式的数据,我这里下载的是浙江省的路网数据
http://download.openstreetmap.fr/extracts/asia/china/
  1. 执行一条命令,由于数据过大,跑的时候可能出现内存问题,可以试试加上-Xmx2g -Xms2g
java -Xmx2g -Xms2g -Ddw.graphhopper.datareader.file=zhejiang-latest.osm.pbf -jar \*.jar server config-example.yml
  1. 服务启动之后,提示打开localhost:8989,后端搭建完毕。

WX20210427-163428@2x.png

四、leaflet前端请求后端GraphHopper服务

请求的完整链接,用代理解决跨域问题,route开头的路径代理到8989端口。

http://localhost:8080/route?point=30.280167196872135,120.00581735089507&point=30.28060382350749,120.01634549212933&type=json&locale=zh-CN&vehicle=car&weighting=fastest&points_encoded=false

axios发送请求

    getRoute(url) {
      /* url =
        'route?point=30.27979217476267,120.00611188821497&point=30.27691993999111,120.01250627450645&type=json&locale=zh-CN&vehicle=car&weighting=fastest&points_encoded=false';
        */
      axios.get(url).then(
        res => {
          console.log(res);
          this._success(res);
        },
        err => {
          console.log(err);
        }
      );
    },
    _success(res) {
      var lnglats = res.paths[0].points.coordinates; //(lng,lat)
      var latlngs = []; //(lat,lng)
      for (let j = 0; j < lnglats.length; j++) {
        var lnglat = lnglats[j];
        var latlng = [];
        latlng[0] = lnglat[1];
        latlng[1] = lnglat[0];
        latlngs.push(latlng);
      }
      var path = L.polyline.antPath(latlngs, { color: '#A52A2A', pulseColor: '#0000FF' });
      path.addTo(this.map);
    },
    _buildRouteUrl() {
      var allPoints = this._setPointsSequence();
      if (allPoints == null || allPoints == undefined) {
        return;
      } else {
        var locs = [],
          i,
          baseUrl;
        for (i = 0; i < allPoints.length; i++) {
          locs.push('point=' + allPoints[i].lat + ',' + allPoints[i].lng);
        }
        baseUrl = 'route?' + locs.join('&');
        return baseUrl + '&type=json&locale=zh-CN&vehicle=car&weighting=fastest&points_encoded=false';
      }
    },

五、结束

为了满足公司核心业务需求,需要一整套离线地图相关的服务。目前还在探索中,期待和有相同需求的同学一起!