疫情可视化part2

news2025/1/21 10:24:47

前言

这是疫情可视化最开始的文章,有需要了解的可前往查看:https://blog.csdn.net/xi1213/article/details/126824752。 本来说有时间就把这个项目完结了的,结果后面一直有事拖着,直到现在十一月份了才搞完。老样子,先看成果。

  • 浏览链接:https://xi1213.gitee.io/covid19-visualization
  • 项目链接(欢迎各位大哥star):https://gitee.com/xi1213/covid19-visualization

修改与添加

后面可能审美疲劳了,越看越丑,就干脆直接用dataV(这可不是阿里的那个dataV)修饰页面了。这是项目改动后的样子:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

主要做了如下改动:

  1. 添加首页的3d粒子球体。
  2. 完成“省内分析”模块。
  3. 完成当地疫情报告生成与下载。
  4. 使用dataV修饰页面。

粒子球体

粒子球体也是用three.js完成的,放大仔细看会发现其实是有三层构成的:内层透明球体,中间点状球体,外层病毒球体。
在这里插入图片描述

具体实现过程是这样的:

  1. 先绘制一个内层透明球体,记得将球体材质的transparent设为true,然后设置不透明度opacity值。
//创建斑点球体
async function createSpotSphere() {
  let globeBufferGeometry = new THREE.SphereGeometry(earthSize - 1, 50, 50);//球体几何体
  let globeInnerMaterial = new THREE.MeshBasicMaterial({
    color: new THREE.Color(dvColor[0]),//颜色
    // blending: THREE.AdditiveBlending,//纹理融合的叠加方式
    // side: THREE.FrontSide,//前面显示
    transparent: true,//透明
    // depthWrite: false,//深度写入
    // depthTest: false,//黑洞效果
    opacity: .3,//不透明度
  });
  let globeInnerMesh = new THREE.Mesh(
    globeBufferGeometry,
    globeInnerMaterial
  );
  earthGroup.add(globeInnerMesh); //将网格放入地球组
  cre
  1. 创建点状球体。这里需要使用一张黑白的世界地图。
let img = new Image();
img.src = earthGrayscale; //黑白地图

将图片使用onload加载到项目中后,利用canvas绘制一遍该图,再使用getImageData获取到像素点数据canData。

let canvas = document.createElement("canvas");
    canvas.width = img.width; //使得canvas尺寸与图片尺寸相同
    canvas.height = img.height;
    (canvas.getContext("2d") as any).drawImage(img, 0, 0, img.width, img.height);//canvas绘制图片
    let canData = (canvas.getContext("2d") as any).getImageData(0, 0, canvas.width, canvas.height);//获取画布像素数据

利用canData .data中的rgba信息生成缓冲几何顶点数组globeCloudVerticesArray。

let globeCloudBufferGeometry = new THREE.BufferGeometry(); //设置缓冲几何体
    let globeCloudVerticesArray = []; //地球云缓冲几何体顶点
    let o = null; //数组处理时的计数
    for (o = 0; o < canData.data.length; o += 4) {
      let r = (o / 4) % canvas.width,
        i = (o / 4 - r) / canvas.width;
      if ((o / 4) % 2 == 1 && i % 2 == 1)
        if (0 === canData.data[o]) {
          let n = r,
            longitude = (i / (canvas.height / 180) - 90) / -1, //经度
            latitude = n / (canvas.width / 360) - 180; //维度
          let s = latLongToVector3(longitude, latitude, earthSize, .1); //经纬度变换
          globeCloudVerticesArray.push(s); //将变换后的顶点放入数组
        }
    }

然后再使用three中的BufferAttribute生成属性position与color。

let l = new Float32Array(3 * globeCloudVerticesArray.length); //创建顶点数组长度
    for (o = 0; o < globeCloudVerticesArray.length; o++) {
      l[3 * o] = globeCloudVerticesArray[o].x;//设置顶点数组数据
      l[3 * o + 1] = globeCloudVerticesArray[o].y;
      l[3 * o + 2] = globeCloudVerticesArray[o].z;
    }
    let positionVal = new THREE.BufferAttribute(l, 3); //设置缓冲区属性值
    globeCloudBufferGeometry.setAttribute("position", positionVal); //给缓冲几何体添加位置属性
    let globeCloudMaterial = new THREE.PointsMaterial({
      color: new THREE.Color(dvColor[1]),//颜色
      fog: true,
      size: 1,
    });//球面斑点材质
    let d = new Float32Array(3 * globeCloudVerticesArray.length), c = [];
    for (o = 0; o < globeCloudVerticesArray.length; o++) {
      c[o] = new THREE.Color(dvColor[1]);//球面斑点颜色
      d[3 * o] = c[o].r;//设置地球云数组rgb颜色
      d[3 * o + 1] = c[o].g;
      d[3 * o + 2] = c[o].b;
    }
    let color_val = new THREE.BufferAttribute(d, 3);
    globeCloudBufferGeometry.setAttribute("color", color_val);//给缓冲几何体添加颜色属性,修改颜色直接修改globeCloudBufferGeometry的setAttribute

最后再使用THREE.Points创建球面的点,将position与color属性添加到点的几何体BufferGeometry中。

let globeCloud = new THREE.Points(//球面的象素点
      globeCloudBufferGeometry,
      globeCloudMaterial
    );

这是需要用到的坐标转换方法:

//经纬度坐标变换(传入e:纬度、a经度、t球半径、o球额外距离)
function latLongToVector3(e: any, a: any, t: any, o: any) {
  var r = (e * Math.PI) / 180,
    i = ((a - 180) * Math.PI) / 180,
    n = -(t + o) * Math.cos(r) * Math.cos(i),
    s = (t + o) * Math.sin(r),
    l = (t + o) * Math.cos(r) * Math.sin(i);
  return new THREE.Vector3(n, s, l); //计算三维向量
};
  1. 创建最外层的病毒球体。每一个病毒都是一个THREE.Sprite,材质是THREE.SpriteMaterial,利用Sprite.position.set设置具体坐标位置,最后将Sprite添加到球体组中。病毒的颜色是由当前点确诊值决定的,颜色值在colors数组中取出。
//创建病毒
function createVirus(data: any, earthSize: any) {
  let colors = [
    new THREE.Color(0xf9b8b8),
    new THREE.Color(0xfe4242),
    new THREE.Color(0xff0000),
  ]; //病毒颜色列表
  let virSize = 4; //病毒大小
  let list = JSON.parse(JSON.stringify(data));
  list.forEach((e: { value: number; color: any; position: any[]; }) => {
    e.value >= 10000000 && (e.color = colors[2]); //根据病毒数赋予不同颜色
    e.value >= 500000 && e.value < 10000000 && (e.color = colors[1]);
    e.value < 500000 && (e.color = colors[0]);
    if (e.position) {
      let virusMaterial = new THREE.SpriteMaterial({
        color: e.color,
        map: new THREE.TextureLoader().load(virusImg),
        side: THREE.FrontSide, //只显示前面
      }); //病毒材质
      let Sprite = new THREE.Sprite(virusMaterial); //点精灵材质
      Sprite.scale.set(virSize, virSize, 1); //点大小
      let lat = e.position[1]; //纬度
      let lon = e.position[0]; //经度
      let s = latLongToVector3(lat, lon, earthSize, 1); //坐标转换
      Sprite.position.set(s.x, s.y, s.z); //设置点的位置
      Sprite.dotData = e; //将点的数据添加到dotData属性中
      Sprite.name = "病毒";
      earthGroup.add(Sprite); //将病毒添加进球体组中
    }
  });
};

省内分析

  1. 省内数据概况。
    在这里插入图片描述
    该数值在加载时是有增加动画的,是利用vue响应式原理完成的,在上一篇文章中有提到。
  2. 省内各地疫情柱状图。
    在这里插入图片描述

这是具体的echart配置:

let option = {
        title: {
            text: provinceBaseData.value.province + "各地数据",
            left: "center",
            top: '5%',
            textStyle: {
                color: "#fff",
            },
        },
        tooltip: {
            trigger: 'axis',
            axisPointer: {
                type: 'shadow'
            }
        },
        dataZoom: [
            {
                type: 'inside',
            },
            {
                show: true,
                yAxisIndex: 0,
                filterMode: 'empty',
                width: 25,
                height: '70%',
                showDataShadow: false,
                left: '3%',
                top: "center"
            }
        ],
        legend: {
            data: ['累计数', '治愈数', '确诊数', '较昨日新增', '死亡数'],
            orient: "vertical",
            top: "15%",
            right: "2%",
            textStyle: {
                color: "#fff"
            },
        },
        grid: {
            left: '3%',
            right: '15%',
            bottom: '10%',
            containLabel: true
        },
        xAxis: {
            type: 'category',
            data: echartData.cityName,
            axisLabel: {
                interval: 0,
                rotate: 50,
                color: "#fff"
            },
        },
        yAxis: {
            type: 'value',
            axisLabel: {
                color: "#fff",
            },
        },
        series: [
            {
                name: '累计数',
                type: 'bar',
                emphasis: {
                    focus: 'series'
                },
                itemStyle: {
                    color: '#f59158'
                },
                data: echartData.conNum
            },
            {
                name: '治愈数',
                type: 'bar',
                emphasis: {
                    focus: 'series'
                },
                itemStyle: {
                    color: '#48c56b'
                },
                data: echartData.cureNum
            },
            {
                name: '确诊数',
                type: 'bar',
                stack: 'total',
                emphasis: {
                    focus: 'series'
                },
                itemStyle: {
                    color: '#ffd889'
                },
                data: echartData.econNum
            },
            {
                name: '较昨日新增',
                type: 'bar',
                stack: 'total',
                emphasis: {
                    focus: 'series'
                },
                itemStyle: {
                    color: '#794ebd'
                },
                data: echartData.asymptomNum
            },
            {
                name: '死亡数',
                type: 'bar',
                stack: 'total',
                emphasis: {
                    focus: 'series'
                },
                itemStyle: {
                    color: '#ff6a6a'
                },
                data: echartData.deathNum
            },
        ]
    };
  1. 省份历史疫情数据图表。
    在这里插入图片描述

这是该图表配置:

let option = {
        title: {
            text: provinceBaseData.value.province + "历史数据",
            left: "center",
            top: '5%',
            textStyle: {
                color: "#fff",
            },
        },
        tooltip: {
            trigger: 'axis',
            axisPointer: {
                type: 'cross',
                label: {
                    backgroundColor: '#6a7985'
                }
            }
        },
        legend: {
            data: ['累计数', '确诊数', '较昨日新增', '治愈数', '死亡数'],
            orient: "vertical",
            top: "15%",
            right: "2%",
            textStyle: {
                color: "#fff"
            },
        },
        grid: {
            left: '8%',
        },
        xAxis: [
            {
                type: 'category',
                boundaryGap: false,
                axisLabel: {
                    color: "#fff",
                },
                data: echatrData.time
            }
        ],
        yAxis: [
            {
                type: 'value',
                axisLabel: {
                    color: "#fff",
                },
            }

        ],
        dataZoom: [
            {
                startValue: ''
            },
            {
                type: 'inside'
            }
        ],
        series: [
            {
                name: '累计数',
                type: 'line',
                stack: 'Total',
                smooth: true,
                lineStyle: {
                    width: 0
                },
                showSymbol: false,
                areaStyle: {
                    opacity: 0.8,
                    color: "#f59158"
                },
                emphasis: {
                    focus: 'series'
                },
                itemStyle: {
                    color: '#f59158'
                },
                data: echatrData.conNum
            },
            {
                name: '确诊数',
                type: 'line',
                stack: 'Total',
                smooth: true,
                lineStyle: {
                    width: 0
                },
                showSymbol: false,
                areaStyle: {
                    opacity: 0.8,
                    color: "#ffd889"
                },
                itemStyle: {
                    color: '#ffd889'
                },
                emphasis: {
                    focus: 'series'
                },
                data: echatrData.econNum
            },
            {
                name: '较昨日新增',
                type: 'line',
                stack: 'Total',
                smooth: true,
                lineStyle: {
                    width: 0
                },
                showSymbol: false,
                label: {
                    show: true,
                    position: 'top'
                },
                areaStyle: {
                    opacity: 0.8,
                    color: "#794ebd"
                },
                itemStyle: {
                    color: '#794ebd'
                },
                emphasis: {
                    focus: 'series'
                },
                data: echatrData.asymptomNum
            },
            {
                name: '治愈数',
                type: 'line',
                stack: 'Total',
                smooth: true,
                lineStyle: {
                    width: 0
                },
                showSymbol: false,
                areaStyle: {
                    opacity: 0.8,
                    color: "#48c56b"
                },
                itemStyle: {
                    color: '#48c56b'
                },
                emphasis: {
                    focus: 'series'
                },
                data: echatrData.cureNum
            },
            {
                name: '死亡数',
                type: 'line',
                stack: 'Total',
                smooth: true,
                lineStyle: {
                    width: 0
                },
                showSymbol: false,
                areaStyle: {
                    opacity: 0.8,
                    color: "#ff6a6a"
                },
                itemStyle: {
                    color: '#ff6a6a'
                },
                emphasis: {
                    focus: 'series'
                },
                data: echatrData.deathNum
            },
        ]
    };

报告生成与下载

报告生成利用的是docxtemplater,这是前端生成word比较方便的一个插件,具体使用方法可以看这里:https://blog.csdn.net/xi1213/article/details/127412672。

  • 这是生成的word报告示例:
    在这里插入图片描述

dataV修饰

  • dataV是一个大屏可视化组件库,这是使用文档:http://datav.jiaminghi.com/guide/。
  • dataV是vue2开始的,vue3使用的话会报错,需要做一点修改,具体看这里:https://blog.csdn.net/xi1213/article/details/127697760。
  • 我只用了dataV中的一些边框与装饰,你在页面中看到的紫色线框和一些动画的装饰就是dataV的,还是蛮漂亮的。

结语

  • 最后我发现人的审美真的变化太快了。
  • 项目刚搞完:嗯不错!真漂亮!
  • 过去一周后:什么玩意!丑得一批!
  • 如果还有后续的话,我可能就要考虑添加自定义主题配色了。

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

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

相关文章

F1. 生活在树上(easy version)树,dfs

题目链接 F1. 生活在树上&#xff08;easy version&#xff09; 题目背景 本题是 B 组的最后一题&#xff0c;是 F2 题的简单版本&#xff0c;两道题目的解法略有不同。本题和 F2 题在题意上的区别在于本题给定树上的边权&#xff0c;而不是点权。 小智生活在「传智国」&am…

汽车 Automotive > SOME/IP VS DDS调研和未来方向

参考&#xff1a;JASPAR, General incorporated association&#xff1a;What is the conqueror in the SOA platform for the future in-vehicle networks? 目录 SOME/IP介绍参考 DDS介绍 SOME/IP VS DDS 研究方向 SOME/IP介绍参考 汽车Automotive &#xff1e; SOME/…

MAC安全(防MAC泛洪攻击)

一、MAC地址表项分类&#xff1a; 1.1 动态表项&#xff1a;通过对帧内的源MAC进行学习而来&#xff0c;有老化时间 1.2 静态表项&#xff1a;由管理员手工配置&#xff0c;不会老化 1.3 黑洞表项&#xff1a;丢弃特定源MAC或目的MAC,不会老化 静态和黑洞表项不会被动态表项…

类与对象(下篇)

类与对象&#xff08;下&#xff09;再谈构造函数回顾构造函数初始化列表explicit 关键字拷贝构造函数也具有初始化列表友元 friend友元函数输入输出流的重载友元类static 成员内部类再谈构造函数 回顾构造函数 在上一篇博客中提到了构造函数&#xff0c;构造函数其主要目的是…

类与对象(中篇)

类中六个默认成员函数构造函数基本概念构造函数特性析构函数基本概念析构函数特性拷贝构造函数基本概念拷贝构造函数特性赋值运算符重载概念引入运算符重载函数的特性部分运算符的重载函数判等赋值前置 、前置--后置、后置--const 成员函数取地址只要生成一个类 &#xff0c;那…

iOS_Custom Transition Animation 自定义转场动画

文章目录1、push-pop 动画协议2、present-dismiss 动画协议3、实现转场动画协议3.1 动画时长3.2 push or present animation (显示动画)3.3 动画结束3.4 pop or dismiss animation (消失动画)4、UIPresentationController4.1 设置presentVC的frame4.2 present 动画4.3 dismiss …

Docker快速安装Oracle 12c

【Oracle系列3】Docker快速安装Oracle 12c 背景 现在还很多企业用12c&#xff0c;以这个版本为例&#xff0c;介绍docker快速启动Oracle并做实验 步骤 1、docker环境的安装&#xff08;略&#xff09; 2、查询镜像&#xff0c;挑选镜像 docker search oracle结果 StoneM…

阿里P8架构师都在学习参考的SpringCloud微服务实战文档

我一直在使用Spring Boot、Spring Data等框架来进行开发工作。 作为一名Spring系列的忠实粉丝&#xff0c;我自然希望能够有更多的开发者参与进来&#xff0c;于是自己坚持写Spring Cloud相关的文章&#xff0c;并且将文章涉及的代码整理后放在GitHub上分享。 这使我得到了很…

【Hack The Box】Linux练习-- Luanne

HTB 学习笔记 【Hack The Box】Linux练习-- Luanne &#x1f525;系列专栏&#xff1a;Hack The Box &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f4c6;首发时间&#xff1a;&#x1f334;2022年11月24日&#x1f334; &#x1f3…

零基础搭建基于知识图谱的电影问答系统

零基础搭建基于知识图谱的电影问答系统一、项目准备二、项目数据三、训练问题分类器四、准备问答模板五、搭建webapp六、问题预处理一、项目准备 首先需要一款python编译器&#xff0c;本人选用的是PyCharm&#xff0c;搭建好Python环境&#xff1b;安装第三方依赖库&#xff…

【Hack The Box】linux练习-- Delivery

HTB 学习笔记 【Hack The Box】linux练习-- Delivery &#x1f525;系列专栏&#xff1a;Hack The Box &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f4c6;首发时间&#xff1a;&#x1f334;2022年11月17日&#x1f334; &#x1…

黄佳《零基础学机器学习》chap1笔记

黄佳 《零基础学机器学习》 chap1笔记 这本书实在是让我眼前一亮&#xff01;&#xff01;&#xff01; 感觉写的真的太棒了&#xff01; 文章目录黄佳 《零基础学机器学习》 chap1笔记第1课 机器学习快速上手路径—— 唯有实战1.1 机器学习族谱1.2 云环境入门实践&#xff1a;…

ERD Online 4.0.4 元数据在线建模(免费、私有部署)

❝ fix(erd): 修改表名、模块名自定义提示fix(erd): 支持自定义表名显示格式fix(erd): 升级ant到5.0.1版本fix(erd): 修复PDMan导入类型列为空fix(erd): 增加类型列宽度&#xff0c;避免类型显示不全fix(erd): 修复表设计报undefine异常fix(erd): 修复版本比对&#xff0c;出现…

二分搜索算法框架解析

文章目录 一、寻找一个数&#xff08;基本的二分搜索&#xff09;二、寻找左侧边界的二分搜索三、寻找右侧边界的二分查找总结 一、寻找一个数&#xff08;基本的二分搜索&#xff09; 这个场景是最简单的&#xff0c;可能也是大家最熟悉的&#xff0c;即搜索一个数&#xf…

2023年天津财经大学珠江学院专升本经济学专业课考试大纲

天津财经大学珠江学院2023年高职升本科专业课考试《经济学》考试大纲一、本大纲系天津财经大学珠江学院2023年高职升本科《经济学》课程考试大纲。所列考试范围出自郑健壮、王培才主编的教材《经济学基础&#xff08;第二版&#xff09;》&#xff0c;清华大学出版社&#xff0…

win10通过Docker搭建LNMP环境全流程

win10通过Docker搭建LNMP环境全流程 下载安装docker desktop 由于博主环境已经安装好了&#xff0c;一些异常设置&#xff0c;暂且略过 根据官方教程下载docker desktop执行文件.exe 注意尽量不要把docker安装到C盘&#xff0c;除非你的C盘很大&#xff0c;具体可以参考文章 …

初识 Spring 框架

文章目录一、Spring 介绍二、Spring 下载安装三、编写入门程序1.项目文件构架2.引入相关依赖3.创建实体类4.Spring 配置文件5.编写测试类四、控制反转与依赖注入1.控制反转概念2.依赖注入概念3.依赖注入的类型4.依赖注入的应用一、Spring 介绍 Spring 是由 Rod Johnson 组织和…

计算机毕设题目设计与实现(论文+源码)_kaic

毕业设计(论文)题目 高校图书馆座位预约选座微信小程序设计与实现 基于防火墙的访问控制系统的设计与实现 基于区块链的农产品追溯系统设计与实现 学生公寓楼改造布线系统规划与设计 智能家居网络设计与实现“互联网”农村精准扶贫共享平台的设计与实现“智慧健康少儿成长平台”…

Linux内核——门相关入门知识

为什么20位的寻址可以达到1MB&#xff1f; &#x1f512; 点击查看答案 &#x1f512; 拆分如下的段描述符&#xff1a; 0000000000000000 00cf9b000000ffff 00cf93000000ffff 00cffb000000ffff 00cff3000000ffff 80008b04200020ab ffc093dff0000001 0040f30000000fff 0000f20…

TypeScript开启

TypeScript是什么&#xff1f; typescript是以JavaScript为基础构建的语言&#xff0c;是一个Javascript的超集&#xff0c;可以在任何支持JavaScript的平台中执行&#xff0c;typescript扩展了JavaScript&#xff0c;并添加了类型。 注意&#xff1a;ts不能被js直接解析执行&…