프로젝트 정리
국비 데이터 분석 2차 프로젝트 결산 - 6
01241
2023. 4. 11. 12:53
Now를 누르면 각 지역별 코로나 현황을 표시하게 끔 하려고 했으나, 시간 이슈로 실패했다.
그래서 일단 지도만 표현하게끔 해주었다.
아래에는 한국의 코로나 하루 증가량을 파악하기 위한 표를 만들었다.
아래 박스에서 선택을 해주면 각 그래프 모양이 바뀌고, 코로나 하루 증가량과 총 증가량으로 구분지었다.
코로나 지도를 쓰기 위해서 d3.js 의 geojson을 사용했다.
d3 version6이라서 js 코드가 약간 달랐다.
world map으로 canvas를 지정하고 input_corona로 나라가 지정되면 js에서 받아 변경하게 해줬다.
table_now 에서는 chart.js의 canvas로 그려줬다.
<section class="headline wrap" >
<body>
<p>코로나 Now는 매일 15:00에 자동으로 최신화 됩니다. <br>
글로벌 데이터는 3일전 데이터(GMT 2일전)가 반영됩니다. <br>
아래 지도는 beta 버전으로 새 지도를 검색시 'clear'를 입력시 초기화 됩니다.
</p>
<div id="world_map">
<h2 id = 'showCase'></h2>
<input class='input_corona' type="text" placeholder='이곳에 국가별 영어표시를 입력해주세요. ex) KOR, USA'>
<canvas width="800" height="400"></canvas>
<svg id="my_dataviz" width="400" height="300"></svg>
</div>
<div id='table_now'>
<canvas id="myChart1"></canvas>
<select id="data_choice">
<option value="all" >한국 코로나 총 수치</option>
<option value="now" selected>한국 코로나 하루 증가량</option>
</select>
<div id='my_total'>
<p>
코로나 하루 증가량을 파이그래프로도 확인 가능합니다.</p>
</div>
<hr>
<select id="graph_choice">
<option value="pie" >파이 그래프</option>
<option value="bar" selected>바 그래프</option>
</select>
</div>
</body>
이맘 때쯤에 js에서 import로 다른 js와 연결시키는 것을 배웠다.
그래서 now.js 파일에 다른 now2를 연결시켰다.
일단 now.js는 geojson으로 지도를 그려주는 역할과 데이터가 입력되면 지도를 찾아가도록 해주었다.
이후 globalYesterday함수로 그 나라 확진자 수, 신규 확진자 수가 나오게 해줬다.
import {coronaNow} from './now2.js'
// import {globalYesterday} from './now4.js'
// import {enterdata,entData} from './now3.js'
// function DData(width,height,globalScale){
// this.width = width,
// this.height = height,
// this.globalScale = globalScale
// }
let geojson = {};
let projections = [
{type: 'Mercator', scale: 70},
];
let width, height, globalScale, fillStyle;
width = -6300, height = 2600, globalScale = 4.5,fillStyle = '#FFF';
function updateCanvas(d) {
let context = this.getContext('2d');
let projection = d3['geo' + d.type]()
.scale(globalScale * 5 * d.scale) // 크기 정함
.center([0, 0]) //중앙
.rotate([0.1, 0, 0]) //위치
.translate([0.5 * width, 0.5 * height]); //위치
let geoGenerator = d3.geoPath()
.projection(projection)
.context(context);
context.lineWidth = 0.5;
// // Graticule
// context.strokeStyle = '#ccc';
// context.fillStyle = 'none';
// context.setLineDash([1,1]);
// context.beginPath();
// geoGenerator(geoGraticule());
// context.stroke();
// World
context.fillStyle = fillStyle;
context.setLineDash([]);
context.beginPath();
geoGenerator({type: 'FeatureCollection', features: geojson.features})
context.fill();
context.stroke();
// // Circles
// context.strokeStyle = '#888';
// context.fillStyle = 'none';
// circles.forEach(function(center) {
// geoCircle.center(center);
// context.beginPath();
// geoGenerator(geoCircle());
// context.stroke();
// });
// Projection label
context.fillStyle = '#333';
context.font = '14px sans-serif';
context.fillText('geo' + d.type, 6, 17);
}
function update() {
// console.log(width,height,globalScale)
let u = d3.select('#world_map')
.selectAll('canvas')
.data(projections);
u.enter()
.append('canvas')
.attr('width', width + 'px')
.attr('height', height + 'px')
.merge(u)
.each(updateCanvas);
u.exit().remove();
}
// // // load data and display the map on the canvas with country geometries
// d3.json("/static/main/json/NNLL.json")
// .then(function (json) {
// geojson = json;
// update();
// console.log(geojson)
// });
d3.json('https://gist.githubusercontent.com/d3indepth/f28e1c3a99ea6d84986f35ac8646fac7/raw/c58cede8dab4673c91a3db702d50f7447b373d98/ne_110m_land.json')
.then(function(json) {
geojson = json;
update();
});
coronaNow()
//coronaNow()로 json 불러오기
export async function globalYesterday() {
const globalY = '../static/main/json/global.json';
const response = await fetch(globalY);
const data = await response.json();
return data;
};
// json 데이터 정리
const enterdata = document.querySelector('.input_corona')
let showCase = document.querySelector("#showCase");
function entData(){
geojson = {};
// update()
let Countrys
enterdata.addEventListener('keypress', (e)=>{
if(e.key == 'Enter'){
switch (enterdata.value) {
case 'clear':{
width = 1320, height = 1600, globalScale = 10;
fillStyle ='#000000';
break
}
case 'KOR':{
// korData = new DData(-6300,2600,4)
fillStyle='#FFF'
width = -6300, height = 2600, globalScale = 4.5;
Countrys = 'KOR'
break
}
case 'USA':{
fillStyle='#FFF'
width = 2500, height = 1200, globalScale = 1.6;
Countrys = 'USA'
alert('미국은 자료가 없습니다.')
break
}
case 'JPN':{
fillStyle='#FFF'
width = -7000, height = 2700, globalScale = 4.5;
Countrys = 'JPN'
break
}
case 'CHN':{
fillStyle='#FFF'
width = -5500, height = 2200, globalScale = 4.5;
Countrys = 'CHN'
break
}
case 'RUS':{
fillStyle='#FFF'
width = 0, height = 1500, globalScale = 1.6;
Countrys = 'RUS'
break
}
case 'FRA':{
fillStyle='#FFF'
width = 800, height = 2400, globalScale = 3.6;
Countrys = 'FRA'
break
}
case 'GBR':{
fillStyle='#FFF'
width = 800, height = 3150, globalScale = 3.6;
Country = 'GBR'
break
}
default:
fillStyle ='#000000';
alert('아직 등록되지 않은 나라입니다.')
break
}
}
globalYesterday().then(data => {
let newDictCountry = {'Japan':'JPN','Republic of Korea':'KOR','China':'CHN','Russian Federation':'RUS','France':'FRA','The United Kingdom':'GBR'}
let New_cases
let New_deaths
for (let i in data){
let Country = data[i].Country
if (newDictCountry[Country]){
New_cases = data[i].New_cases
New_deaths = data[i].New_deaths
// console.log(New_cases,New_deaths,newDictCountry[Country])
if (Countrys == newDictCountry[Country]){
// console.log('aa')
showCase.innerText =`나라명 : ${Country} // 새 확진자 수 : ${New_cases} // 새 사망자 수 : ${New_deaths}`
}
}
}
})
update();
})
}
entData()
now2.js는 국내 데이터 차트이다.
chart.js는 이전과 비슷하게 사용했고, 카테고리 선택 함수, 랜덤 색 뽑는 함수 정도만 더 추가해봤다.
//coronaNow()로 json 불러오기
export async function coronaNow() {
const newCorona = '../static/main/json/newCorona.json';
const response = await fetch(newCorona);
const data = await response.json();
return data;
};
// json 데이터 정리
coronaNow().then(data => {
//초기화
let new_loc_list = []
let new_up_list = []
let new_all_list = []
for (let i in data){
let loc = data[i].지역
let all_people = data[i].총인원
let up_people = data[i].증가값
// chart 가져올때 숫자화
let all_people_num = all_people.replace(/,/g,'')
all_people_num = all_people_num *1
up_people = up_people.replace(/[\(\)]/g,'')
let up_people_num = up_people.replace(/[\+]/g,'')
up_people_num = up_people_num * 1
// chart data가 배열형태로 넣어야함
new_loc_list.push(loc)
new_up_list.push(up_people_num)
new_all_list.push(all_people_num)
// 나중에 시간나면 테이블 만들기
// let tr = document.createElement('tr');
// let th = document.createElement('th');
// let td = document.createElement('td');
}
// console.log(Object.keys(data).length)
//index 0 총합은 따로 빼서 다른 곳에서 사용하기
new_loc_list.shift();
const up_total = new_up_list.shift();
new_all_list.shift();
// div 밑에 p tag 넣어서 매번 자동으로 바뀌게 해주기
const my_total_div = document.querySelector('#my_total');
let p = document.createElement('p');
p.innerHTML=(
`국내 코로나 총 확진자 수 는 ${data[0].총인원}명 입니다. <br/>
국내 지난 하루 확진자 증가 수는 ${up_total}명 입니다.`
);
p.style.fontSize = 'x-large'
my_total_div.appendChild(p)
// 16진법으로 #00ff00 이런식으로 랜덤으로 뽑기
let color_list = []
function backRandom1() {
let colorCode = '#' + Math.round(Math.random() * 0xffffff).toString(16);
color_list.push(colorCode)
}
// Object.keys(data).length -> 데이터가 object 타입으로 나옴, 이때는 이렇게 해주면 길이 찍힘
// 랜덤 색 뽑기
for (let i=0; i<Object.keys(data).length; i++){
backRandom1();
}
//그래프 선택시 수정하도록 설정
const data_choice = document.querySelector('#data_choice');
const graph_choice = document.querySelector('#graph_choice');
//selectData() 함수
function selectData() {
let select_value = data_choice.value;
let graph_choice_value = graph_choice.value;
// 각 데이터 고를 때 선택하도록
const graphset_choice = ( graph_choice_value === 'bar' ? 'bar' : 'pie' )
const dataset_choice = (select_value === 'now' ?
{
label: '한국 코로나 하루 증가량',
backgroundColor: color_list,
borderColor: 'rgb(255, 255, 255)',
data: new_up_list,
pointHoverBorderColor : 'rgb(0,255,255)',
pointHoverBorderWidth : 30,
}:
{
label: '한국 코로나 총 수치',
backgroundColor: color_list,
borderColor: 'rgb(255, 255, 255)',
data: new_all_list,
pointHoverBorderColor : 'rgb(0,255,255)',
pointHoverBorderWidth : 30,
})
// data 입력
const datas1 = {
labels : new_loc_list,
datasets: [
dataset_choice
]
}
const ctx1 = document.getElementById('myChart1').getContext('2d');
const chart1 = new Chart(ctx1, {
// 만들기 원하는 차트의 유형
type: graphset_choice,
// 데이터 집합을 위한 데이터
data: datas1,
// 설정은 여기서 하세요
options: {
legend: {
labels: {
fontColor : 'white',
fontSize : 18
}
},
aspectRatio: 1,
scales: {
xAxes: [{
ticks:{
fontColor : "rgba(251, 203, 9, 1)",
fontSize : 20,
},
gridLines:{
color: "rgba(87, 152, 23, 1)",
lineWidth: 1
}
}],
x: {
type: 'linear',
position: 'bottom'
},
yAxes: [{
ticks: {
beginAtZero: true,
// stepSize : 1,
fontColor : "rgba(251, 203, 9, 1)",
fontSize : 20,
},
gridLines:{
color: 'rgba(166, 201, 226, 1)',
lineWidth:3
}
}]
}
}
})
}
// 선택하게 해서 결정시키기
data_choice.addEventListener('change', () =>{
selectData()
})
graph_choice.addEventListener('change', () =>{
selectData()
})
// 초기에 실행
selectData()
})
원래 계획은 aws에 연결 후 linux와 schedule을 이용해 매일 15시에 최신화 시키려 했으나, aws 배포에 실패해서, schedule만 자동으로 돌렸다.
아래는 자동화 코드이다.
import schedule
import time
import requests
import pandas as pd
def doCorona():
address = 'https://ncov.kdca.go.kr/bdBoardList_Real.do?brdId=1&brdGubun=13&ncvContSeq=&contSeq=&board_id=&gubun='
res = requests.get(address)
res.encoding = None
from bs4 import BeautifulSoup as bs
soup = bs(res.text)
sb = soup.select('.rpsa_map > .rpsam_graph button')
newList=[]
for s in sb:
newDict={}
newDict['지역'] = s.span.text
newDict['총인원'] = s.find('span','num').text
newDict['증가값'] = s.find('span','before').text
newList.append(newDict)
pd.DataFrame(newList).to_json('./main/static/main/json/newCorona.json', orient = 'index', indent = 4)
# print('3초마다 실행되는지 확인')
# 매일 특정 HH:MM 및 다음 HH:MM:SS에 작업 실행
schedule.every(3).seconds.do(doCorona)
schedule.every().day.at("15:00").do(doCorona)
while True:
schedule.run_pending()
time.sleep(1)
후기는 나중에 써야지