Javascript๋ก ์ ์ธ๊ณ ๐ ๐ฅ ํ์ฑ ํ์ฌ ๐ฅ ์๊ฐํ
30688 ๋จ์ด climatejavascriptlearn
์ฐ๋ฆฌ๋ ๋ฌด์์ ๋ฐฐ์ธ ๊ฒ์ ๋๊น?
์ด ๊ธฐ์ฌ์์๋ 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 ์ฑ์ผ๋ก ๋ง๋ค์ด์ก์ต๋๋ค.
Reference
์ด ๋ฌธ์ ์ ๊ดํ์ฌ(Javascript๋ก ์ ์ธ๊ณ ๐ ๐ฅ ํ์ฑ ํ์ฌ ๐ฅ ์๊ฐํ), ์ฐ๋ฆฌ๋ ์ด๊ณณ์์ ๋ ๋ง์ ์๋ฃ๋ฅผ ๋ฐ๊ฒฌํ๊ณ ๋งํฌ๋ฅผ ํด๋ฆญํ์ฌ ๋ณด์๋ค https://dev.to/alagrede/visualize-active-fires-around-the-world-with-javascript-3hehํ ์คํธ๋ฅผ ์์ ๋กญ๊ฒ ๊ณต์ ํ๊ฑฐ๋ ๋ณต์ฌํ ์ ์์ต๋๋ค.ํ์ง๋ง ์ด ๋ฌธ์์ URL์ ์ฐธ์กฐ URL๋ก ๋จ๊ฒจ ๋์ญ์์ค.
์ฐ์ํ ๊ฐ๋ฐ์ ์ฝํ ์ธ ๋ฐ๊ฒฌ์ ์ ๋ (Collection and Share based on the CC Protocol.)
์ข์ ์นํ์ด์ง ์ฆ๊ฒจ์ฐพ๊ธฐ
๊ฐ๋ฐ์ ์ฐ์ ์ฌ์ดํธ ์์ง
๊ฐ๋ฐ์๊ฐ ์์์ผ ํ ํ์ ์ฌ์ดํธ 100์ ์ถ์ฒ ์ฐ๋ฆฌ๋ ๋น์ ์ ์ํด 100๊ฐ์ ์์ฃผ ์ฌ์ฉํ๋ ๊ฐ๋ฐ์ ํ์ต ์ฌ์ดํธ๋ฅผ ์ ๋ฆฌํ์ต๋๋ค