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.mzph.cn/news/649804.shtml

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

相关文章

基于SpringBoot实现策略模式提供系统接口扩展能力

相信我们对策略模式都有耳闻&#xff0c;但是可能不知道它在项目中具体能有什么作用&#xff0c;我们需要在什么场景下才能去尽可能得去使用策略模式。 这里我简单的列出一个我之前在公司做的一个需求&#xff1a;跟第三方oa系统对接接口&#xff0c;对方需要回调我们当前系统…

R12.2 EBS 修改 APPS 密码 详细步骤

目录 前言准备修改步骤1.关闭应用层2.FNDCPASS 修改密码3. 运行 autoconfig4.单独启动 webLogic 服务5.登录weblogic&#xff0c;更新apps密码6.启动应用层7.验证 结尾 前言 本文的目的是修改 apps 密码&#xff0c;主要参考官方文档 metalink 1674462.1&#xff0c;请注意本文…

代码随想录算法训练营Day|层序遍历,翻转二叉树,对称二叉树

层序遍历 层序遍历一个二叉树&#xff0c;就是从左往右一层一层的遍历二叉树&#xff0c;这种遍历方式需要借用一个辅助数据结构即队列来实现&#xff0c;队列先进先出&#xff0c;符合一层一层遍历的逻辑&#xff0c;而用栈先进后出适合模拟深度优先遍历也就是递归的逻辑。而…

Task04:DDPG、TD3算法

本篇博客是本人参加Datawhale组队学习第四次任务的笔记 【教程地址】https://github.com/datawhalechina/joyrl-book 【强化学习库JoyRL】https://github.com/datawhalechina/joyrl/tree/main 【JoyRL开发周报】 https://datawhale.feishu.cn/docx/OM8fdsNl0o5omoxB5nXcyzsInGe…

Delphi 7 IdHTTP POST 中文乱码得解决

WEB后台使用UTF-8进行编码&#xff0c;由于D7默认是ansiString&#xff0c;直接提交到后台会使中文乱码。 解决方法&#xff1a; 1.先把AnsiString转WideString 2.通过System单元中的ansitoUTF8()函数进行转换之后再提交就可以了。 代码示例&#xff1a; procedure postDe…

华为三层交换机之基本操作

Telnet简介 Telnet是一个应用层协议,可以在Internet上或局域网上使用。它提供了基于文本的远程终端接口&#xff0c;允许用户在本地计算机上登录到远程计算机&#xff0c;然后像在本地计算机上一样使用远程计算机的资源。Telnet客户端和服务器之间的通信是通过Telnet协议进行的…

OpenAI 降低价格并修复拒绝工作的“懒惰”GPT-4,另外ChatGPT 新增了两个小功能

OpenAI降低了GPT-3.5 Turbo模型的API访问价格&#xff0c;输入和输出价格分别降低了50%和25%。这对于使用API进行文本密集型应用程序的用户来说是一个好消息。 OpenAI官网&#xff1a;OpenAI AIGC专区&#xff1a;aigc 教程专区&#xff1a;AI绘画&#xff0c;AI视频&#x…

Vite+Electron快速构建一个VUE3桌面应用(二)——动态模块热重载

一. 简介 在上一篇文章ViteElectron快速构建一个VUE3桌面应用中&#xff0c;我们了解了如何使用Vite和Electron来快速构建一个Vue3桌面应用。但是&#xff0c;之前构建的应用仅仅是一个简单的版本。在开发过程中&#xff0c;为了更好的开发体验&#xff0c;在开发electron的时…

博弈论(牛客练习赛)

思路&#xff1a;我们考虑小念赢 1、如果n>1并且p0&#xff0c;小念可以连续取两次&#xff0c;相当于小念有挂&#xff0c;可以从必败态转为必胜态&#xff0c;必赢。 2、如果n>1并且m>n-1&#xff0c;小念第一次取n-1个&#xff0c;小念必赢。 代码&#xff1a; …

【C语言】(1)初识C语言

什么是C语言 C语言是一种广泛应用的计算机编程语言&#xff0c;它具有强大的功能和灵活性&#xff0c;使其成为系统编程和底层开发的首选语言。C语言的设计简洁、高效&#xff0c;且不依赖于特定的硬件或系统&#xff0c;因此在各种计算平台上都能稳定运行。 C语言的特点 高…

C++ 之LeetCode刷题记录(十九)

&#x1f604;&#x1f60a;&#x1f606;&#x1f603;&#x1f604;&#x1f60a;&#x1f606;&#x1f603; 开始cpp刷题之旅。 依旧是追求耗时0s的一天。 108. 将有序数组转换为二叉搜索树 给你一个整数数组 nums &#xff0c;其中元素已经按 升序 排列&#xff0c;请你…

针对于vue element-plus组件的el-date-picker日期区间组件的日期格式问题以及如何进行区间判断

<template><el-date-picker v-model"value1" type"daterange" range-separator"To" start-placeholder"开始日期" end-placeholder"结束日期" :size"size" change"sarend" /> </templat…

速盾:海外网站CDN加速建站有哪些优势?

随着互联网的发展&#xff0c;海外网站的建设和访问需求也日益增加。而为了提高海外网站的访问速度和用户体验&#xff0c;CDN加速技术成为了一种必不可少的解决方案。本文将介绍海外网站CDN加速建站的优势&#xff0c;并回答一些相关问题。 一、提高网站访问速度 海外网站部署…

C#学习笔记_数组

数组是一个存储相同类型元素的固定大小的顺序集合。数组是用来存储数据的集合&#xff0c;通常认为数组是一个同一类型变量的集合。 声明数组 一、一维数组 一维数组声明语法如下&#xff1a; datatype[] arrayName; 其中&#xff0c;datatype为数据类型&#xff0c;array…

844.比较含退格的字符串(力扣LeetCode)

844.比较含退格的字符串 题目描述 给定 s 和 t 两个字符串&#xff0c;当它们分别被输入到空白的文本编辑器后&#xff0c;如果两者相等&#xff0c;返回 true 。# 代表退格字符。 注意&#xff1a;如果对空文本输入退格字符&#xff0c;文本继续为空。 示例 1&#xff1a;…

泽众云真机-机型集中化运维方案升级全面完成

2024年元月份&#xff0c;泽众云真机运维团队&#xff0c;经过几个月软硬件多轮安装调试&#xff0c;机型集中化运维方案升级全面完成。解决了云真机的机型集中化运维难题&#xff0c;方便了运营人员手机管理。 具体如下&#xff1a; 1、集中化运维&#xff0c;如服务器、PC、…

中级ccnp多久可以考下来?ccnp 课件哪里有?

思科中级CCNP考试要求考生具备一定的网络基础知识&#xff0c;包括IP地址规划、VLAN配置、STP协议等。然而&#xff0c;对于许多初学者来说&#xff0c;中级CCNP考试的难度和门槛都是一个不小的挑战。那么&#xff0c;究竟需要多久才能考下中级CCNP证书呢?下面将为你详细解读。…

esp32 操作DS1307时钟芯片

电气参数摘要 有VCC供电&#xff0c;IIC活动状态是1.5mA&#xff0c;待机状态200μA&#xff0c;电池电流5nA(MAX50nA&#xff09;无VCC供电的时候&#xff0c;电池电流&#xff0c;300nA&#xff08;时钟运行&#xff09;&#xff0c;10nA&#xff08;时钟停止&#xff09;供…

Compose中添加Android原生控件

文章目录 一、前言二、示例代码三、AndroidView的注意事项四、参考链接 一、前言 Compose自身组件有时候并不能完全满足要求&#xff0c;这里演示如何在Compose中添加原生组件及其注意事项 二、示例代码 Composablefun AndroidTextView(modifier: Modifier) {AndroidView(mod…

机器学习-pandas(含数据)

pandas 优势&#xff1a; 增强图表可读性便捷的数据处理能力读取文件方便封装了Matplotlib、Numpy的画图和计算 更详细的教程&#xff1a;Pandas 教程 | 菜鸟教程 (runoob.com) Pandas数据结构 Pandas中一共有三种数据结构&#xff0c;分别为&#xff1a;Series、DataFram…