관리 메뉴

Dev Blog

Completed Map_20210430_Day3 본문

Projects/Covid19 Tracker

Completed Map_20210430_Day3

Nomad Kim 2021. 5. 1. 00:57

[ADD]Make Covid19 Map function

- Utilize Leaflet Service for making Map

- Circles for Covid19 / Recovered / Deaths

- Detect location of country and zoom-in

- Click Effects on infoboxes by CSS


 

맵 구현

 

1) useEffect in App.js, 모든 국가의 바이러스 관련 데이터를 fetch 해온다

await fetch("https://disease.sh/v3/covid-19/countries")

2) setMapCountries 의 상태로 데이터 저장

3) 상태값 mapCenter, mapZoom, mapCountries, casesType 을 Map 컴포넌트에 전달

 

App.js

import React, { useState, useEffect } from 'react'
import './App.css';
import {
  MenuItem,
  FormControl,
  Select,
  Card,
  CardContent
} from "@material-ui/core"
import InfoBox from './Infobox'
import Map from './Map'
import Table from './Table'
import LineGraph from './LineGraph'
import { sortData, prettyPrintStat } from './util'
import 'leaflet/dist/leaflet.css'

function App() {

  ...생략...
  
  const [mapCenter, setMapCenter] = useState({ lat: 37, lng: 127.5});
  const [mapZoom, setMapZoom] = useState(3);
  const [mapCountries, setMapCountries] = useState([]);
  const [casesType, setCasesType] = useState("cases");

...생략...

  useEffect(() => {
    const getCountriesData = async () => {
      await fetch("https://disease.sh/v3/covid-19/countries")
      .then(res => res.json())
      .then((data) =>{
        const countries = data.map((country)=> (
          {
            name:country.country,
            value: country.countryInfo.iso2
          }
        ))
        // console.log(data)
        const sortedData = sortData(data);
        setTableData(sortedData);
        setMapCountries(data);
        setContries(countries);
        // console.log(countries)
      })
    }
    getCountriesData();
  }, [])

...생략...

  return (
    <div className="app">
      <div className="app__left">
      
...생략...

        <Map countries={mapCountries} casesType={casesType} center={mapCenter} zoom={mapZoom}/>
      </div>
       
...생략...       
       
    </div>
  );
}

export default App;

 

4) react-leaflet 라이브러리의 해당 엘리먼트에 각 상태(mapCenter, mapZoom, mapCountries, casesType)를 전달

      <MapContainer>
        <ChangeView center={center} zoom={zoom}/>
        <TileLayer
        url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
        attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
        />
        {showDataOnMap(countries, casesType)}
      </MapContainer>

 

Map.js

import React from 'react';
import { MapContainer, TileLayer, Marker, Popup, useMap } from 'react-leaflet';
import './Map.css';
import { showDataOnMap } from './util';

function Map({countries, casesType, center, zoom}) {

  function ChangeView({ center, zoom }) {
    const map = useMap();
    map.setView(center, zoom);
    return null;
  }

  // console.log(countries.country)
  // console.log(casesType)
  // console.log(typeof casesType)

  return (
    <div className="map">
      <MapContainer>
        <ChangeView center={center} zoom={zoom}/>
        <TileLayer
        url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
        attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
        />
        {showDataOnMap(countries, casesType)}
      </MapContainer>
    </div>
  )
}

export default Map

5) showDataOnMap 컴포넌트에서 data 와 casesType 을 받아,

  a. Circle 엘리먼트의 center(국가의 위도와 경도)를 추출 및 반영

  b. casesTypeColors 를 이용하여 casesType 에 따라 radius(반경) 산출, pathOptions 에서 다른 색 적용

  c. Popup 엘리먼트에 국기 이미지, cases, recovered, deaths 수를 반영

country data

6) 렌더링

 

util.js - showDataOnMap

import React from 'react';
import { Circle, Popup } from 'react-leaflet';
import numeral from 'numeral';

const casesTypeColors = {
  cases: {
    hex: "#CC1034",
    rgb: "rgb(204, 16, 52)",
    half_op: "rgba(204, 16, 52, 0.5)",
    multiplier: 400,
  },
  recovered: {
    hex: "#7DD71D",
    rgb: "rgb(125, 215, 29)",
    half_op: "rgba(125, 215, 29, 0.5)",
    multiplier: 500,
  },
  deaths: {
    hex: "#fb4443",
    rgb: "rgb(251, 68, 67)",
    half_op: "rgba(251, 68, 67, 0.5)",
    multiplier: 1000,
  },
};

...생략...

//Draw circles on the map with interative tooltip
export const showDataOnMap = (data, casesType) =>(
  data.map(country => (
    <Circle
    center={[country.countryInfo.lat, country.countryInfo.long]}
    fillOpacity={0.4}
    pathOptions={{
      color:casesTypeColors[casesType].hex,
      fillColor: casesTypeColors[casesType].hex
    }}
    radius={
      Math.sqrt(country[casesType]) * casesTypeColors[casesType].multiplier
    }
    >
    <Popup style={{display:"flex", backgroundColor:"gray"}}>
      <div className="info-container">
        <div className="info-flag">
          <img src={`${country.countryInfo.flag}`} alt=""/>
        </div>
        <div className="info-confirmed">Cases: {numeral(country.cases).format("0,0")}</div>
        <div className="info-recovered">Recovered: {numeral(country.recovered).format("0,0")}</div>
        <div className="info-deaths">Deaths: {numeral(country.deaths).format("0,0")}</div>
        <div></div>
      </div>
    </Popup>

    </Circle>
  ))
)

추가 진행 예정

- 코드 로직 리뷰 및 적용 라이브러리 사용법 정리

- CSS refactoring

 

도움 받은 사이트

Comments