结对第二次作业——某次疫情统计可视化的实现
这个作业属于哪个课程 | 班级的链接 |
---|---|
这个作业要求在哪里 | 作业要求 |
结对学号 | 221701125 & 221701126 |
这个作业的目标 | 实现通过地图的形式来直观显示 疫情的大致分布情况,还可以查 看具体省份的疫情统计情况;点 击某个省份显示该省疫情的具体 情况;学习与队友进行合作;学 习使用github进行团队合作;学 习如何进行前后端数据交互。 |
作业正文 | 正文 |
其他参考文献 | echarts官网 W3Cschool教程 |
1、仓库地址&代码规范
2、展示作品
(1)首页如图所示,上半部分显示整体疫情,下半部分以中国地图来显示整体疫情,地图根据疫情显示不同颜色
(2)当鼠标移动到中国地图的某个省份,将会出现浮动窗口显示该省份的疫情,并且该省份地图会高亮显示。
(3)地图左边可查看根据确诊人数显示的不同颜色深浅,例如将鼠标移到100-999的部分,符合该部分的省份都将高亮显示
(4)鼠标点击100-999的图标,将重置所有疫情符合100-999的省份为白色,其他部分的颜色功能相同,点击即重置为白色,再次点击又会重新显示出颜色。
(5)中间的输入框可选择日期
(6)选择日期后,整个界面将统计该日期的疫情,地图的颜色也会根据疫情人数有所改变
(7)点击地图上某个省份,跳转到新页面显示该省份的疫情界面
(8)将鼠标移动到折线图上,将会跳出浮动窗显示具体数据(新增确诊趋势、新增疑似趋势、治愈趋势、死亡趋势)
(9)折线图上方有四个标题,点击某个标题可隐藏该折线图,再次点击又可显示
(10)中间的输入框可选择日期,选择后将显示该省份该日期的具体疫情(现有确诊、累计确诊、累计治愈、累计死亡)
(11)折线图右上方有个下载的图标,点击可下载折线图界面
3、结对讨论过程描述
刚拿到题目后,和队友进行讨论,总结了以下问题:
(1)该用什么语言来写,需要能支持服务器(javaee还是php)?
(2)后端数据怎么获取?怎么处理?怎么统计?
(3)后端接口要如何返回数据?
(4)前端如何接收后端传来的数据?
(5)前端界面要如何规划?每个界面要如何划分模块?
(6)前端中国地图要如何绘制?前端折线图要如何绘制?
(7)前端如何实现不同省份不同疫情要显示不同颜色?鼠标移动如何显示浮窗?颜色高亮?
(8)前端如何实现日期选择?
经过讨论,并且上网查找资料后,基本解决了所有问题:
用javaee来做这个项目
后端读取日志文件,并写算法统计数据,讲数据封装到哈希表
后端接口返回int []类型的数组
前端用jsp来写,可直接写java代码调用后端接口传数据
前端首页和折线图页面都分为上下两部分,上半部分显示疫情数字,下半部分显示图表
中国地图和折线图都用echarts来绘制
echarts可设置不同数量显示不同颜色,中国地图echarts自带浮窗和显示高亮
前端日期选择用HTML5中的input标签,将属性type设置为date
echarts的用法可在官网找到使用教程
两人结对讨论截图:
4、设计实现过程
设计实现过程:后端处理数据后,前端调用后端接口获取数据,填充到echarts显示,中国地图点击某省份,跳转到折线图时,应该在跳转的URL后添加参数省份的id,调用方法request.getParameter()可获取input输入选择的日期,根据不同日期做相应的计算显示数据。
功能结构图:
5、关键代码
Entry.java 读取日志文件,处理数据,封装到哈希表中
/* * 处理每一个文件 */ public static void solveTheFile(String[] fileList) { Province nationwide = map.get("全国"); // TODO Auto-generated method stub for(String aFile : fileList) { String fileDate = aFile.substring(0, aFile.indexOf(".")); Date nationwideDate = new Date(); nationwideDate.setDate(fileDate); try { File file = new File(path + "\\" + aFile); InputStreamReader inputReader = new InputStreamReader(new FileInputStream(file), "GB2312"); BufferedReader bf = new BufferedReader(inputReader); String str; while ((str = bf.readLine()) != null && str.indexOf("//") != 0) { if(str.length() == 0) continue; String[] information = str.split("\\s+"); String provinceName = information[0];//先取到省份 int number = getNumber(information);//取出各行人数 if(map.get(provinceName) != null) { Province province = map.get(provinceName); Map<String, Date> dateMap = province.getDateMap(); Date date = new Date(); if(dateMap.get(fileDate) != null) { date = province.getDateMap().get(fileDate); } else { date = new Date(); date.setDate(fileDate); } switch (information[1]) { case "新增": if(information[2].equals("感染患者")) { int ip = date.getIp(); ip += number; date.setIp(ip); int nationwideIp = nationwideDate.getIp(); nationwideIp += number; nationwideDate.setIp(nationwideIp); } else {//疑似患者的情况 int sp = date.getSp(); sp += number; date.setSp(sp); int nationwideSp = nationwideDate.getSp(); nationwideSp += number; nationwideDate.setSp(nationwideSp); } break; case "治愈": int cure = date.getCure(); int ip1 = date.getIp(); cure += number; ip1 -= number; date.setCure(cure); date.setIp(ip1); int nationwideCure = nationwideDate.getCure(); nationwideCure += number; nationwideDate.setCure(nationwideCure); break; case "死亡": int dead = date.getDead(); int ip2 = date.getIp(); dead += number; ip2 -= number; date.setDead(dead); date.setIp(ip2); int nationwideDead = nationwideDate.getDead(); nationwideDead += number; nationwideDate.setDead(nationwideDead); break; default: break; } dateMap.put(fileDate, date); nationwide.getDateMap().put(fileDate, nationwideDate); } } bf.close(); // bw.close(); inputReader.close(); } catch (IOException e) { e.printStackTrace(); } } }
index.jsp 首页,调用接口获取数据,在script中绘制中国地图
var dataMap = [{id:1, name: '北京',value:beijing }, {id:2, name: '天津',value:tianjin}, {id:3, name: '上海',value:shanghai }, {id:4, name: '重庆',value:chongqing }, {id:5, name: '河北',value:hebei }, {id:6, name: '河南',value:henan }, {id:7, name: '云南',value:yunnan }, {id:8, name: '辽宁',value:liaoning }, {id:9, name: '黑龙江',value:heilongjiang }, {id:10, name: '湖南',value:hunan }, {id:11, name: '安徽',value:anhui }, {id:12, name: '山东',value:shandong }, {id:13, name: '新疆',value:xinjiang }, {id:14, name: '江苏',value:jiangsu }, {id:15, name: '浙江',value:zhejiang }, {id:16, name: '江西',value:jiangxi }, {id:17, name: '湖北',value:hubei }, {id:18, name: '广西',value:guangxi }, {id:19, name: '甘肃',value:gansu }, {id:20, name: '山西',value:shanxi }, {id:21, name: '内蒙古',value:neimenggu }, {id:22, name: '陕西',value:shanxi2 }, {id:23, name: '吉林',value:jilin }, {id:24, name: '福建',value:fujian }, {id:25, name: '贵州',value:guizhou }, {id:26, name: '广东',value:guangdong }, {id:27, name: '青海',value:qinghai }, {id:28, name: '西藏',value:xizang }, {id:29, name: '四川',value:sichuan }, {id:30, name: '宁夏',value:ningxia }, {id:31, name: '海南',value:hainan }, {id:32, name: '台湾',value:"none" }, {id:33, name: '香港',value:"none"}, {id:34, name: '澳门',value:"none" }] // 需要在页面上直接标记出来的城市 //var specialMap = ['浙江', '云南', '陕西']; var specialMap = []; // 对dataMap进行处理,使其可以直接在页面上展示 for (var i = 0; i < specialMap.length; i++) { for (var j = 0; j < dataMap.length; j++) { if (specialMap[i] == dataMap[j].name) { dataMap[j].selected = true; break; } } } // 绘制图表,准备数据 var option = { tooltip: { formatter: function (params) { var info = '<p style="font-size:18px">' + params.name + '</p><p style="font-size:14px">'+'确诊:'+params.value+'人'+'</p>' return info; }, //提示标签背景颜色 backgroundColor:"rgba(0,0,255,0.5)", textStyle: { color: "#fff" } //提示标签字体颜色 }, series: [ { name: '中国', type: 'map', mapType: 'china', selectedMode: 'multiple', label: { normal: { show: true,//显示省份标签 // textStyle:{color:"#c71585"}//省份标签字体颜色 }, emphasis: { show: true,//对应的鼠标悬浮效果 //textStyle:{color:"#00FFFF"} } }, itemStyle: { normal: { borderWidth: .5,//区域边框宽度 // borderColor: '#009fe8',//区域边框颜色 // areaColor:"#ffefd5",//区域颜色 }, emphasis: { borderWidth: .5, borderColor: '#4b0082', areaColor: "#00FFFF", } }, data: dataMap } ], visualMap: { show : true, x: 'left', y: 'bottom', splitList: [ {start: 10000}, {start: 1000, end: 9999}, {start: 100, end: 999}, {start: 10, end: 99}, {start: 1, end: 9}, {start: 0, end: 0}, ], color: ['#660208', '#8C0D0D', '#CC2929','#FF7B69' ,'#FFAA85' , '#F8F9FA'] }, }; //初始化echarts实例 var myChart = echarts.init(document.getElementById('container')); //使用制定的配置项和数据显示图表 myChart.setOption(option); myChart.on('click', function (params) { console.log(params.data.id); window.location.href="statistics.jsp?id="+params.data.id; });
statistics.jsp 绘制折线图
var option = { title: { text: '<%=province%> 疫情趋势', // 标题名称 }, xAxis: { type: 'category', data: ['01-19', '01-20', '01-21', '01-22', '01-23', '01-24', '01-25','01-26','01-27','01-28','01-29','01-30','01-31','02-01','02-02'] }, yAxis: { type: 'value', name: '单位:例', }, tooltip: { trigger: 'axis' }, legend: { data: ['新增确诊趋势', '新增疑似趋势', '治愈趋势', '死亡趋势'] }, toolbox: { feature: { saveAsImage: {} } }, series: [ { name:"新增确诊趋势", data: [<%=dateMap.get("2020-01-19")[0]%>, <%=dateMap.get("2020-01-20")[0]%>, <%=dateMap.get("2020-01-21")[0]%>, <%=dateMap.get("2020-01-22")[0]%>, <%=dateMap.get("2020-01-23")[0]%>, <%=dateMap.get("2020-01-24")[0]%>, <%=dateMap.get("2020-01-25")[0]%>,<%=dateMap.get("2020-01-26")[0]%>, <%=dateMap.get("2020-01-27")[0]%>,<%=dateMap.get("2020-01-28")[0]%>, <%=dateMap.get("2020-01-29")[0]%>,<%=dateMap.get("2020-01-30")[0]%>, <%=dateMap.get("2020-01-31")[0]%>,<%=dateMap.get("2020-02-01")[0]%>, <%=dateMap.get("2020-02-02")[0]%>], type: 'line', symbol:'star',//拐点样式 symbolSize: 5,//拐点大小 smooth: true, itemStyle : { normal : { lineStyle:{ color:"#EE3B3B",//折线颜色 borderColor:'#EE3B3B', } } }, }, { name:"新增疑似趋势", data: [<%=dateMap.get("2020-01-19")[1]%>, <%=dateMap.get("2020-01-20")[1]%>, <%=dateMap.get("2020-01-21")[1]%>, <%=dateMap.get("2020-01-22")[1]%>, <%=dateMap.get("2020-01-23")[1]%>, <%=dateMap.get("2020-01-24")[1]%>, <%=dateMap.get("2020-01-25")[1]%>,<%=dateMap.get("2020-01-26")[1]%>, <%=dateMap.get("2020-01-27")[1]%>,<%=dateMap.get("2020-01-28")[1]%>, <%=dateMap.get("2020-01-29")[1]%>,<%=dateMap.get("2020-01-30")[1]%>, <%=dateMap.get("2020-01-31")[1]%>,<%=dateMap.get("2020-02-01")[1]%>, <%=dateMap.get("2020-02-02")[1]%>], type: 'line', symbol:'star',//拐点样式 symbolSize: 5,//拐点大小 smooth: true, itemStyle : { normal : { lineStyle:{ color:"#EE7942",//折线颜色 borderColor:'#EE7942', } } }, }, { name:"治愈趋势", data: [<%=dateMap.get("2020-01-19")[2]%>, <%=dateMap.get("2020-01-20")[2]%>, <%=dateMap.get("2020-01-21")[2]%>, <%=dateMap.get("2020-01-22")[2]%>, <%=dateMap.get("2020-01-23")[2]%>, <%=dateMap.get("2020-01-24")[2]%>, <%=dateMap.get("2020-01-25")[2]%>,<%=dateMap.get("2020-01-26")[2]%>, <%=dateMap.get("2020-01-27")[2]%>,<%=dateMap.get("2020-01-28")[2]%>, <%=dateMap.get("2020-01-29")[2]%>,<%=dateMap.get("2020-01-30")[2]%>, <%=dateMap.get("2020-01-31")[2]%>,<%=dateMap.get("2020-02-01")[2]%>, <%=dateMap.get("2020-02-02")[2]%>], type: 'line', symbol:'star',//拐点样式 symbolSize: 5,//拐点大小 smooth: true, itemStyle : { normal : { lineStyle:{ color:"#20B2AA",//折线颜色 borderColor:'#20B2AA', } } }, }, { name:"死亡趋势", data: [<%=dateMap.get("2020-01-19")[3]%>, <%=dateMap.get("2020-01-20")[3]%>, <%=dateMap.get("2020-01-21")[3]%>, <%=dateMap.get("2020-01-22")[3]%>, <%=dateMap.get("2020-01-23")[3]%>, <%=dateMap.get("2020-01-24")[3]%>, <%=dateMap.get("2020-01-25")[3]%>,<%=dateMap.get("2020-01-26")[3]%>, <%=dateMap.get("2020-01-27")[3]%>,<%=dateMap.get("2020-01-28")[3]%>, <%=dateMap.get("2020-01-29")[3]%>,<%=dateMap.get("2020-01-30")[3]%>, <%=dateMap.get("2020-01-31")[3]%>,<%=dateMap.get("2020-02-01")[3]%>, <%=dateMap.get("2020-02-02")[3]%>], type: 'line', symbol:'star',//拐点样式 symbolSize: 5,//拐点大小 smooth: true, itemStyle : { normal : { lineStyle:{ color:"#36648B",//折线颜色 borderColor:'#36648B', } } }, }, ] }; //初始化echarts实例 var myChart = echarts.init(document.getElementById('container')); //使用制定的配置项和数据显示图表 myChart.setOption(option);
6、心路历程与收获
221701125张家榜:
一开始拿到这个作业我是觉得很困难,因为看到作业要求后,我觉得有很多功能难以实现,并且我也没有学过一些框架,一些技术上的问题难以解决,并且,我也没尝试过用web进行前后端数据交互。不过在后来在实现的过程中,不断上网查找资料解决难题,学习新技术,解决难题。这次作业让我意识到,光靠本科课程所学的知识是远远不够的,还要课外学习常用的技术。在这次作业,我负责写前端,队友负责写后端,最后由我整合,让我体会到团队合作的重要性。
我的队友是一位全能型选手,不管什么难题,只要交给他,他总能高效率地去完成。在此之前,我们经常一起讨论问题,一起分享成果,彼此十分熟悉,配合也很默契。即使这次作业对我来说十分困难,但是只要一想到与他合作,我就信心十足。一个优秀的队友在团队中是十分重要 ,我很庆幸能与他合作。在本次作业中,我们互相帮助,有困难一起解决。我希望今后还能有机会与他在此合作!
221701126 唐靖钧:
这次结对作业中我主要负责后端数据的读取、处理和接口的编写,本来想尝试使用python进行实时数据的爬取,但是由于水平有限,最终还是采用导入静态文件的方式来进行处理,这也是一个遗憾,借此机会也勉励自己一定要努力提升自己的技术,这样未来不会因为某些技术受限而无法实现某些需求。
我们两个人先确定了代码规范,这是编程的基础,也是一个良好习惯的养成,一个好的代码规范在团队协作中起到了至关重要的作用。
接着我们进行需求分析和代码设计,得出结论即为我负责后端读入文件,讲文件中的数据进行处理,接着编写接口返回这些以数组为结构的数据,前端调用我的接口即可绘制地图、折线图等。
无数次的QQ聊天交流,互相汇报两个人的进度以及当前困难遇到的困难,我想团队开发的魅力就体现于此吧。
每完成一个部分就上传至我们两个人的团队仓库,虽然这次结对作业只有两个人,但是也让我感受到团队协作的魅力,同伴需要什么接口,我就提供给他,也让我有了成就感,未来的软工实践团队项目应该会具挑战,但是我认为遇到困难不可怕,大家不沟通不交流才可怕,我也对未来的真正的项目团队协作充满了期待,也具有一定的正向压力。
我认为我的同伴是一个具有领导力的人,他能够清晰的分析任务的需求和所需技术,并且能够进行很好的分工,在我遇到一些困难无法实现的时候,我都会去求助于他,而他也会很耐心的为我找寻解决办法,我认为这次结对作业的核心在于结对,在于同伴,一个优秀的同伴会让自己在开发过程中不会惧怕困难,正如我上面所述,在这次任务中,我遇到了许多后端方面的问题,还有前后端交互的认识障碍,但是我们两个人能够互相帮助,共享资源,也就使得这些技术上的困难没有那么可怕了,我不担心麻烦他,我也希望他也能够在遇到困难时多麻烦我,希望我的同伴在未来的项目实践中能够一帆风顺,将来有机会一定会选择与他再次组队!
![]
-------------------------------------------
个性签名:满天的星星都在你的眼里
如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,博主在此感谢!