最近在做一个国网的一个项目,需要实现一些热力图以及冒泡式的标注等;而且需要在内网使用,感觉使用 echarts 就可以简单的实现无需加载类似 openlayers ,leaflet 等 webgis 框架然后再发布地图服务实现。下面就写一下实现的过程。

实现思路

  • 百度地图API文件
  • 创建本地工具资源文件getModules.js
  • 地图瓦片

百度地图API文件

下载 API 文件

首先需要准备离线的百度地图 API 文件,浏览器打开地址:http://api.map.baidu.com/api?v=2.0,不需要申请 key,当然申请也是可以的;打开之后如下:

(function(){ 
   window.BMap_loadScriptTime = (new Date).getTime(); 
   document.write('<script type="text/javascript" src="http://api.map.baidu.com/getscript?v=2.0&ak=&services=&t=20200327103013"></script>');
})();

复制 script 加载的 src 地址,在浏览器中打开;如下:
在这里插入图片描述
然后复制里面的内容,保存到自己的项目中命名为:baiduApi.js;这个名字随意起;

修改 API 文件

然后需要修改里面的加载方式,进行加载我们本地的资源;首先需要去掉ak验证:搜索charset = 'utf-8',添加 if (/^http/.test(a)) return;如下:
在这里插入图片描述

  function pa(a, b) {
    // 下面是需要添加的代码
    // 如果是调用外部资源就退出去
    if (/^http/.test(a)) return;
    if (b) {
      var c = (1E5 * Math.random()).toFixed(0);
      z._rd["_cbk" + c] = function(a) {
        b && b(a);
        delete z._rd["_cbk" + c]
      };
      a += "&callback=BMap._rd._cbk" + c
    }
    var d = K("script", {
      type: "text/javascript"
    });
    d.charset = "utf-8";
    d.src = a;
    d.addEventListener ? d.addEventListener("load",
    function(a) {
      a = a.target;
      a.parentNode.removeChild(a)
    },
    q) : d.attachEvent && d.attachEvent("onreadystatechange",
    function() {
      var a = window.event.srcElement;
      a && ("loaded" == a.readyState || "complete" == a.readyState) && a.parentNode.removeChild(a)
    });
    setTimeout(function() {
      document.getElementsByTagName("head")[0].appendChild(d);
      d = p
    },
    1)
  }

可以将复制的百度地图API的代码在 https://www.bejson.com/jshtml_format/这里进行格式化;这样看的比较清楚。

创建本地工具资源文件getModules.js

继续在我们保存的百度地图API中搜索 domain.main_domain_cdn.baidu[0],找到使用它定义的z.ma,修改为z.ma = '';
在这里插入图片描述

  z.dz = window.HOST_TYPE || "0";
  z.url = z.l0[z.dz];
  z.Dp = z.url.proto + z.url.domain.baidumap + "/";
  z.oc = z.url.proto + ("2" == z.dz ? z.url.domain.main_domain_nocdn.other: z.url.domain.main_domain_nocdn.baidu) + "/";
  z.ma = z.url.proto + ("2" == z.dz ? z.url.domain.main_domain_cdn.other[0] : z.url.domain.main_domain_cdn.baidu[0]) + "/";
  // 需要添加的代码
  z.ma = '';
  z.hj = z.url.proto + z.url.domain.main_domain_cdn.webmap[0] + "/";

加载模块短路处理

首先创建一个我们本地工具模块保存的脚本,本项目中命名为getModules.js;
然后在我们保存的百度地图API中搜索搜索 &mod=,替换。那个 console.log(a),是为了给后面创建getModules.js,用于打印出需要加载的工具模块;注意这里的函数 pa,就是我们前面去掉ak验证:添加 if (/^http/.test(a)) return;的方法。
在这里插入图片描述

    load: function(a, b, c) {
      var d = this.kb(a);
      if (d.Cd == this.Kj.lq) c && b();
      else {
        if (d.Cd == this.Kj.DG) {
          this.zK(a);
          this.NN(a);
          var e = this;
          e.PC == q && (e.PC = o, setTimeout(function() {
            for (var a = [], b = 0, c = e.Rd.Ln.length; b < c; b++) {
              var d = e.Rd.Ln[b],
              l = "";
              ja.Zy.tK(d) ? l = ja.Zy.get(d) : (l = "", a.push(d + "_" + Cc[d]));
              e.Rd.ew.push({
                fN: d,
                YE: l
              })
            }
            e.PC = q;
            e.Rd.Ln.length = 0;
            // 需要修改的地方
            // 0 == a.length ? e.kL() : pa(e.wG.nQ + "&mod=" + a.join(","))
            0 == a.length ? e.kL() : pa("../../public/data/getmodules.js")
            console.log(a)
          },
          1));
          d.Cd = this.Kj.XP
        }
        d.av.push(b)
      }
    },

创建本地工具资源文件getModules.js

在这里面放API需要调用的模块,上面打印的数组a里面是需要请求的模块,打印出来,通过下面方式获取,放到getmodules.js,例如 canvablepath_lf2t4w, 通过http://api0.map.bdimg.com/getmodules?=v=2.0&t=20140707&mod=canvablepath_lf2t4w下载。
在这里插入图片描述
复制内容保存到getmodules.js文件中,注意:将我们上一步骤打印的a中所有的内容都需要按照这种方法保存到getmodules.js文件中;

下载地图瓦片

首先我们的下载思路是获取到每个瓦片的请求地址,然后通过nodejs进行下载图片;下面只是做了简单的下载瓦片的方法,下载的内容还有有局限性,如果需要比较全的数据可以找一些瓦片地图的下载工具进行下载。

获取瓦片请求地址

在保存的百度地图API脚本中搜索getTilesUrl方法:
在这里插入图片描述
修改如下

  Kd.getTilesUrl = function(a, b, c) {
    var d = a.x,
    a = a.y,
    e = Tb("normal"),
    f = 1,
    c = Jd[c];
    this.map.Vx() && (f = 2);
    d = this.map.gb.Mw(d, b).ns;
    var nUrl = (Id[Math.abs(d + a) % Id.length] + "?qt=vtile&x=" + (d + "").replace(/-/gi, "M") + "&y=" + (a + "").replace(/-/gi, "M") + "&z=" + b + "&styles=" + c + "&scaler=" + f + (6 == x.da.la ? "&color_dep=32&colors=50": "") + "&udt=" + e + "&from=jsapi2_0").replace(/-(\d+)/gi, "M$1")
    window.xxxUrls = window.xxxUrls || [];
    var nname = 'tiles/' + b + '/' + d + '/' + a + '.png'
    var urlArr = {
      url: nUrl,
      name: nname
    }
    window.xxxUrls.push(urlArr)
    return nUrl
  };

我们将每个瓦片请求的地址存放到了window.xxxUrls 数组中;在浏览器中打印该数组:
在这里插入图片描述
先对地图进行拖拽,确保想要的省市都出现在窗口可视范围内,然后会将每个瓦片请求的地址保存到window.xxxUrls 数组中;不过需要放大到每个级别然后进行拖拽显示完成整个需要下载的范围。这样保证将指定级别获取所有范围图片的地址。

下载瓦片

首先获取到window.xxxUrls 数组中的数据,可以在前端发送 ajax请求,然后进行下载。这里我只做了简单的下载,直接在浏览器中打印window.xxxUrls 数组,然后复制内容,到下面down.js文件:
在这里插入图片描述
这里的imgArr 就是window.xxxUrls 数组中的数据;

const imgArr = [{"url":"http://maponline3.bdimg.com/tile/?qt=vtile&x=6327&y=2356&z=15&styles=pl&scaler=1&udt=20200514&from=jsapi2_0","name":"tiles/15/6327/2356.png"}]
const newArr = new Set(imgArr); //去重
const finalArr = Array.from(newArr); 
//创建目录
const fs = require('fs');
// const axios = require('axios');

const request = require('request');
const path = require('path')

const hostdir = "./";
function mkdirSync(dirname) {
    if (fs.existsSync(dirname)) {
        return true;
    } else {
        if (mkdirSync(path.dirname(dirname))) {   
            fs.mkdirSync(dirname);
            return true;
        }
    }
    return false
}
var n = 0;
for (const item of finalArr) {
  const last = item.name.lastIndexOf('/')
  if (last > 0) {
    const name = item.name.substr(last + 1)
    const dir = item.name.substr(0, last)
    const dstpath = hostdir + dir + '/' + name
    if (name.length && dir.length && !fs.existsSync(dstpath)) {
      if (mkdirSync(hostdir + dir)) {
        ++ n;
        request(item.url).pipe(fs.createWriteStream(dstpath))
      }
    }
  }
}

然后运行命令 node down.js,执行完之后就会看到下载的tiles文件夹:
在这里插入图片描述

使用离线瓦片进行加载

还是找到我们刚刚修改的getTilesUrl的位置,修改如下:
注意加载的tiles文件夹的地址要写正确。

  Kd.getTilesUrl = function(a, b, c) {
    var x = a.x
    var y = a.y
    var e = 1
    var z = a
    return 'tiles/'+b+'/'+x+'/'+y+'.png';
  };

这样就完成了百度地图的离线加载。

echart 中加载百度地图

首先项目中使用我们下载的百度地图api文件,有两种方式,一种是在public文件夹下的index.html中直接使用:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width,initial-scale=1.0">
  <link rel="icon" href="<%= BASE_URL %>favicon.ico">
  <title><%= htmlWebpackPlugin.options.title %></title>
  <script type="text/javascript" src="./data/baiduapi.js"></script>
</head>

<body>
  <noscript>
    <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled.
      Please enable it to continue.</strong>
  </noscript>
  <div id="app"></div>
  <!-- built files will be auto injected -->
</body>

</html>

另一种是直接在项目的入口文件引入:

import Vue from 'vue'
import App from './App.vue'
import '../public/data/baiduapi.js'

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
}).$mount('#app')

该项目中使用vue,整体代码如下:

<template>
  <div style="position: absolute;left: 0px; top: 0px;width: 100%;height: 100%;">
    <div id="main" style="height: 100%;width: 100%;">
    </div>
  </div>
</template>

<script>
import echarts from 'echarts';
import { geoCoordMap, datas } from './data';
import 'echarts/extension/bmap/bmap';
export default {
  name: 'allView',
  data() {
    return {
      options: {},
      mapChart: null,
    };
  },
  computed: {
    convertedData() {
      return [this.convertData(datas), this.convertData(datas.sort((a, b) => { b.value - a.value; }).slice(0, 6))];
    }
  },
  created() {
    this.option = {
      // 百度地图api的设置
      bmap: {
        center: [116.404, 39.915],
        zoom: 15,
        roam: true,
      },
      series: [
        {
          name: '全部',
          type: 'scatter',
          symbol: 'pin',
          coordinateSystem: 'bmap',
          data: this.convertData(datas),
          symbolSize: val => {
            return val[2];
          },
          label: {
            normal: {
              formatter: '{@[2]}',
              show: true,
              textStyle: {
                color: '#fff',
                fontSize: 9
              }
            },
            emphasis: {
              show: true
            }
          },
          itemStyle: {
            normal: {
              color: '#F62157'
            }
          },
          zlevel: 6
        }
      ]
    };
  },
  mounted() {
    this.$nextTick(() => {
      this.mapChart = echarts.init(document.getElementById('main'));
      this.mapChart.setOption(this.option);
      this.mapChart.on('click', 'series.scatter', item => {
        console.log(item);
      });
    });
  },
  methods: {
    convertData(data) {
      const res = [];
      for (let i = 0; i < data.length; i++) {
        const geoCoord = geoCoordMap[data[i].name];
        if (geoCoord) {
          res.push({
            name: data[i].name,
            value: geoCoord.concat(data[i].value),
            imgUrl: data[i].imgUrl
          });
        }
      }
      console.log(res);
      return res;
    }
  }
};
</script>

data.js文件:

export const geoCoordMap = {
  '位置1': [90.9180416971, 41.0807155340],
  '位置2': [123.4965120599, 51.0206466741],
  '位置3': [100.4728967514, 26.1734892363],
  '位置4': [121.5121844054, 31.2106872661],
  '位置5': [111.50148, 31.2458353752],
  '位置6': [111.50148, 24.2458353752]
};

export const datas = [
  {name: '位置1', value: 19, imgUrl: ['http://localhost:8030/ad.jpg', 'http://localhost:8030/ad.jpg']},
  {name: '位置2', value: 20, imgUrl: ['http://localhost:8030/ad.jpg', 'http://localhost:8030/ad.jpg']},
  {name: '位置3', value: 32, imgUrl: ['http://localhost:8030/ad.jpg', 'http://localhost:8030/ad.jpg']},
  {name: '位置4', value: 24, imgUrl: ['http://localhost:8030/ad.jpg', 'http://localhost:8030/ad.jpg']},
  {name: '位置5', value: 46, imgUrl: ['http://localhost:8030/ad.jpg', 'http://localhost:8030/ad.jpg']},
  {name: '位置6', value: 30, imgUrl: ['http://localhost:8030/ad.jpg', 'http://localhost:8030/ad.jpg']}
];

展示效果如下:
在这里插入图片描述

参考 https://segmentfault.com/a/1190000016024960