Skip to content
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

[FLAG-1018][EPIC] Tree cover gain 5y baseline year #4873

Open
wants to merge 28 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
6af36dd
tree gain new decoding initial test
AngelArcones Feb 22, 2024
687e91e
Default canPlay to true in the Timestep component and remove hardcode…
SARodrigues Mar 19, 2024
ddcd03f
Timeline Component to support an option to match the legend to the sl…
SARodrigues Mar 21, 2024
739032a
Add support for disabling start/end handles in the Timestep component
SARodrigues Mar 22, 2024
16cda70
Tweak slider component variable names to be more descriptive
SARodrigues Jul 25, 2024
7460f9d
feat(tree-cover): add baselineYear dropdown
willian-viana Oct 18, 2024
044ce96
feat(datasets): update datasets versions
willian-viana Oct 4, 2024
3a8d34a
feat(tree-cover): add baselineYear to the SQL query
willian-viana Oct 22, 2024
bcff20d
feat(tree-cover): add baselineYear dropdown to Location of Tree cover…
willian-viana Oct 22, 2024
e52cb88
chore(tree-cover): set 2000 as default value for baselineYear query
willian-viana Oct 22, 2024
08a1fd9
feat(tree-cover): add baselineYear dropdown to Tree cover gain outsid…
willian-viana Nov 13, 2024
74dbf77
fix(query): add baseline year to tree cover gain by plantation type
willian-viana Nov 14, 2024
7505fe2
feat(tree-cover): validate if data is empty
willian-viana Nov 13, 2024
54aea59
chore(selector): encapsulate settings.baselineYear to the params object
willian-viana Nov 13, 2024
8d8fb69
Merge pull request #4754 from wri/data/tree_gain_5y_decoding
willian-viana Nov 15, 2024
4601297
fix(baseline-year): remove 2020
willian-viana Nov 15, 2024
bbd3f3b
chore(envs): Split RW and GFW API's keys to make it more flexible
willian-viana Nov 15, 2024
e33204e
chore(sentence): remove the bold formatting from the word 'and' used …
willian-viana Nov 12, 2024
9091af7
fix(sentence): add placeholder instead of the word and to avoid set b…
willian-viana Nov 13, 2024
ac99421
fix(layer): set the correct dataset_slug to the widget
willian-viana Nov 15, 2024
b40b014
fix(baseline-year): get startYear instead of baseline year for analysis
willian-viana Nov 19, 2024
bd39ad6
feat(baseline-year): add new case for baseline year dropdown
willian-viana Nov 19, 2024
cf79bf5
feat(baseline-year): use startYear instead of baselineYear to be able…
willian-viana Nov 20, 2024
becc80c
fix(query): improve the way we compare baseline year
willian-viana Nov 26, 2024
2ae8181
fix(tree-gain): use getGain instead of getGainGrouped
willian-viana Nov 27, 2024
6b26d88
fix(baseline-year): add a default value for the startYear
willian-viana Nov 27, 2024
aa68713
chore(filename): add baseline year to the download filename
willian-viana Nov 27, 2024
14aacb6
fix(layer): update decode function
willian-viana Dec 10, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ const mapStateToProps = (
startDate,
endDate,
trimEndDate,
step,
matchLegend,
dynamicTimeline,
...props
}
Expand All @@ -28,7 +30,8 @@ const mapStateToProps = (
dynamicTimeline,
};
return {
marks: getMarks({ dates, dynamicTimeline }),
marks: getMarks({ dates, step, matchLegend, dynamicTimeline }),
step,
...props,
};
};
Expand Down
13 changes: 11 additions & 2 deletions components/map/components/legend/components/timeline/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,15 @@ import moment from 'moment';
import range from 'lodash/range';

const getDates = (state) => state.dates;
export const getMarks = createSelector(getDates, (dates) => {
const getSliderStep = (state) => state.step;
const getMatchLegend = (state) => state.matchLegend;

const getTicksStep = (numOfYears, sliderStep, matchLegend) => {
if (matchLegend && numOfYears && sliderStep) return numOfYears / sliderStep;
return (numOfYears > 6 ? 6 : numOfYears);
}

export const getMarks = createSelector(getDates, getSliderStep, getMatchLegend, (dates, sliderStep, matchLegend) => {
if (!dates) return null;
const { minDate, maxDate, dynamicTimeline = false } = dates;
const numOfYears = moment(maxDate).diff(minDate, 'years');
Expand All @@ -12,11 +20,12 @@ export const getMarks = createSelector(getDates, (dates) => {
if (!numOfYears || maxDays <= 365) return null;

const marks = {};
const ticksStep = getTicksStep(numOfYears, sliderStep, matchLegend);

let ticks = range(
0,
maxDays + 1,
maxDays / (numOfYears > 6 ? 6 : numOfYears)
maxDays / ticksStep
);

if (dynamicTimeline) {
Expand Down
69 changes: 67 additions & 2 deletions components/slider/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ export class Slider extends PureComponent {
railStyle: PropTypes.shape({}),
dotStyle: PropTypes.shape({}),
pushable: PropTypes.oneOfType([PropTypes.bool, PropTypes.number]),
disableStartHandle: PropTypes.bool,
disableEndHandle: PropTypes.bool,
playing: PropTypes.bool,
onChange: PropTypes.func,
};

static defaultProps = {
Expand All @@ -44,14 +48,39 @@ export class Slider extends PureComponent {
railStyle: { backgroundColor: '#d9d9d9' },
dotStyle: { visibility: 'hidden', border: '0px' },
pushable: true,
disableStartHandle: false,
disableEndHandle: false,
playing: false,
onChange: () => {},
};

renderHandle = (props) => {
const { formatValue, showTooltip } = this.props;
const {
formatValue,
showTooltip,
playing,
disableStartHandle,
disableEndHandle,
} = this.props;
const { value, dragging, index, ...restProps } = props;
const formattedValue = formatValue ? formatValue(value) : value;
const tooltipVisible = showTooltip ? showTooltip(index) : false;

// Start handle
if (disableStartHandle && props?.index === 0) {
return null;
}

// End handle
if (disableEndHandle && props?.index === 2) {
return null;
}

// Vertical line that indicates the current position, when playing
if (!playing && props?.index === 1) {
return null;
}

return (
<Tooltip
key={index}
Expand All @@ -68,8 +97,43 @@ export class Slider extends PureComponent {
);
};

handleOnChange = (newSliderPositions) => {
const {
value: sliderPositions,
disableStartHandle,
disableEndHandle,
onChange,
} = this.props;

// Both handles are disabled, no possible changes can be made.
if (disableStartHandle && disableEndHandle) {
return null;
}

// Start handle disabled. We allow trim and end value, but keep the start value the same.
if (disableStartHandle) {
onChange([
sliderPositions[0],
newSliderPositions[1],
newSliderPositions[2],
]);
return null;
}

// End handle disabled. We allow the start value, but keep the same trim and end value.
if (disableEndHandle) {
onChange([newSliderPositions[0], sliderPositions[1], sliderPositions[2]]);
return null;
}

// Full functionality, pass the new values along.
onChange(newSliderPositions);
return null;
};

render() {
const { customClass, range, handleStyle, value, ...rest } = this.props;
const { customClass, range, handleStyle, value, onChange, ...rest } =
this.props;

const Component = range ? Range : RCSlider;
const handleNum = Array.isArray(value) ? value.length : 1;
Expand Down Expand Up @@ -98,6 +162,7 @@ export class Slider extends PureComponent {
handle={this.renderHandle}
handleStyle={handleStyles}
value={value}
onChange={this.handleOnChange}
{...rest}
/>
</div>
Expand Down
11 changes: 10 additions & 1 deletion components/timestep/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,15 @@ class Timestep extends PureComponent {
handleOnChange: PropTypes.func,
handleOnAfterChange: PropTypes.func,
handleOnPlay: PropTypes.func,
disableStartHandle: PropTypes.bool,
disableEndHandle: PropTypes.bool,
};

static defaultProps = {
customClass: null,
range: true,
pushable: 0,
canPlay: false,
canPlay: true,

trim: null,

Expand Down Expand Up @@ -75,6 +77,8 @@ class Timestep extends PureComponent {
handleOnChange: null,
handleOnAfterChange: null,
handleOnPlay: null,
disableStartHandle: false,
disableEndHandle: false,
};

constructor(props) {
Expand Down Expand Up @@ -462,6 +466,8 @@ class Timestep extends PureComponent {
range,
pushable,
PlayButton,
disableStartHandle,
disableEndHandle,
} = this.props;

const { playing } = this.state;
Expand All @@ -488,6 +494,9 @@ class Timestep extends PureComponent {
pushable={pushable}
onChange={this.handleOnChange}
onAfterChange={this.handleOnAfterChange}
disableStartHandle={disableStartHandle}
disableEndHandle={disableEndHandle}
playing={playing}
/>
</div>
</div>
Expand Down
11 changes: 6 additions & 5 deletions components/ui/dynamic-sentence/component.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,14 @@ class DynamicSentence extends PureComponent {
}
} else {
// eslint-disable-next-line security/detect-non-literal-regexp
const regex = new RegExp(`{${p}}`, 'g');
const DYNAMIC_PARAMETERS = new RegExp(`{${p}}`, 'g');
const PLACEHOLDER = new RegExp(`\\bPLACEHOLDER\\b`, 'g'); // regex to remove the bold from the word 'and' between indicators

formattedSentence =
formattedSentence &&
formattedSentence.replace(
regex,
`<b>${translateText(param)}</b>`
);
formattedSentence
.replace(DYNAMIC_PARAMETERS, `<b>${translateText(param)}</b>`)
.replace(PLACEHOLDER, `<span class="no-bold">and</span>`);
}
}
});
Expand Down
4 changes: 4 additions & 0 deletions components/ui/dynamic-sentence/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
font-size: 20px;
color: $slate;

.no-bold {
font-weight: 300 !important;
}

strong,
b,
span {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,29 @@ class WidgetSettings extends PureComponent {
/>
</div>
);

case 'baseline-select':
return (
options &&
!!options.length && (
<Dropdown
className={cx('widget-settings-selector', type)}
theme={cx('theme-select-light', {
'theme-dropdown-button': type === 'mini-select',
})}
label={label}
value={startValue}
options={options}
onChange={(change) =>
propagateChange({ [startKey]: change && change.value })}
disabled={loading}
clearable={clearable}
infoAction={metaKey ? () => handleShowInfo(metaKey) : null}
optionsAction={handleShowInfo}
optionsActionKey="metaKey"
noSelectedValue={placeholder}
/>
)
);
case 'compare-select':
return (
<div className={cx('widget-settings-selector', type)}>
Expand All @@ -120,7 +142,7 @@ class WidgetSettings extends PureComponent {
const loadingDatepicker = !startValue || !minDate || !maxDate;

return (
<div className={cx("widget-settings-selector", type)}>
<div className={cx('widget-settings-selector', type)}>
<div className="datepicker-selector">
<div>
<span className="label">From</span>
Expand All @@ -130,7 +152,7 @@ class WidgetSettings extends PureComponent {
selected={new Date(startValue)}
onChange={(change) =>
propagateChange({
[startKey]: moment(change).format("YYYY-MM-DD"),
[startKey]: moment(change).format('YYYY-MM-DD'),
})}
minDate={new Date(minDate)}
maxDate={new Date(maxDate)}
Expand All @@ -146,7 +168,7 @@ class WidgetSettings extends PureComponent {
selected={new Date(endValue)}
onChange={(change) =>
propagateChange({
[endKey]: moment(change).format("YYYY-MM-DD"),
[endKey]: moment(change).format('YYYY-MM-DD'),
})}
minDate={new Date(minDate)}
maxDate={new Date(maxDate)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import { EuropeFAOCountries } from 'utils/fao-countries';

import getWidgetProps from './selectors';

const MIN_YEAR = 2000;

export default {
widget: 'treeCoverGainOutsidePlantations',
title: {
Expand Down Expand Up @@ -43,8 +45,16 @@ export default {
clearable: true,
blacklist: ['wdpa'],
},
{
key: 'baselineYear',
label: 'Baseline Year',
type: 'baseline-select',
startKey: 'startYear',
placeholder: MIN_YEAR,
clearable: true,
},
],
refetchKeys: ['forestType', 'landCategory'],
refetchKeys: ['forestType', 'landCategory', 'startYear'],
chartType: 'pieChart',
colors: 'gainWithinOutsidePlantations',
metaKey: 'widget_tree_cover_gain_outside_plantations',
Expand All @@ -70,24 +80,28 @@ export default {
},
sentences: {
globalInitial:
'Globally between 2000 and 2020, {gainPercent} of tree cover gain occurred outside of plantations.',
'Globally between {baselineYear} and 2020, {gainPercent} of tree cover gain occurred outside of plantations.',
globalWithIndicator:
'Globally between 2000 and 2020, {gainPercent} of tree cover gain within {indicator} occurred outside of plantations.',
'Globally between {baselineYear} and 2020, {gainPercent} of tree cover gain within {indicator} occurred outside of plantations.',
regionInitial:
'In {location} between 2000 and 2020, {gainPercent} of tree cover gain occurred outside of plantations.',
'In {location} between {baselineYear} and 2020, {gainPercent} of tree cover gain occurred outside of plantations.',
regionWithIndicator:
'In {location} between 2000 and 2020, {gainPercent} of tree cover gain within {indicator} occurred outside of plantations.',
'In {location} between {baselineYear} and 2020, {gainPercent} of tree cover gain within {indicator} occurred outside of plantations. ',
},
blacklists: {
adm0: EuropeFAOCountries,
},
settings: {
threshold: 0,
startYear: MIN_YEAR,
endYear: 2020, // reference to display the correct data on the map
},
getData: (params) => {
return getTreeCoverGainByPlantationType(params).then((response) => {
const { data } = (response && response.data) || {};

if (data?.length === 0) return null;

const totalArea = data.reduce(
(prev, curr) => prev + curr?.gain_area_ha,
0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,15 @@ export const parseSentence = createSelector(
}
})();

const { baselineYear: dateFromDashboard, startYear: dateFromMapLayer } =
settings;

const params = {
location: locationName,
indicator: indicator && indicator.label,
startYear: settings.startYear,
endYear: settings.endYear,
baselineYear: dateFromMapLayer || dateFromDashboard || 2000,
gainPercent: formatNumber({
num: (100 * data?.areaOutsidePlantations) / data?.totalArea,
unit: '%',
Expand Down
Loading
Loading