Skip to content

Feature/future carbon gains #3681

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jan 15, 2019
2 changes: 2 additions & 0 deletions app/javascript/components/widgets/manifest.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import * as treeCoverLocated from './widgets/land-cover/tree-cover-located';
// Climate
import * as emissions from './widgets/climate/emissions';
import * as emissionsDeforestation from './widgets/climate/emissions-deforestation';
import * as futureCarbonGains from './widgets/climate/future-carbon-gains';

// Biodiversity
// import * as gladBiodiversity from './widgets/biodiversity/glad-biodiversity';
Expand Down Expand Up @@ -63,6 +64,7 @@ export default {
// climate
emissions,
emissionsDeforestation,
futureCarbonGains,
// biodiversity
// gladBiodiversity,
// land use
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import axios from 'axios';
import { getEmissions } from 'services/climate';

export default ({ params }) =>
axios.all([...getEmissions({ ...params })]).then(
axios.spread(
(
cYSF,
cMASF,
cPasture,
cCrops,
co2YSF,
co2MASF,
co2Pasture,
co2Crops
) => {
const data = {
cGain: {
YSF: cYSF.data && cYSF.data.values,
MASF: cMASF.data && cMASF.data.values,
Pasture: cPasture.data && cPasture.data.values,
Crops: cCrops.data && cCrops.data.values
},
co2Gain: {
YSF: co2YSF.data && co2YSF.data.values,
MASF: co2MASF.data && co2MASF.data.values,
Pasture: co2Pasture.data && co2Pasture.data.values,
Crops: co2Crops.data && co2Crops.data.values
}
};
return data;
}
)
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export default {
widget: 'futurecarbongains',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we camelCase this like other widgets?

title: {
initial: 'Annual tree cover loss by dominant driver in {location}',
global: 'Global annual tree cover loss by dominant driver'
},
categories: ['climate'],
colors: 'climate',
types: ['country', 'region'],
admins: ['adm0', 'adm1', 'adm2'],
options: {
units: ['co2Gain', 'cGain']
},
layers: ['fffa76d3-5008-48b7-afeb-2c7054548f2e'],
metaKey: 'potential_tree_biomass_gain',
sortOrder: {
summary: 1,
forestChange: 1,
global: 1
},
sentences: {
initial:
'In {location}, potential carbon sequestration may reach {amount} of {variable} by {maxYear}.'
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Component from 'components/widgets/components/widget-composed-chart';
import getData from './actions';
import getProps from './selectors';
import config from './config';
import settings from './settings';

export { getData, getProps, Component, config, settings };
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { createSelector, createStructuredSelector } from 'reselect';
import isEmpty from 'lodash/isEmpty';
import { yearTicksFormatter } from 'components/widgets/utils/data';
import { formatNumber } from 'utils/format';

const getData = state => state.data || null;
const getSettings = state => state.settings || null;
const getLocationName = state => state.locationName || null;
const getSentences = state => state.config && state.config.sentences;
const getColors = state => state.colors || null;

export const parseData = createSelector(
[getData, getSettings],
(data, settings) => {
if (isEmpty(data)) return null;
const years = {};
const selectedData = data[settings.unit];
Object.keys(selectedData).forEach(key =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could use a reduce here to make this simpler.

selectedData[key].forEach(obj => {
if (years[obj.year]) years[obj.year][key] = obj.value;
else years[obj.year] = { year: obj.year, [key]: obj.value };
})
);
return Object.values(years);
}
);

export const parseConfig = createSelector(
[getData, getSettings, getColors],
(data, settings, colors) => {
if (isEmpty(data)) return null;
const selectedData = data[settings.unit];
const yKeys = {};
Object.keys(selectedData).forEach((k, i) => {
yKeys[k] = {
fill: colors.ramp && colors.ramp[i],
stackId: 1
};
});
let tooltip = [
{
key: 'year'
}
];
const labels = {
YSF: 'Young Secondary Forest',
MASF: 'Mid-Age Secondary Forests'
};
tooltip = tooltip.concat(
Object.keys(selectedData)
.map((k, i) => ({
key: k,
label: labels[k] ? labels[k] : k,
color: colors.ramp && colors.ramp[i]
}))
.reverse()
);
return {
height: 250,
xKey: 'year',
yKeys: {
bars: yKeys
},
xAxis: {
ticksFormatter: yearTicksFormatter
},
tooltip
};
}
);

export const parseSentence = createSelector(
[getSettings, getLocationName, getSentences, parseData],
(settings, location, sentences, parsedData) => {
if (isEmpty(parsedData)) return null;
const maxYear = parsedData[parsedData.length - 1];
const amount = Object.values(maxYear).reduce(
(acc, n) => acc + n,
-maxYear.year
);
const variables = {
cGain: 'carbon',
co2Gain: 'carbon dioxide'
};
const variable = variables[settings.unit];
const { initial } = sentences;

return {
sentence: initial,
params: {
location,
amount: formatNumber({ num: amount * 1000000, unit: 't' }),
variable,
maxYear: maxYear.year
}
};
}
);

export const parseTitle = createSelector(
[],
() => 'Potential tree biomass gain'
);

export default createStructuredSelector({
data: parseData,
dataConfig: parseConfig,
sentence: parseSentence,
title: parseTitle
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default {
unit: 'co2Gain'
};
3 changes: 3 additions & 0 deletions app/javascript/data/colors.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
"ramp": ["#070752", "#231f74", "#3c3898", "#5452be", "#6d6de5", "#8a86ec", "#a49ff3", "#bdb9f9", "#d4d4ff"],
"noGain": "#e7e5a4"
},
"climate": {
"ramp": [ "#EDD093", "#BFBC35", "#007F53", "#123A33"]
},
"plantations": {
"main": "#d67828",
"species": {
Expand Down
10 changes: 9 additions & 1 deletion app/javascript/data/units.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,16 @@
"label": "CO2 Emissions",
"value": "co2LossByYear"
},
{
"label": "C02",
"value": "co2Gain"
},
{
"label": "Carbon",
"value": "cLossByYear"
},
{
"label": "Carbon",
"value": "cGain"
}
]
]
25 changes: 25 additions & 0 deletions app/javascript/services/climate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import request from 'utils/request';

const REQUEST_URL =
'http://climate.globalforestwatch.org/api/indicators/{indicator}?thresh={thresh}&iso={iso}&id_1={id}&area={area}';

const INDICATORS = [
3110, // (Carbon) Young Secondary Forest
3111, // (Carbon) Mid-Age Secondary Forests
3112, // (Carbon) Pasture
3113, // (Carbon) Crops
3114, // C02 Young Secondary Forest
3115, // C02 Mid-Age Secondary Forests
3116, // C02 Pasture
3117 // C02 Crops
];

export const getEmissions = ({ threshold, adm0, adm1, adm2 }) =>
INDICATORS.map(indicator => {
const url = REQUEST_URL.replace('{indicator}', indicator)
.replace('{thresh}', threshold || '0')
.replace('{iso}', adm0 ? String(adm0) : '')
.replace('{id}', adm1 ? String(adm1) : '')
.replace('{area}', adm2 ? String(adm1) : '');
return request.get(url);
});