51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

Echarts中国地图下钻,支持下钻到县(vue3)

引言

Echarts 大家都不陌生吧,时常被用于绘制各种图表,也作为大屏可视化的常驻用户,这里就不多说了,今天主要是讲述一下 Echarts 的地图下钻,支持下钻到县、返回上一级。

准备工作

地图JSON数据

DataV.GeoAtlas地理小工具系列 (aliyun.com) 支持在线调用API和下载json资源(我这里是调用的API)

如果地图json API请求报错403,可参考这个解决办法 :地图请求阿里的geojson数据时,返回403Forbidden解决方案 - 掘金 (juejin.cn)

技术栈

  • vue: 3.3.7

  • vue-echarts: 6.6.1 (直接使用 Echarts 也是一样的,这个只是对 Echarts 的组件封装)

  • vite: 4.5.0

地图效果

**项目预览地址:**UnusualAdmin

**项目代码地址:**UnusualAdmin

实现

template

这里只需要一个 Echarts 节点和一个按钮就行了

<template>
  <div :style="`height: ${calcHeight('main')};`" class="wh-full pos-relative">
    <v-chart :option="mapOption" :autoresize="true" @click="handleClick" />
    <n-button v-show="isShowBack" class="pos-absolute top-10 left-10" @click="goBack">返回</n-button>
  </div>
</template>

获取mapJson

// 使用线上API
const getMapJson = async (mapName: string) => {
  const url = `https://geo.datav.aliyun.com/areas_v3/bound/${mapName}.json`
  const mapJson = await fetch(url).then(res => res.json())
  return mapJson
}

// 使用本地资源 const getMapJson = async (mapName: string) => {   const url = @/assets/mapJson/${mapName}.json   const mapJson = await import(/* @vite-ignore */ url)   return mapJson }

第二种方法(使用本地资源)存在问题:这个方法后续发现,vite打包不会把json文件打包到dist,线上会报错,目前没找到可靠的解决办法(如果放到public文件夹下会打包进去),故舍弃。

如果大家有什么解决这个问题的好办法,请在评论区留言,博主会一一去尝试的???

更新地图配置options

const setOptions = (mapName: string, mapData: any) => {
  return {
     // 鼠标悬浮提示
    tooltip: {
      show: true,
      formatter: function (params: any) {
        // 根据需要进行数据处理或格式化操作
        if (params && params.data) {
          const { adcode, name, data } = params.data;
          // 返回自定义的tooltip内容
          return `adcode: ${adcode}<br>name: ${name}<br>data: ${data}`;
        }
      },
    },
    // 左下角的数据颜色条
    visualMap: {
      show: true,
      min: 0,
      max: 100,
      left: 'left',
      top: 'bottom',
      text: ['高', '低'], // 文本,默认为数值文本
      calculable: true,
      seriesIndex: [0],
      inRange: {
        color: ['#00467F', '#A5CC82'] // 蓝绿
      }
    },
    // geo地图
    geo: {
      map: mapName,
      roam: true,
      select: false,
      // 图形上的文本标签,可用于说明图形的一些数据信息,比如值,名称等。
      selectedMode: 'single',
      label: {
        show: true
      },
      emphasis: {
        itemStyle: {
          areaColor: '#389BB7',
          borderColor: '#389BB7',
          borderWidth: 0
        },
        label: {
          fontSize: 14,
        },
      }
    },
    series: [
      // 地图数据
      {
        type: 'map',
        map: mapName,
        roam: true,
        geoIndex: 0,
        select: false,
        data: mapData
      },
      // 散点
      {
        name: '散点',
        type: 'scatter',
        coordinateSystem: 'geo',
        data: mapData,
        itemStyle: {
          color: '#05C3F9'
        }
      },
      // 气泡点
      {
        name: '点',
        type: 'scatter',
        coordinateSystem: 'geo',
        symbol: 'pin', //气泡
        symbolSize: function (val: any) {
          if (val) {
            return val[2] / 4 + 20;
          }
        },
        label: {
          show: true,
          formatter: function (params: any) {
            return params.data.data || 0;
          },
          color: '#fff',
          fontSize: 9,
        },
        itemStyle: {
          color: '#F62157', //标志颜色
        },
        zlevel: 6,
        data: mapData,
      },
      // 地图标点
      {
        name: 'Top 5',
        type: 'effectScatter',
        coordinateSystem: 'geo',
        data: mapData.map((item: { data: number }) => {
          if (item.data > 60) return item
        }),
        symbolSize: 15,
        showEffectOn: 'render',
        rippleEffect: {
          brushType: 'stroke'
        },
        label: {
          formatter: '{b}',
          position: 'right',
          show: true
        },
        itemStyle: {
          color: 'yellow',
          shadowBlur: 10,
          shadowColor: 'yellow'
        },
        zlevel: 1
      },
    ]
  }
}

渲染地图

const renderMapEcharts = async (mapName: string) => {
  const mapJson = await getMapJson(mapName)
  registerMap(mapName, mapJson); // 注册地图
  // 为地图生成一些随机数据
  const mapdata = mapJson.features.map((item: { properties: any }) => {
    const data = (Math.random() * 80 + 20).toFixed(0) // 20-80随机数
    const tempValue = item.properties.center ? [...item.properties.center, data] : item.properties.center
    return {
      name: item.properties.name,
      value: tempValue, // 中心点经纬度
      adcode: item.properties.adcode, // 区域编码
      level: item.properties.level, // 层级
      data // 模拟数据
    }
  });
  // 更新地图options
  mapOption.value = setOptions(mapName, mapdata)
}

实现地图点击下钻

// 点击下砖
const mapList = ref<string[]>([]) // 记录地图
const handleClick = (param: any) => {
  // 只有点击地图才触发
  if (param.seriesType !== 'map') return
  const { adcode, level } = param.data
  const mapName = level === 'district' ? adcode : adcode + '_full'
  // 防止最后一个层级被重复点击,返回上一级出错
  if (mapList.value[mapList.value.length - 1] === mapName) {
    return notification.warning({ content: '已经是最下层了', duration: 1000 })
  }
  // 每次下转都记录下地图的name,在返回的时候使用
  mapList.value.push(mapName)
  renderMapEcharts(mapName)
}

返回上一级实现

// 点击返回上一级地图
const goBack = () => {
  const mapName = mapList.value[mapList.value.length - 2] || '100000_full'
  mapList.value.pop()
  renderMapEcharts(mapName)
}

全部代码

<template>
  <div :style="`height: ${calcHeight('main')};`" class="wh-full pos-relative">
    <v-chart :option="mapOption" :autoresize="true" @click="handleClick" />
    <n-button v-show="isShowBack" class="pos-absolute top-10 left-10" @click="goBack">返回</n-button>
  </div>
</template>

<script setup lang="ts" name="EchartsMap"> import { use, registerMap } from 'echarts/core' import VChart from 'vue-echarts' import { CanvasRenderer } from 'echarts/renderers' import { MapChart, ScatterChart, EffectScatterChart } from 'echarts/charts' import { TitleComponent, TooltipComponent, LegendComponent, GridComponent, VisualMapComponent } from 'echarts/components' import { calcHeight } from '@/utils/help';

use([   CanvasRenderer,   TitleComponent,   TooltipComponent,   LegendComponent,   GridComponent,   VisualMapComponent,   MapChart,   ScatterChart,   EffectScatterChart ])

const notification = useNotification() const mapOption = ref() const mapList = ref<string[]>([]) // 记录地图 const isShowBack = computed(() => {   return mapList.value.length !== 0 })

const getMapJson = async (mapName: string) => {   const url = https://geo.datav.aliyun.com/areas_v3/bound/${mapName}.json   const mapJson = await fetch(url).then(res => res.json())   return mapJson }

const setOptions = (mapName: string, mapData: any) => {   return {     tooltip: {       show: true,       formatter: function (params: any) {         // 根据需要进行数据处理或格式化操作         if (params && params.data) {           const { adcode, name, data } = params.data;           // 返回自定义的tooltip内容           return adcode: ${adcode}&lt;br&gt;name: ${name}&lt;br&gt;data: ${data};         }       },     },     visualMap: {       show: true,       min: 0,       max: 100,       left: 'left',       top: 'bottom',       text: ['高', '低'], // 文本,默认为数值文本       calculable: true,       seriesIndex: [0],       inRange: {         color: ['#00467F', '#A5CC82'] // 蓝绿       }     },     geo: {       map: mapName,       roam: true,       select: false,       // zoom: 1.6,       // layoutCenter: ['45%', '70%'],       // layoutSize: 750,       // 图形上的文本标签,可用于说明图形的一些数据信息,比如值,名称等。       selectedMode: 'single',       label: {         show: true       },       emphasis: {         itemStyle: {           areaColor: '#389BB7',           borderColor: '#389BB7',           borderWidth: 0         },         label: {           fontSize: 14,         },       }     },     series: [       // 数据       {         type: 'map',         map: mapName,         roam: true,         geoIndex: 0,         select: false,         data: mapData       },       {         name: '散点',         type: 'scatter',         coordinateSystem: 'geo',         data: mapData,         itemStyle: {           color: '#05C3F9'         }       },       {         name: '点',         type: 'scatter',         coordinateSystem: 'geo',         symbol: 'pin', //气泡         symbolSize: function (val: any) {           if (val) {             return val[2] / 4 + 20;           }         },         label: {           show: true,           formatter: function (params: any) {             return params.data.data || 0;           },           color: '#fff',           fontSize: 9,         },         itemStyle: {           color: '#F62157', //标志颜色         },         zlevel: 6,         data: mapData,       },       {         name: 'Top 5',         type: 'effectScatter',         coordinateSystem: 'geo',         data: mapData.map((item: { data: number }) => {           if (item.data > 60) return item         }),         symbolSize: 15,         showEffectOn: 'render',         rippleEffect: {           brushType: 'stroke'         },         label: {           formatter: '{b}',           position: 'right',           show: true         },         itemStyle: {           color: 'yellow',           shadowBlur: 10,           shadowColor: 'yellow'         },         zlevel: 1       },     ]   } }

const renderMapEcharts = async (mapName: string) => {   const mapJson = await getMapJson(mapName)   registerMap(mapName, mapJson);   const mapdata = mapJson.features.map((item: { properties: any }) => {     const data = (Math.random() * 80 + 20).toFixed(0) // 20-80随机数     const tempValue = item.properties.center ? [...item.properties.center, data] : item.properties.center     return {       name: item.properties.name,       value: tempValue, // 中心点经纬度       adcode: item.properties.adcode, // 区域编码       level: item.properties.level, // 层级       data // 模拟数据     }   });   mapOption.value = setOptions(mapName, mapdata) }

renderMapEcharts('100000_full') // 初始化绘制中国地图

// 点击下砖 const handleClick = (param: any) => {   // 只有点击地图才触发   if (param.seriesType !== 'map') return   const { adcode, level } = param.data   const mapName = level === 'district' ? adcode : adcode + '_full'   // 防止最后一个层级被重复点击,返回上一级出错   if (mapList.value[mapList.value.length - 1] === mapName) {     return notification.warning({ content: '已经是最下层了', duration: 1000 })   }   mapList.value.push(mapName)   renderMapEcharts(mapName) }

// 点击返回上一级地图 const goBack = () => {   const mapName = mapList.value[mapList.value.length - 2] || '100000_full'   mapList.value.pop()   renderMapEcharts(mapName) } </script>

博客主要记录一些学习的文章,如有不足,望大家指出,谢谢。

赞(4)
未经允许不得转载:工具盒子 » Echarts中国地图下钻,支持下钻到县(vue3)