当前位置: 首页 > news >正文

vue3+echarts绘制某省区县地图

vue3+echarts绘制某省区县地图

工作中经常需要画各种各样的图,echarts是使用最多的工具,接近春节,想把之前画的echarts图做一个整合,方便同事和自己随时使用,因此用vue3专门写了个web项目,考虑之后不断完善

其中有这么个需求,需要展示某省各区县的数据,写在vue3项目中,最终展示结果如下:

image-20240125173609220

大体的思路如下:

  1. 在阿里云dataV数据可视化平台获取数据
  2. 整合某省各区县的数据成为一个单独的文件
  3. echarts中注册这个省的地图
  4. echarts画图

主要用的程序语言是JavaScript和Python

下面详细介绍,有些技术细节也是自己经常遇到的,通过这段时间强化训练,感觉对echarts越来越熟练了

一、阿里云dataV地图数据获取

首先上地址,阿里云数据可视化平台,感谢阿里和高德提供如此牛逼的工具

然后选择点击自己所需的省份,比如上面图示的河北

image-20240125174337829

接下来依次点击河北省各地级市,比如我点了石家庄,此时右侧出现了一个json链接,如下图,复制那个链接

image-20240125174601709

如果浏览器装了解析json文件的插件,就会显示这个json文件的数据,如果安装插件,应该会直接把这个json文件下载下来,json数据如下:

image-20240125174758844

接下来,依次去点击河北其他城市的地图,并获取数据,也可以写个爬虫的程序,挺简单的

二、将各地市的数据,整合成一个省的数据

其实思路就是把单个json中的features提取出来,然后整合到一个json文件中去

把上一步下载好的所有文件放到一个目录中,如folder

接下来用Python处理一下

    features = []  # 初始化 features 列表for file in os.listdir(folder):filename = os.path.join(folder, file)try:with open(filename, 'r', encoding='utf-8') as f:data = json.load(f)features.extend(data.get('features', []))except (IOError, json.JSONDecodeError) as e:print(f"Error reading JSON file {filename}: {e}")json_file = {"type": "FeatureCollection","features": features    }# 导出为 JSON 文件output_file_path = 'hebei_combined.json'with open(output_file_path, 'w', encoding='utf-8') as output_file:json.dump(json_file, output_file, ensure_ascii=False, indent=2)

通过处理后,得到的数据样式如下:

image-20240125175455169

我不太喜欢在vue项目中直接使用json,因为很多情况下都需要异步引入,对于没有后端的项目,写起来比较费劲。更为致命的是,echarts对各种异步的操作非常不友好,经常在等待数据的时候,发现数据还没有返回,就会各种报错。我更倾向于把数据写入到js文件中,然后对外暴露,实际上这个项目我也是这么操作的,我把json里的内容放进同名js文件中,然后按需向外暴露,对象名为hebeiAreas

三、echarts注册地图

echarts中注册地图非常简单,就两步:

  1. 导入地图数据
  2. 注册

体现在程序中如下:

import * as echarts from "echarts";
import { hebeiAreas } from '@/assets/js/areasOfProvince/hebei_combined'
// 注册
echarts.registerMap('proMap', hebeiAreas)

这里的注册需要写在正确的地方,如果只画一个图,写在哪里都无所谓,如果涉及到多个省份的切换,我建议写在切换成功的地方,或者是重绘地图的地方

四、绘图以及踏过的坑

绘图就是正儿八经写代码,我先上完整代码

<template><div class="container"><div class="top"><el-select v-model="province" placeholder="请选择省份" @change="choosePro" style="width: 120px"><el-option v-for="(item, index) in provinces" :key="index" :label="item.label":value="item.value"></el-option></el-select><el-button type="primary" style="margin-left: 10px;" @click="changeData">更换数据</el-button><input ref="input" type="file" style="display: none" @change="handleFileChange" /></div><div class="proMap" ref="proMap"></div></div>
</template><script setup>
import { ref, onMounted, h } from 'vue'
import { provinces } from './data/provinceName'
import { ElMessage, ElNotification } from 'element-plus'
import * as echarts from "echarts";
import { areas } from '@/assets/js/areas'
import { hebeiAreas } from '@/assets/js/areasOfProvince/hebei_combined'const province = ref('hebei')
const provinceZH = ref('河北')
const provinceCode = ref('13')
const proMap = ref()const drawData = ref([])
const maxData = ref(100)const getMaxData = () => {const arr = []arr.push(drawData.value.map(item => item.value))maxData.value = Math.max(...arr[0])
}const getData = () => {const areasOfCurrentProvince = areas.filter(item => item.provinceCode == provinceCode.value)areasOfCurrentProvince.forEach(item => {drawData.value.push({name: item.name,value: Math.floor(Math.random() * 101)})});getMaxData()
}// 更换省份
const choosePro = () => {console.log(province.value)if (province.value != 'hebei') {ElNotification({title: '提醒',message: h('i', { style: 'color: teal' }, '省份到区县分块需要处理大量数据,功能待后期完成,现在只做了河北的'),duration: 0})province.value = 'hebei'}
}
// 更换数据
// 隐藏输入框的dom
const input = ref()
const changeData = () => {ElNotification({title: '提醒',message: h('i', { style: 'color: teal' }, '请务必使用当前省份下的区县数据,否则无法显示正确的数据'),duration: 0})input.value.click()
}
const handleFileChange = async event => {const file = event.target.files[0]const reader = new FileReader()reader.readAsText(file, "UTF-8")reader.onload = async (evt) => {const fileString = await evt.target.resultconst count = fileString.trim().split('\n').lengthconsole.log(count)const handleData = []for (let i = 0; i < count; i++) {const fileline = fileString.split("\n")[i].split('\t')handleData.push({ name: fileline[0], value: parseInt(fileline[1]) })}// 更换数据drawData.value = handleDatagetMaxData()drawProMap()}
}
// 画地图相关
let initMap
const drawProMap = () => {echarts.registerMap('proMap', hebeiAreas)if (initMap != null && initMap != "" && initMap != undefined) {initMap.dispose(); //销毁}initMap = echarts.init(proMap.value)initMap.setOption({backgroundColor: "transparent", // 设置背景色透明tooltip: {show: true,},visualMap: {text: ["", ""],showLabel: true,left: "200",bottom: "100",min: 0,max: maxData.value,inRange: {color: ["#edfbfb", "#b7d6f3", "#40a9ed", "#3598c1", "#215096"],},// splitNumber: 5,seriesIndex: "0",},series: [{type: "map",map: 'proMap',tooltip: {trigger: 'item',formatter: function (params) {// params 包含了鼠标悬浮时的相关信息return params.name + '<br/>' + '数值: ' + params.value;}},zoom: 1,label: {show: false, // 显示地市名称color: "#000",align: "center",},top: "10%",left: "center",aspectScale: 0.75,roam: true, // 地图缩放和平移itemStyle: {borderColor: "#3ad6ff", // 省分界线颜色  阴影效果的borderWidth: 1,areaColor: "#F5F5F5",opacity: 1,},// 控制鼠标悬浮上去的效果emphasis: {// 聚焦后颜色disabled: false, // 开启高亮label: {align: "center",color: "#ffffff",},itemStyle: {color: "#ffffff",areaColor: "#0075f4", // 阴影效果 鼠标移动上去的颜色},},z: 2,data: drawData.value,}]})window.addEventListener("resize", () => {initMap.resize();});
}onMounted(() => {getData()setTimeout(() => { drawProMap() }, 200)
})
</script><style lang="scss" scoped>
.top {padding: 5px;width: 100%;box-shadow: rgba(17, 17, 26, 0.1) 0px 0px 16px;
}.proMap {height: 95%;width: 95%;
}
</style>

以上代码有自己踏过的不少坑,我都说明一下,肯定还有其他坑,一句话,echarts全是坑

  1. getData()是生成地图对应数据的方法,我这里用了随机数,数据格式如下:

    [{name: '涞源县', value: 100},....
    ]
    

    就是由key为name和value对象组成的数组

    getMaxData()是获取上面数组中的value的最大值,这主要是绘图的时候,图例范围的最大值设置

    drawProMap()是绘制地图的方法

  2. 坑1:注意onMounted钩子中的写法:

    onMounted(() => {getData()setTimeout(() => { drawProMap() }, 200)
    })
    

    挂载组件之前,先要获取数据,然后组件出现,就应该有图出现,这里我设置了0.2s的延时画图,原因是需要先等dom渲染完成后再画图,不然会直接报错

  3. 坑2:画图dom的宽和高必须要先设置,看我的样式:

    .proMap {height: 95%;width: 95%;
    }
    

    这必须写,不然图出不来,还会报警说无法获取dom的宽高

  4. 坑3:地图dom的初始化问题:

    看相关的代码

    // 画地图相关
    let initMap
    const drawProMap = () => {echarts.registerMap('proMap', hebeiAreas)if (initMap != null && initMap != "" && initMap != undefined) {initMap.dispose(); //销毁}initMap = echarts.init(proMap.value)/*省略其他代码*/
    }
    

    一般情况下,可能我们会在画图的时候,直接就是:

    const initMap = echarts.init(proMap.value)
    

    上来就直接初始化画图的dom,可能的情况是,如果是在相同的dom上重绘echarts图,控制台就会报警(并非报错,效果会正常出现),说这个dom上本来就存在echarts图,所以在初始化之前,正确的操作是判断dom上的echarts图是否占用,占用的话,就销毁,也就是initMap.dispose();

  5. 从上面的代码可以看出,省份是可以选择的,数据也是可以改的

    • 先说改数据。改数据的逻辑是设定一个隐藏的input dom元素,为什么要用input,因为input可以打开文件,如下代码,其中type="file"就是打开文件,change是文件改变的事件

      <input ref="input" type="file" style="display: none" @change="handleFileChange" />
      

      由于这个dom是隐藏的(style="display: none"),所以打开文件应该是由按钮来控制,也就是下面这行代码

      <el-button type="primary" style="margin-left: 10px;" @click="changeData">更换数据</el-button>
      

      它俩的逻辑关系是:

      • 点击按钮,隐藏的input按钮实现点击事件,如下代码:

        const input = ref()
        const changeData = () => {ElNotification({title: '提醒',message: h('i', { style: 'color: teal' }, '请务必使用当前省份下的区县数据,否则无法显示正确的数据'),duration: 0})input.value.click()
        }
        
      • 接下来就会触发input的文件打开功能,选定文件后,就会执行handleFileChange方法,在这个方法中,使用了处理txt文本文件的方法,需要注意其中的异步操作,并且有处理换行以及按tab分割的逻辑,这里需要根据个人的项目进行适配,处理好数据后,替换画图的数据即可,然后执行获取最大值和画图方法,相关代码如下:

        const handleFileChange = async event => {const file = event.target.files[0]const reader = new FileReader()reader.readAsText(file, "UTF-8")reader.onload = async (evt) => {const fileString = await evt.target.resultconst count = fileString.trim().split('\n').lengthconsole.log(count)const handleData = []for (let i = 0; i < count; i++) {const fileline = fileString.split("\n")[i].split('\t')handleData.push({ name: fileline[0], value: parseInt(fileline[1]) })}// 更换数据drawData.value = handleDatagetMaxData()drawProMap()}
        }
        
    • 再说切换省份。我的代码中并没有实现切换省份的逻辑,因为需要大量的数据支撑,也就是说要把全国34个省级行政区划(包括港澳台)的地图文件都获取,切换成功后,异步引入地图文件,并注册地图,然后画图,这一步我在其他地方实现过,同样存在坑

      坑4:异步导入json文件,首次绘图会出现报错,报错如下:

      Error: Invalid geoJson format coordinate.charCodeAt is not a function

      但是刷新页面就正常了,查了一下相关的资料,有人解释是:

      因为 echarts 会绘制解析 json 之后 执行 decode 方法 后 会将其 UTF8Encoding 的值 从 true 改为false,第二次绘制 时如果 为 false 则 不需要走 decode 方法,如果每次都是新引入的 json,那每次都走 decode 就会报错
      ————————————————

                              版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
      

      原文链接:https://blog.csdn.net/m0_37805167/article/details/122553278

他建议是 Object.assign({}, json) 拷贝一次,解释是异步获取的数据是只读的,echarts无法更改,所以会报错,需要拷贝一下,确实在某些情况下能解决,但通过路由切换到需要画图的页面上来时,依然会报错,目前还没有找到靠谱的解决方案,可能不用json而是用js会解决这个问题,需要我来确认

相关文章:

vue3+echarts绘制某省区县地图

vue3echarts绘制某省区县地图 工作中经常需要画各种各样的图&#xff0c;echarts是使用最多的工具&#xff0c;接近春节&#xff0c;想把之前画的echarts图做一个整合&#xff0c;方便同事和自己随时使用&#xff0c;因此用vue3专门写了个web项目&#xff0c;考虑之后不断完善…...

MyBatis详解(2)-- mybatis配置文件

MyBatis详解&#xff08;2&#xff09; mybatis配置文件 mybatis配置文件 1.构建SqlSessionFactory的依据。 2.MyBatis最为核心的内容&#xff0c;对MyBatis的使用影响很大。 3.配置文件的层次顺序不能颠倒&#xff0c;一旦颠倒会出现异常。 < c o n f i g u r a t i o n…...

蓝桥杯备战——8.DS1302时钟芯片

1.分析原理图 由上图可以看到&#xff0c;芯片的时钟引脚SCK接到了P17,数据输出输入引脚IO接到P23,复位引脚RST接到P13。 2.查阅DS1302芯片手册 具体细节还需自行翻阅手册&#xff0c;我只截出重点部分 总结&#xff1a;数据在上升沿写出&#xff0c;下降沿读入&#xff0c;…...

freeRTOS / day02

1. 定时器使用流程 1.1 ST32CubeMX 设置 1.1.0 选择定时器 --> TIM1 1.1.1 Clock Source --> Internal Clock 1.1.2 Prescaler --> 预分频系数 1.1.3 Counter Period -- > 重装值 1.1.4 Prescaler 和 Counter Period 计算公式 定时时间&#xff08;s) &…...

Ubuntu 18.04 x86_64 上交叉编译 boost 库(ARMv7L)

先安装 ARMv7L 交叉编译器环境&#xff1a; 在 Ubuntu 18.04 x86_64 上面安装 Linux-ARMv7 A/L GCC编译器-CSDN博客 1、下载 boost 1.84 的源代码访问要编译的目录&#xff0c;并且解压出来源代码&#xff0c;切入源代码根目录 2、./bootstrap.sh --with-librariesfilesyste…...

为什么 FPGA 比 CPU 和 GPU 快?

FPGA、GPU 与 CPU——AI 应用的硬件选择 现场可编程门阵列 (FPGA) 为人工智能 (AI) 应用带来许多优势。图形处理单元 (GPU) 和传统中央处理单元 (CPU) 相比如何&#xff1f; 人工智能&#xff08;AI&#xff09;一词是指能够以类似于人类的方式做出决策的非人类机器智能。这包…...

js常用函数总结

js常用函数总结 1、模糊搜索 export const fuzzyQuery (list, keyWord, attribute name) > {const reg new RegExp(keyWord)const arr []for (let i 0; i < list.length; i) {if (reg.test(list[i][attribute])) {arr.push(list[i])}}return arr } list 原数组 ke…...

cartographer离线建图报错:data_.trajectory_nodes.SizeOfTrajectoryOrZero

cartographer离线建图报错: data_.trajectory_nodes.SizeOfTrajectoryOrZero [FATAL] [1706177325.876019302, 1706015603.398505596]: F0125 18:08:45.000000 17607 pose_graph_2d.cc:1314] Check failed: data_.trajectory_nodes.SizeOfTrajectoryOrZero(trajectory_id) &…...

【YOLO系列算法俯视视角下舰船目标检测】

YOLO系列算法俯视视角下舰船目标检测 数据集和模型YOLO系列算法俯视视角下舰船目标检测YOLO系列算法俯视视角下舰船目标检测可视化结果 数据集和模型 数据和模型下载&#xff1a; YOLOv6俯视视角下舰船目标检测训练好的舰船目标检测模型舰船目标检测数据YOLOv7俯视视角下舰船…...

python进程间使用共享内存multiprocessing.shared_memory来通讯

python多个进程通讯使用共享内存 1、multiprocessing.shared_memory ​ 使用这个模块可从进程直接访问共享内存,该模块提供了一个 SharedMemory 类,用于分配和管理多核或对称多处理器(SMP)机器上进程间的共享内存。 ​ 为了协助管理不同进程间的共享内存生命周期,multi…...

经典目标检测YOLO系列(三)YOLOv3的复现(2)正样本的匹配、损失函数的实现

经典目标检测YOLO系列(三)YOLOv3的复现(2)正样本的匹配、损失函数的实现 我们在之前实现YOLOv2的基础上&#xff0c;加入了多级检测及FPN&#xff0c;快速的实现了YOLOv3的网络架构&#xff0c;并且实现了前向推理过程。 经典目标检测YOLO系列(三)YOLOV3的复现(1)总体网络架构…...

编程笔记 html5cssjs 061 JavaScrip简介

编程笔记 html5&css&js 061 JavaScrip简介 一、JavaScript概述二、JavaScript的主要特点三、历史延革四、JavaScript与前端开发小结 JavaScript 是 web 开发者必学的三种语言之一&#xff1a;HTML 定义网页的内容&#xff1b;CSS 规定网页的布局&#xff1b;JavaScript…...

计算机网络 第5章(运输层)

系列文章目录 计算机网络 第1章&#xff08;概述&#xff09; 计算机网络 第2章&#xff08;物理层&#xff09; 计算机网络 第3章&#xff08;数据链路层&#xff09; 计算机网络 第4章&#xff08;网络层&#xff09; 计算机网络 第5章&#xff08;运输层&#xff09; 计算机…...

pythonSM4加密

数据安全法及密评要求&#xff0c;敏感数据系统需要使用国密算法进行加解密处理。 敏感数使用SM4/ECB加解密方式 #密钥参数epidemic_key #加密信息参数 message #加密算法SM4/ECB/PKCS5Padding #加密类型SM4-ECB #添加模式PKCS5Padding from cryptography.hazmat.primitives.…...

JSP在线阅读系统myeclipse定制开发SQLServer数据库网页模式java编程jdbc

一、源码特点 JSP 小说在线阅读系统是一套完善的web设计系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库 &#xff0c;系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为SQLServer2008&#…...

el-date-picker设置default-time的默认时间

default-time &#xff1a;选择日期后的默认时间值。 如未指定则默认时间值为 00:00:00 默认值修改 <el-form-item label"计划开始时间" style"width: 100%;" prop"planStartTime"><el-date-picker v-model"formData.planStart…...

List集合根据对象某个元素去重

序言 检视代码时有下面这样一段代码&#xff08;已脱敏处理&#xff09;&#xff0c; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors…...

QML Qt4版本移植到Qt5概述

C++代码 在Qt5中,QML应用程序使用OpenGL场景图架构来渲染,而在Qt4中使用的是图形视图框架。这种结构上的变化导致C++接口进行了大量重构。QtDeclarative模块已被弃用,该模块的类被移动到新的QtQML和QtQuick模块中,名称有了变化,如表3-1所列。如果需要使用Qt5中新的QQml和…...

【极数系列】Flink环境搭建Linux版本 (03)

文章目录 引言01 Linux部署JDK11版本1.下载Linux版本的JDK112.创建目录3.上传并解压4.配置环境变量5.刷新环境变量6.检查jdk安装是否成功 02 Linux部署Flink1.18.0版本1.下载Flink1.18.0版本包2.上传压缩包到服务器3.修改flink-config.yaml配置4.启动服务5.浏览器访问6.停止服务…...

2023年深圳市节假日人口迁入数据,shp/excel格式,需要自取!

基本信息. 数据名称: 深圳市节假日人口迁入数据 数据格式: Shp、excel 数据时间: 2023年国庆节 数据几何类型: 线 数据坐标系: WGS84 数据来源&#xff1a;网络公开数据 数据字段&#xff1a; 序号字段名称字段说明1a0928迁入人口占迁入深圳市人口的比值&#xff0…...

Windows10上通过MSYS2编译FFmpeg 6.1.1源码操作步骤

1.从github上clone代码&#xff0c;并切换到n6.1.1版本&#xff1a;clone到D:\DownLoad目录下 git clone https://github.com/FFmpeg/FFmpeg.git git checkout n6.1.1 2.安装MSYS2并编译FFmpeg源码: (1).从https://www.msys2.org/ 下载msys2-x86_64-20240113.exe &#…...

HiveSQL题——用户连续登陆

目录 一、连续登陆 1.1 连续登陆3天以上的用户 0 问题描述 1 数据准备 2 数据分析 3 小结 1.2 每个用户历史至今连续登录的最大天数 0 问题描述 1 数据准备 2 数据分析 3 小结 1.3 每个用户连续登录的最大天数(间断也算) 0 问题描述 1 数据准备 2 数据分析 3 小…...

题解仅供学习使用

...

Linux命令-apt-get命令(Debian Linux发行版中的APT软件包管理工具)

补充说明 apt-get命令 是Debian Linux发行版中的APT软件包管理工具。所有基于Debian的发行都使用这个 包管理系统。deb包可以把一个应用的文件包在一起&#xff0c;大体就如同Windows上的安装文件。 语法 apt-get [OPTION] PACKAGE选项 apt-get install 安装新包 apt-get r…...

matlab appdesigner系列-仪器仪表3-旋钮

旋钮&#xff0c;同过旋转显示特定的值 示例&#xff1a;模拟收音机调频 操作步骤&#xff1a; 1&#xff09;将旋钮、标签按钮拖拽到画布上&#xff0c;将标签文字修改为&#xff1a;欢迎收听&#xff0c;并将旋钮其数值范围改为90-107 2&#xff09;设置旋钮的回调函数 代…...

常见の算法5

位图 一个int类型32字节&#xff0c;可以表示0-31这32个数出没出现过&#xff0c;出现过1没出现0&#xff0c;再扩大一点搞个数组&#xff0c;就可以表示0-1023出没出现过&#xff0c;一个long类型可储存64位 如何把10位组成的数&#xff0c;第四位由1改成零 package class05…...

MYSQL中group by分组查询的用法详解(where和having的区别)!

文章目录 前言一、数据准备二、使用实例1.如何显示每个部门的平均工资和最高工资2.显示每个部门的每种岗位的平均工资和最低工资3.显示平均工资低于2000的部门和它的平均工资4.having 和 where 的区别5.SQL查询中各个关键字的执行先后顺序 前言 在前面的文章中&#xff0c;我们…...

架构篇25:高可用存储架构-双机架构

文章目录 主备复制主从复制双机切换主主复制小结存储高可用方案的本质都是通过将数据复制到多个存储设备,通过数据冗余的方式来实现高可用,其复杂性主要体现在如何应对复制延迟和中断导致的数据不一致问题。因此,对任何一个高可用存储方案,我们需要从以下几个方面去进行思考…...

微信小程序(十五)自定义导航栏

注释很详细&#xff0c;直接上代码 上一篇 新增内容&#xff1a; 1.组件文件夹创建方法 2.自定义组件的配置方法 3.外部修改组件样式&#xff08;关闭样式隔离或传参&#xff09; 创建组件文件夹 如果是手动创建建议注意在json文件声明&#xff1a; mynav.json {//声明为组件可…...

Python3进行pdf文件分割及转word

今天有个pdf分割的需求&#xff0c;电脑装的Python3&#xff0c;网上查资料都是Python2的代码&#xff0c;所以整理一份3的 安装&#xff1a; pip install PyPDF2 import PyPDF2def funSplitPdf():pdf_file open(/path/fileName.pdf, rb)pdf_reader PyPDF2.PdfReader(pdf_fi…...