데이터 사이언스

[데이터 시각화] D3.js로 세계 지도에 국가별 Corona19 코로나 확진자 수 실시간으로 나타내기

판다의 삶 2020. 5. 19. 01:02
728x90

코로나19 확진자는 어느 국가를 중심으로 어떻게 늘었을까?

코로나19 확진자 수를 세계 지도에 날짜별 국가별로 나타내어 확진자가 어느 국가를 중심으로 어떻게 늘어났는지 시각화해보았습니다.

확진자 수 데이터는 kaggle.com에서 다운받아 전처리하여 사용하였습니다

 

TopoJSON에는 국가명이 아닌 국가 코드만 존재하였고, 국가별 확진자 수 데이터에는 국가 코드가 아닌 국가명만 존재하였습니다.

그래서 국가별 코드 및 국가명 데이터를 별도로 구하여 TopoJSON에 추가하였습니다.

하지만 TopoJSON에 추가한 국가명이 국가별 확진자 수 데이터의 국가명과 정확하게 일치하지 않는 경우가 많아 데이터 전처리 단계에서 상당한 어려움을 겪었습니다.

 

index.html

<!DOCTYPE html>
<html>
<meta charset="utf-8">
<head>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://d3js.org/d3.v4.min.js"></script>
        <script src="https://unpkg.com/topojson-client@3"></script>
        <script src="https://d3js.org/queue.v1.min.js"></script>
        <script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
        <link rel="stylesheet" href="style.css">
</head>
<body>
    <h1>World COVID-19 Confirmed Cases Dashboard <span id="date"></span></h1>
        <script>
            
                var width = 1200,
                    height = 700;
                
                var svg = d3.select("body").append("svg")
                    .attr("width", width)
                    .attr("height", height)
                    .attr("class", "map");
                
                var projection = d3.geoMercator()
                    .translate([width / 1.9, height / 1.5]);
                
                var path = d3.geoPath()
                    .projection(projection);
                
                var g = svg.append("g");
            
                var popById = d3.map();
                var covid = d3.map();
                var features = null;

                var newDate = new Date("2020-01-22T00:00:00");
                var newDateString = null;
                var size = null;
                
                queue()
                    .defer(d3.json, "https://unpkg.com/world-atlas@1/world/110m.json" )
                    .defer(d3.csv, "data/accumulated_covid_19_data.csv")
                    .defer(d3.csv, "data/world_country_id.csv", function(row){
                        popById.set(row.id, row.name);
                    })
                    .await(ready);
                
                function ready(error, geoData, covidData){

                        covidData.map(function(row){
                        var date = row.ObservationDate;

                        if (covid.has(date)){
                            covid.get(date).push({country : row["Country/Region"], confirmed : row.Confirmed});
                        }else{
                            covid.set(date, [{country : row["Country/Region"], confirmed : row.Confirmed}]);
                        }
                        });

                    features = topojson.feature(geoData, geoData.objects.countries).features;
                
                    // add country name using country code
                    features.forEach(function(d){
                        var countryName = popById.get(d.id);
                        if (countryName) d.properties.name = countryName;
                    
                    })
                
                    svg.selectAll("path")
                      .data(features)
                      .enter()
                      .append("path")
                      .attr("d", path)
                      .attr("stroke", "white")
                      .attr("stroke-width", 0.8)
                      .attr("fill", "#202020DE");
                      
                    
                    svg.selectAll("text")
                        .data(features)
                        .enter()
                        .append("text")
                        .attr("transform", function(d) { return "translate(" + path.centroid(d) + ")"; })
                        .attr("dy", ".35em")
                        .attr("class", "country-label")
                        .text(function(d) { return d.properties.name});

                        svg.append("path");
                        var values = covidData.map(function(d){return d.Confirmed});

                    var valueExtent = d3.extent(values);

                    size = d3.scaleSqrt()
                            .domain(valueExtent)
                            .range([ 1, 28]);

                    var valuesToShow = [10000,100000,1000000]
                    var xCircle = 200
                    var xLabel = 320

                    svg
                        .selectAll("legend")
                        .data(valuesToShow)
                        .enter()
                        .append("circle")
                        .attr("cx", xCircle)
                        .attr("cy", function(d){ return height - size(d) - 50 } )
                        .attr("r", function(d){ return size(d) })
                        .style("fill", "none")
                        .attr("stroke", "black")

                    // Add legend: segments
                    svg
                        .selectAll("legend")
                        .data(valuesToShow)
                        .enter()
                        .append("line")
                        .attr('x1', function(d){ return xCircle + size(d) } )
                        .attr('x2', xLabel)
                        .attr('y1', function(d){ return height - size(d) - 50 } )
                        .attr('y2', function(d){ return height - size(d) - 50} )
                        .attr('stroke', 'black')
                        .style('stroke-dasharray', ('2,2'))

                    // Add legend: labels
                    svg
                        .selectAll("legend")
                        .data(valuesToShow)
                        .enter()
                        .append("text")
                        .attr('x', xLabel)
                        .attr('y', function(d){ return height - size(d) - 50 } )
                        .text( function(d){ return d } )
                        .style("font-size", 14)
                        .attr('alignment-baseline', 'middle')

                    redraw();

                    setInterval(redraw, 500);
                
                }



            var pullData = function(callback){

            newDateString = ("0"+(newDate.getMonth()+1)).slice(-2)+"/"+("0"+newDate.getDate()).slice(-2)+"/"+newDate.getFullYear();

            var result = covid.get(newDateString);

            if(result){


                var curData = [];
                features.map(function(d){
                    var curConfirmed = result.find(x => x.country == d.properties.name);

                    if(curConfirmed){
                        d.properties.confirmed = curConfirmed.confirmed;
                        curData.push(d);
                    }
                });
                date.innerText = newDateString;
                }else{
                    clearInterval(redraw);
                    return;
                }


            newDate.setDate(newDate.getDate() + 1);
            callback(curData);
            }

            var redraw = function(settings){
                pullData(redrawChart);  
            }

            var redrawChart = function(curData){
                //add circles
                svg.selectAll("circles")
                .data(curData)
                    .enter()
                    .append("circle")
                    .attr("transform", function(d) { return "translate(" + path.centroid(d) + ")"; })
                    .attr("r", function(d) { return size(+d.properties.confirmed)})
                    .attr("fill", "#EC332020");
            }
            
</script>
<h4>data from <a href="https://www.kaggle.com/sudalairajkumar/novel-corona-virus-2019-dataset">kaggle.com</a></h4>
</body>
</html>

* 전체 데이터와 코드 :

COVID19-world-dashboard-master.zip
0.98MB


참고자료:

[존스홉킨스 코로나 바이러스 자원 센터] 코로나19 확진자 실시간 버블 맵 coronavirus.jhu.edu/map.html

[D3.js 그래프 갤러리] 버블 맵 예제 www.d3-graph-gallery.com/graph/bubblemap_template.html


[데이터 시각화] D3.js로 World map 세계 지도 데이터 시각화하기

 

[데이터 시각화] D3.js로 World map 세계 지도 데이터 시각화하기

D3.js와 TopoJSON을 활용하여 세계 지도 데이터를 시각화할 수 있다. index.html

life-of-panda.tistory.com

 

728x90