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会解决这个问题,需要我来确认

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/246873.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

STM正点mini-新建工程模板,GPIO及寄存器(介绍)

一.新建工程模板(基于固件库) 1.1库函数与寄存器的区别 这里的启动文件都是根据容量来进行区分的 对MDK而言即使include了&#xff0c;也不知道在哪里找头文件 STM32F10X_HD,USE_STDPERIPH_DRIVER 二.新建工程模板(基于寄存器) 上面的大部分配置与固件库的一样 具体可以看手…

第5章 (python深度学习——波斯美女)

第5章 深度学习用于计算机视觉 本章包括以下内容&#xff1a; 理解卷积神经网络&#xff08;convnet&#xff09; 使用数据增强来降低过拟合 使用预训练的卷积神经网络进行特征提取 微调预训练的卷积神经网络 将卷积神经网络学到的内容及其如何做出分类决策可视化 本章将…

【Linux】开始使用 vim 吧!!!

Linux 1 what is vim &#xff1f;2 vim基本概念3 vim的基本操作 &#xff01;3.1 vim的快捷方式3.1.1 复制与粘贴3.1.2 撤销与剪切3.1.3 字符操作 3.2 vim的光标操作3.3 vim的文件操作 总结Thanks♪(&#xff65;ω&#xff65;)&#xff89;感谢阅读下一篇文章见&#xff01;…

成熟的内外网数据交换方案,如何实现跨网传输?

网络迅速发展&#xff0c;我们可以从网络上查找到各式各样的信息&#xff0c;但是同时网络安全问题也随之严重。近几年&#xff0c;各种有关网络安全的新闻不断被报道&#xff0c;数据泄露给很多企业带来了严重打击&#xff0c;不仅是经济损失&#xff0c;严重者还会对企业的声…

SharedPreferences卡顿分析

SP的使用及存在的问题 SharedPreferences(以下简称SP)是Android本地存储的一种方式&#xff0c;是以key-value的形式存储在/data/data/项目包名/shared_prefs/sp_name.xml里&#xff0c;SP的使用示例及源码解析参见&#xff1a;Android本地存储之SharedPreferences源码解析。以…

STM32 PWM驱动设计

单片机学习&#xff01; 目录 文章目录 前言 一、PWM驱动配置步骤 二、代码示例及注意事项 2.1 RCC开启时钟 2.2 配置时基单元 2.3 配置输出比较单元 2.4 配置GPIO 2.5 运行控制 三、PWM周期和占空比计算 总结 前言 PWM本质是利用面积等效原理来改变波形的有效值。 一、PWM驱动…

git安装步骤

安装环境&#xff1a;Windows10 64bit 下载 Git网址 &#xff1a;Git - Downloading Package 版本&#xff1a;Git-2.21.0-64-bit 第一步&#xff1a;双击下载后的Git-2.21.0-64-bit.exe&#xff0c;开始安装 安装开始 第二步&#xff1a;选择安装路径&#xff0c;点击[next]…

2024年美赛数学建模思路 - 案例:退火算法

文章目录 1 退火算法原理1.1 物理背景1.2 背后的数学模型 2 退火算法实现2.1 算法流程2.2算法实现 建模资料 ## 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 退火算法原理 1.1 物理背景 在热力学上&a…

机器学习之pandas库学习

这里写目录标题 pandas介绍pandas核心数据结构SeriesDataFrameDataFrame的创建列访问列添加列删除行访问行添加行删除数据修改 pandas介绍 pandas是基于NumPy 的一种工具&#xff0c;该工具是为了解决数据分析任务而创建的。Pandas 纳入 了大量库和一些标准的数据模型&#xff…

通过Android Logcat分析firebase崩溃

参考&#xff1a;UnityIL2CPP包Crash闪退利用Android Logcat还原符号表堆栈日志 - 简书 一、安装Android Logcat插件 1、新建空白unity工程&#xff0c;打开PackageManager窗口&#xff0c;菜单栏Window/PackageManager 2、PackageManager中安装Android Logcat日志工具 3、安…

橘子学Mybatis08之Mybatis关于一级缓存的使用和适配器设计模式

前面我们说了mybatis的缓存设计体系&#xff0c;这里我们来正式看一下这玩意到底是咋个用法。 首先我们是知道的&#xff0c;Mybatis中存在两级缓存。分别是一级缓存(会话级)&#xff0c;和二级缓存(全局级)。 下面我们就来看看这两级缓存。 一、准备工作 1、准备数据库 在此之…

【docker】linux系统docker的安装及使用

一、docker应用的安装 1.1 安装方式 Docker的自动化安装&#xff0c;即使用提供的一键安装的脚本&#xff0c;进行安装。 官方的一键安装方式&#xff1a;curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun 国内 daocloud一键安装命令&#xff1a;curl -s…

【Web】小白也能做的RWCTF体验赛baby题部分wp

遇到不会的题&#xff0c;怎么办&#xff01;有的师傅告诉你完了&#xff0c;废了&#xff0c;寄了&#xff01;只有Z3告诉你&#xff0c;稳辣&#xff01;稳辣&#xff01;都稳辣&#xff01; 这种CVE复现的题型&#xff0c;不可能要求选手从0到1进行0day挖掘&#xff0c;其实…

Django介绍

一、介绍 Django是Python语言中的一个Web框架&#xff0c;Python语言中主流的web框架有Django、Tornado、Flask 等多种 优势&#xff1a;大而全&#xff0c;框架本身集成了ORM、模型绑定、模板引擎、缓存、Session等功能&#xff0c;是一个全能型框架&#xff0c;拥有自己的A…

C#,获取与设置Windows背景图片的源代码

为了满足孩子们个性化桌面的需求。 这里发布获取与设置Windows背景图片的源代码。 1 文本格式 using System; using System.IO; using System.Data; using System.Linq; using System.Text; using System.Drawing; using System.Collections; using System.Collections.Gene…

【MATLAB第94期】#源码分享 | 基于MATLAB的广义加性模型多输入单输出回归预测模型(至少2021a版本)

【MATLAB第94期】#源码分享 | 基于MATLAB的广义加性模型多输入单输出回归预测模型&#xff08;至少2021a版本&#xff09; 参考链接&#xff1a; 一、代码展示 %% 清空环境变量 warning off % 关闭报警信息 close all % 关闭开启的图窗 clear …

Linux系统——函数与数组

目录 一、函数 1.函数的定义 2.使用函数 3.定义函数的方法 4.函数举例 4.1判断操作系统 4.2判断ip地址 5.查看函数列表 6.删除函数 7.函数返回值——Return 8.函数的作用范围 9.函数传参 10.函数递归 10.1病毒 10.2阶乘 10.2.1 用for循环 10.2.2函数阶乘 10.…

Unity中的协程

这里写目录标题 前言一、 什么是协程&#xff1f;二、如何声明一个协程三、协程的作用四、协程的优缺点优点 缺点五、 应用示例延迟执行渐变效果 六、总结 前言 在学习unity的过程中会遇到“协程”的概念&#xff0c;听到协程我们脑海里应该会想到它 当然不是一个东西&#xf…

基于物联网设计的水稻田智能灌溉系统(STM32+华为云IOT)

一、项目介绍 随着科技的不断发展和人们生活水平的提高&#xff0c;农业生产也逐渐向智能化、高效化的方向发展。水稻作为我国主要的粮食作物之一&#xff0c;其生长过程中的灌溉管理尤为重要。传统的灌溉方式往往依赖于人工观察和控制&#xff0c;不仅效率低下&#xff0c;而…

硬件知识(1) 手机的长焦镜头

#灵感# 手机总是配备好几个镜头&#xff0c;研究一下 目录 手机常配备的摄像头&#xff0c;及效果举例 长焦的焦距 焦距的定义和示图&#xff1a; IPC的焦距和适用场景&#xff1a; 手机常配备的摄像头&#xff0c;及效果举例 以下是小米某个手机的摄像头介绍&#xff1a…