Javascript๋กœ ์ „ ์„ธ๊ณ„ ๐ŸŒ ๐Ÿ”ฅ ํ™œ์„ฑ ํ™”์žฌ ๐Ÿ”ฅ ์‹œ๊ฐํ™”

30688 ๋‹จ์–ด climatejavascriptlearn
์›๋ž˜ ๋‚ดblog์— ๊ฒŒ์‹œ๋จ

์šฐ๋ฆฌ๋Š” ๋ฌด์—‡์„ ๋ฐฐ์šธ ๊ฒƒ์ž…๋‹ˆ๊นŒ?



์ด ๊ธฐ์‚ฌ์—์„œ๋Š” Javascript์—์„œ Znote์„ ํ†ตํ•ด Plotlyjs ๋ฐ Streams์™€ ํ•จ๊ป˜ ๊ฐœ๋ฐฉํ˜• ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ „ ์„ธ๊ณ„ ํ™œ์„ฑ ํ™”์žฌ ๋ฐ์ดํ„ฐ๋ฅผ ํƒ์ƒ‰ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์‚ดํŽด๋ด…๋‹ˆ๋‹ค.

ํ™œ์„ฑ ํ™”์žฌ ๋ฐ์ดํ„ฐ



๋‘ ๊ฐœ์˜ ์œ„์„ฑ(Aqua ๋ฐ Terra)์— ํƒ‘์žฌ๋œ MODIS ์‹œ์Šคํ…œ(๋ณดํ†ต ํ•ด์ƒ๋„ ์ด๋ฏธ์ง• ๋ถ„๊ด‘ ๋ฐฉ์‚ฌ๊ณ„)์„ ์‚ฌ์šฉํ•˜์—ฌ ํ™”์žฌ ์œ„์น˜ ๋ฐ ์—ด ์ด์ƒ์„ ์ œ๊ณตํ•˜๋Š” NASA ๋•๋ถ„์— ํ™”์žฌ๊ฐ€ ํ–‰์„ฑ ์ „์ฒด์— ์–ด๋–ป๊ฒŒ ๋ถ„ํฌ๋˜์–ด ์žˆ๋Š”์ง€ ์‹œ๊ฐํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์–ด๋””์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์–ป์Šต๋‹ˆ๊นŒ?



๋งˆ์ง€๋ง‰ ํ™œ์„ฑ ํ™”์žฌ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹คhere.

๊ธฐ๋ก์„ ํƒ์ƒ‰ํ•˜๋ ค๋ฉด ์•„์นด์ด๋ธŒhere๋ฅผ ์š”์ฒญํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฐ์ดํ„ฐ ์„ธํŠธ ์„ค๋ช…



Danfojs์œผ๋กœ ๊ฐ„๋‹จํ•œ ์ธ์‡„๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์—ด์„ ์ฐพ๊ธฐ ์œ„ํ•ด ๋ฐ์ดํ„ฐ ์„ธํŠธ ๋ฏธ๋ฆฌ ๋ณด๊ธฐ๋ฅผ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.

const df = await dfd.readCSV("https://firms.modaps.eosdis.nasa.gov/data/active_fire/modis-c6.1/csv/MODIS_C6_1_Global_7d.csv");
print(df); // or df.ctypes to list all columns




์ „์ฒด ๋ฐ์ดํ„ฐ์„ธํŠธ ์„ค๋ช…์€ here์ž…๋‹ˆ๋‹ค.

ํ™œ์„ฑ ํ™”์žฌ์˜ ์„ธ๊ณ„์ง€๋„



๊ฐ ํฌ์ธํŠธ๋Š” ๋งค์ผ ์„ ์–ธ๋œ ์ƒˆ๋กœ์šด ํ™”์žฌ์ž…๋‹ˆ๋‹ค. ๋งค์ผ ์‹œ์ž‘๋˜๋Š” ์ƒˆ๋กœ์šด ํ™”์žฌ์˜ ์ˆ˜๋Š” ์ถฉ๊ฒฉ์ ์ž…๋‹ˆ๋‹ค ๐Ÿ˜จ

์•„๋ž˜ ์ง€๋„๋Š” 2022๋…„ 7์›”๊ณผ 8์›”์„ ์Šคํฌ๋กคํ•ฉ๋‹ˆ๋‹ค.

์ด ๋งต์„ ์–ด๋–ป๊ฒŒ ๊ตฌ์ถ•ํ•ฉ๋‹ˆ๊นŒ?



const summer2022 = "/Users/alagrede/Desktop/modis-active-fires-7d.csv";

const content = _fs.readFileSync(summer2022, 'utf8'); // read data file

function show(date) {
    // get lat/long fields for date given
    const latLong = content.split('\n')
        .filter(r=>String(r.split(",")[5]).startsWith(date))
        .map(r => [r.split(",")[0], r.split(",")[1]]) // lat/long
    latLong.shift(); // remove header

    var data = [{
        type: 'scattergeo',
        lon: latLong.map(r=>r[1]),
        lat: latLong.map(r=>r[0]),
        marker: {size: 2, color:'red'}, // draw fires
    }];

    var layout = {
        geo: {
            scope: 'world',
            resolution: 50,
            showland: true,
            showocean: true,
        },
        title: date,
    };
    Plotly.newPlot(el, data, layout);
}

// list days between 2 dates (functions used are described at the end of the article)
var daylist = await getDaysArray(new Date("2022-08-18"), new Date("2022-08-24"));

// loop over days
for(const day of daylist) {
    show(day);
    await sleep(500);
}

๋ฐ์ดํ„ฐ ์ƒˆ๋กœ ๊ณ ์นจ(๋งˆ์ง€๋ง‰ 7์ผ) โ„น๏ธ



์ด์ œ ๋ฐ์ดํ„ฐ๋ฅผ ์ƒˆ๋กœ ๊ณ ์น˜๊ณ  ํŒŒ์ผ์— ์ €์žฅํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋งˆ์ง€๋ง‰ 7d ํŒŒ์ผ์€ ์˜จ๋ผ์ธ์—์„œ ์ง์ ‘ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const r = await fetch('https://firms.modaps.eosdis.nasa.gov/data/active_fire/modis-c6.1/csv/MODIS_C6_1_Global_7d.csv')
const content = await r.text();
_fs.writeFileSync('/Users/alagrede/Desktop/modis-active-fires-7d.csv', content);


๊ตญ๊ฐ€์— ์ง‘์ค‘



์ด์ œ ์ค‘์‹ฌ์ ์„ ์„ ํƒํ•˜๊ณ  ์ง€๋„ ๋ฒ”์œ„๋ฅผ ๋ณ€๊ฒฝํ•˜์—ฌ ์„ ํƒํ•œ ํŠน์ • ๊ตญ๊ฐ€์— ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํŠน์ • ์ง€๋ฆฌ์  ์œ„์น˜๋ฅผ ์ฐพ์œผ๋ ค๋ฉด Google ์ง€๋„๋ฅผ ์—ด๊ณ  ์ง€์ ์„ ์„ ํƒํ•œ ๋‹ค์Œ ์œ„๋„/๊ฒฝ๋„๋ฅผ ๋ณต์‚ฌ/๋ถ™์—ฌ๋„ฃ๊ธฐํ•˜์‹ญ์‹œ์˜ค.

Plotlyjs API ์„ค๋ช…์„ ์ฐพ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹คhere.

var layout = {
    geo: {
        center: {lat: -46.449031, lon: 2.521705}, // France
        projection: {scale: 1.5}, // zoom
        scope: 'europe', // "africa" | "asia" | "europe" | "north america" | "south america" | "usa" | "world"
        resolution: 50,
        showland: true,
        showocean: true,
    },
    title: date,
};
Plotly.newPlot(el, data, layout);


ํ”„๋ž‘์Šค





์œ„์—์„œ ์ œ์•ˆํ•œ ๋Œ€๋กœ ์Šฌ๋ผ์ด๋”๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ์‹œ๊ฐ„์„ ์ˆ˜๋™์œผ๋กœ ํƒ์ƒ‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Plotly.newPlot(el+"-myGraph", data, layout);

// add a slider control (min - max - onChange callback)
const slider = await createSlider(0, daylist.length - 1, async function() {
   show(daylist[slider.value]);
   await sleep(200);
});

htmlEl.innerHTML=""; // reset result div
htmlEl.appendChild(slider); // add html slider
var graph = createElement("div"); // add the map into a div
graph.id = el+"-myGraph"; // plotly html id
htmlEl.appendChild(graph); // append map
show(daylist[0]);



2000๋…„ ์ดํ›„ ํ”„๋ž‘์Šค ํ™”์žฌ ์ง„ํ™”



์ด์ œ ์šฐ๋ฆฌ๋Š” ํ™”์žฌ๊ฐ€ ๋ฐœ์ƒํ•œ ์œ„์น˜๋ฅผ ์•Œ์•˜์œผ๋ฏ€๋กœ 2000๋…„ ์ดํ›„ ํ™”์žฌ ํšŸ์ˆ˜๊ฐ€ ์–ด๋–ป๊ฒŒ ๋ณ€ํ–ˆ๋Š”์ง€ ์‚ดํŽด๋ณด๋Š” ๊ฒƒ์ด ํฅ๋ฏธ๋กœ์šธ ๊ฒƒ์ž…๋‹ˆ๋‹ค.
์ด ํ‘œ๋Š” media๋กœ ์ธํ•ด massive heat wave in Europe์— ๋ณด๊ณ ๋œ 2003๋…„์˜ ์˜ˆ์™ธ์ ์ธ ํ™”์žฌ ํ™œ๋™์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

2019๋…„ ์‹ ๊ธฐ๋ก
Forest fires: in France, in the middle of summer, the area burned is greater than in all of 2019

2022๋…„์—๋„ ์‹ ๊ธฐ๋กโ€ฆ
France's unprecedented summer of wildfires, in maps and graphs



// All France fires count (make a ticket to ask history of fires)
const allfires = "/Users/alagrede/Desktop/fire_archive_M-C61_290304.csv";
const content = _fs.readFileSync(allfires, 'utf8');
const activeFires = content.split('\n');
activeFires.shift();
const count = activeFires
    // map to month
    .map(r=>String(r.split(",")[5]).slice(0,7)) // date field like 2008-08
    // map to year
    //.map(r=>String(r.split(",")[5]).slice(0,4)) // date field like 2008
    // group by
    .reduce((total, value) => {
        total[value] = (total[value] || 0) + 1;
        return total;
    }, {});

const data = [{
  y: Object.values(count),
  x: Object.keys(count),
  type: 'scatter'
}];

const layout = {
  title: "Evolution of active fires",
  height: 400,
  width: 500
}

Plotly.newPlot(el, data, layout);


์˜จ๋„ ์ด์ƒ



๊ธฐ์˜จ ์ธก๋ฉด์—์„œ๋Š” 2000๋…„๋Œ€ ์ดํ›„ ๊ธฐ์˜จ์˜ ์ƒ์Šน์„ธ๊ฐ€ ๋šœ๋ ทํ•˜๋‹ค.

์ด๊ฒƒ์€ ๋ถ€๋ถ„์ ์œผ๋กœ ํ™”์žฌ์˜ ์ฆ๊ฐ€์™€ ๊ธฐ๋ก์ด ์ž์ฃผ ๊นจ์ง€๋Š” ์ด์œ ๋ฅผ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.



const r = await fetch('https://www.ncei.noaa.gov/access/monitoring/climate-at-a-glance/global/time-series/globe/land_ocean/ytd/12/1880-2016.json')
const json = await r.json();
//printJSON(json);
const result = Object.entries(json.data).map(year => [year[0], year[1]]);

const data = [{
  y: result.map(r=>r[1]),
  x: result.map(r=>r[0]),
  type: 'scatter'
}];

const layout = {
  title: "World temperature anomalies",
  height: 400,
  width: 500
}

Plotly.newPlot(el, data, layout);



์„ธ๊ณ„

Unsurprisingly, the world is following the same upward trend.





๊ธฐํƒ€ ๋Œ€๋ฅ™



์œ ๋Ÿฝ์— ๋น„ํ•˜๋ฉด ์•„ํ”„๋ฆฌ์นด์™€ ๋‚จ๋ฏธ ๋Œ€๋ฅ™์—์„œ ์ƒˆ๋กœ ์„ ํฌ๋œ ํ™”์žฌ ๊ฑด์ˆ˜๋Š” ๋ง๊ทธ๋Œ€๋กœ ๋ฌด์„ญ๋‹ค... ๐Ÿ˜…





์Šคํฌ๋ฆฝํŠธ์—์„œ ์‚ฌ์šฉ๋˜๋Š” ํ•จ์ˆ˜

async function getDaysArray(start, end) {
    for(var arr=[],dt=new Date(start); dt<=new Date(end); dt.setDate(dt.getDate()+1)){
        arr.push(new Date(dt).toISOString().slice(0,10));
    }
    return arr;
}
async function sleep(time) {
  await new Promise(r => setTimeout(r, time));
}
async function createSlider(min, max, callback) {
  var slider = createElement('input');
  slider.type = 'range';
  slider.min = min;
  slider.max = max;
  slider.value = 0;
  slider.step = 1;
  slider.style.marginLeft = "0px"
  slider.style.width = "100%"
  slider.addEventListener('input', callback, false);
  return slider;
}


๋” ๋‚˜์•„๊ฐ€



์ด ์˜ˆ์ œ๋ฅผ ๋” ์ž์„ธํžˆ ์‚ดํŽด๋ณด๋ ค๋Š” ์‚ฌ์šฉ์ž๋ฅผ ์œ„ํ•ด ์ „์ฒด Javascript ๋…ธํŠธ๋ถ์€ myGithub repo์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๋ฐ์ดํ„ฐ์˜ ์‚ฌ์šฉ์„ ์šฉ์ดํ•˜๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด ์ด ์˜ˆ์ œ๋Š” ๋Œ€ํ™”ํ˜•Znote ์•ฑ์œผ๋กœ ๋งŒ๋“ค์–ด์กŒ์Šต๋‹ˆ๋‹ค.

์ข‹์€ ์›นํŽ˜์ด์ง€ ์ฆ๊ฒจ์ฐพ๊ธฐ