ECharts实现可视化全国地图省市县(区)区域下钻

19,011 阅读5分钟

大家好呀,我是一个前端小倒霉蛋wangly。

近期一直忙于业务开发,和复习知识点,就没有更新文章。刚好最近业务上做了一个地图的下钻和原点图。就将它写成一篇文章吧。与诸君共勉。当然,如果最近的项目中需要制作这个功能。也可以直接clone代码。根据自己的需求实现相应功能。

ECharts的地图json文件可以在Github中找到。在项目地址也可以直接复制哦。

这次就以一个省市区下钻的案例来做一下吧。也是我写在自己的新项目里面的。还没有进行美化。项目是vue开发的,React,JQuery...等可以按照思路参考。全国地图只需要多处理一层配置就好了。文章看下来。我想应该能明白原理

流程图和基本效果(未扩展)

流程图看上去是这样的。当ECharts容器初始化后,对地图进行注册。按照自己需要加载注册不同的组件。随后设置OptionConfig。这样,地图就会在你定义的DOM容器中展示出来了。

流程
流程
效果
效果

初始化容器

在模板中添加一个id名为map的div标签作为ECharts的初始化容器。

<template>
  <div class="home">
    <a-breadcrumb>
      <a-breadcrumb-item
        v-for="[key, value] in hashMap" :key="key">
        <span @click="readyMap(key, value)">{{value.name}}</span>
      </a-breadcrumb-item>
    </a-breadcrumb>
    <!-- map容器 -->
    <div id="map" style="width: 500px;height: 500px;"></div>
  </div>
</template>

随后,在mounted声明周期中初始化map容器,这里不论是用ref还是document按照自己的风格来获取DOM

mounted () {
  this.$nextTick(() => {
    this.cityMap = echarts.init(document.getElementById('map'))
    this.registerMap()
  })
}

在初识化后面,跟随了一个registerMap的方法,主要是用来注册并且绘制地图的。那么来看下它做了什么吧。

绘制地图

registerMap 听起来名称是有点误会的。也是我想的不周到,其实它相当于一个main,相当于一个方法容器,如下图,将绘制一系列地图的拆分成为一个模块,其实是比较好理解的。而registerMap的作用就是将这些module methods给加载进来,可以看到rendHEAD方法,这个其实是绘制主地图的方法,本文用某省作为HEAD头层,那么它们的关系看上去就是一个单向链表,一节一节的将下级城市推演出来。那么,一起看看是如何绘制它们的吧?

  • rendHEAD 绘制省级地图
  • rendCity 绘制市区地图
  • Huizhi
registerMap () {
  console.log('开始注册地图')
  // 加载头部地图
  this.rendHEAD()
  // 点击事件
  this.cityMap.on('click', $mod => {
    const { name, value } = $mod.data
    switch ($mod.data.zoom) {
      case 1:
        this.rendCity(name, value)
        this.setHashMap($mod.data.zoom + 1, name, value)
        break
      case 2: {
        const oneMap = {
          type: 'FeatureCollection',
          features: [$mod.data.data],
          parent: value,
          zoom: 3
        }
        echarts.registerMap(name, oneMap)
        this.renderMap(name, [{
          name: name,
          parent: value,
          zoom: 3
        }])
        this.setHashMap($mod.data.zoom + 1, name, value)
      } break
      case 3:
        this.rendHEAD()
        this.clearHashMap()
        break
    }
  })
}

绘制头层(省级)下的城市

JiangxiMap是我们通过require引入进来的JSON,这个可以根据自己喜好来决定是用ajax请求还是通过模块加载都可以。只需要将json中的文件内容拿到就好了。JSON中的features记录了需要的下级市信息,通过一个循环,将城市名称内容都集中到一个数组,它最终将作为EChartsdata来显示,可以自定义你需要的数据。为将来点击事件做处理。然后将ECharts.registerMap来注册地图信息,最后通过renderMap方法进行地图展示。

rendHEAD () {
  const mapName = []
  JiangxiMap.features.forEach(city => {
    // 城市名称信息
    mapName.push({
      // 名称
      name: city.properties.name,
      // 城市编码,对应文件名
      value: city.id,
      // 层级
      zoom: 1
    })
  })
  // 注册
  echarts.registerMap('江西省', JiangxiMap)
  // 加载
  this.renderMap('江西省', mapName)
}

绘市级内容

在绘制省级内容的时候,传入了一个mapName的配置对象数组。当点击ECharts地图区域时,它会返回一个当前市的对象出来。如图$mod.data就是传入进行的数据。参考上面,很快就理解了这个数据到底是什么。根据value去导入或请求当前市的数配置文件,结构和绘制省是一样。一样的流。遍历features后这次拿到的是当前市下所有的县区资料。这里注意,将遍历出来的整个箱放在countyName[data]中,让它带入到各自的县中去。为什么这么做下面绘制县区会做解释。照葫芦画瓢一样的。

// 绘制市级别地图
rendCity (name, value) {
  // name代表的是该地区中文名称
  // value 代表行政编号,如:3600000
  const fileName = value
  const cityData = require(`@/plugin/map/city/${fileName}.json`)
  echarts.registerMap(name, cityData)
  // 县区名称配置
  const countyName = []
  cityData.features.forEach(county => {
    countyName.push({
      name: county.properties.name,
      value: null,
      data: county,
      parent: value,
      zoom: 2
    })
  })
  // 加载地图
  this.renderMap(name, countyName)
},

绘制县区内容

因为县区内容的独立性。代表着,县区是没有下级的。因此就不需要配置文件展现下级。上个市区绘制时,在Echarts的 data中直接放入一个城市数据。所以可以用来伪造成一个块就可以单独展示了。也就是在市的基础上,抛弃掉非进入县区的地图。 将点击地图县的时候,上一层就注入的data此时就是当前县区的地图配置。但是因为没有节点,所以它不能作为json直接使用注册,z而是做了一层蒙版。可以看到oneMap,相当于给它套了个壳,当等于:

  • 市级别地图展现的内容:['县1', '县2', '区1']
  • 套模板后显示的内容:['当前县']

通过包装,在通过渲染方法,可以得到县的地图展示。

// 生成单独的县区数据,
const oneMap = {
  type: 'FeatureCollection',
  features: [$mod.data.data],
  value: null,
  parent: value,
  zoom: 3
}
// 注册
echarts.registerMap(name, oneMap)
// 加载
this.renderMap(name, [{
  name: name,
  parent: value,
  zoom: 3
}])

rendMap加载方法

默认的option中,是一个固定的内容。主要是绘制功能。而rendMap中则是对图的表述。例如文本内容,样式,展现的一些东西。传入了mapName,cityNameList两个参数。通过传入的内容,a生成不一样的地图片段。这样初步的绘制流程就绘制完成了。下面是要对点击效果做一些功能。

  • mapName: 注册地图的显示名称
  • cityNameList: 注册地图的数据,给optionsData的值
// 运行地图
renderMap(mapName, cityNameList) {
  option.series = [{
    name: mapName,
    // 地图类型
    type: 'map',
    mapType: mapName,
    nameMap: mapName,
    label: {
      normal: {
        show: true,
        textStyle: {
          color: '#303753',
          fontSize: 13
        }
      },
      emphasis: {
        show: true,
        textStyle: {
          color: '#2a8ab3',
          fontSize: 16
        }
      }
    },
    // 加载值
    data: cityNameList,
    itemStyle: {
      normal: {
        areaColor: 'rgb(78, 163, 151)',
        borderWidth: 2,
        borderColor: 'rgb(34, 195, 170)',
        label: {
          show: true,
          textStyle: {
            color: 'rgb(0, 152, 217)',
            fontSize: 12
          }
        }
      },
      emphasis: {
        areaColor: '#0f6471',
        label: {
          show: true,
          textStyle: {
            color: '#fff',
            fontSize: 15
          }
        }
      }
    }
  }]
  this.cityMap.setOption(option)
}

点击切换城市

还记得在自定义添加的data数据中。我存放了一个zoom吗。以下我通过zoom做了一个点击事件的区分。通过zoom我用switch选择了不同层级的点击事件操作。如果是全国那么就从 1->2->3 变为了 1->2->3->4层次。理论上都是照葫芦画瓢。

this.cityMap.on('click', $mod => {
  const {
    name,
    value
  } = $mod.data
  switch ($mod.data.zoom) {
    case 1:
      this.rendCity(name, value)
      this.setHashMap($mod.data.zoom + 1, name, value)
      break
    case 2: {
      const oneMap = {
        type: 'FeatureCollection',
        features: [$mod.data.data],
        value: null,
        parent: value,
        zoom: 3
      }
      echarts.registerMap(name, oneMap)
      this.renderMap(name, [{
        name: name,
        parent: value,
        zoom: 3
      }])
      this.setHashMap($mod.data.zoom + 1, name, value)
    }
    break
  case 3:
    this.rendHEAD()
    this.clearHashMap()
    break
  }
})

堆栈信息

这里除了做顶部的面包屑之外。也是对应了业务功能。我就不过多赘述了。原理很简单。Map的值不能重复,那么key为层级。value为内容。将zoom层次的信息做一个堆栈。返回时将下面元素清除。当你一层层打开地图的时候,堆栈就是下图这样的。当堆栈中某一个层级抽离时,会将优先比它先出去的元素给剔除。达到一个不可逆的进行时。具体可以看源码中下图两个方法。非常简单。

源码相关地址

Vue源码: 点击前往

map文件: 点击前往

UI组件库求star

elegance-ui项目地址:点击前往

总结

深夜写完这篇文章,突然有些困乏。看着窗外的小雨。想到了自己。不就像空中的雨滴一样无根漂泊吗。可能也只有写点文章,才能给自己平淡的生活增加点乐趣吧。最近复习有点心累。如果想要绘制全国的下钻也时一样的思路,无非就是HEAD换成了全国地图罢了。多加了一个节点。整体的逻辑是不变的

如果本篇文章对你有帮助,不妨点个赞给我升三级吧。