app/assets/javascripts/insights/views/glad-alerts/InsightsGladAlertsView.js
define(
[
'backbone',
'mps',
'handlebars',
'd3',
'underscore',
'_string',
'insights/views/glad-alerts/InsightsGladAlertsChartView',
'views/ShareView',
'views/SourceModalView',
'helpers/NumbersHelper',
'text!insights/templates/glad-alerts/insights-glad-alerts.handlebars',
'text!insights/templates/glad-alerts/insights-glad-alerts-legend.handlebars'
],
function(
Backbone,
mps,
Handlebars,
d3,
_,
_string,
InsightsGladAlertsChart,
ShareView,
SourceModalView,
NumbersHelper,
tpl,
tplLegend
) {
'use strict';
var GFW_URL = window.gfw.config.GFW_URL;
var API = window.gfw.config.GFW_API_HOST_V2;
var ENDPOINT_CONFIG =
'/query?sql=SELECT * FROM ' + window.gfw.config.GLAD_INSIGHT_CONFIG_ID;
var ENDPOINT_DATA =
'/query/' +
window.gfw.config.GLAD_INSIGHT_ID +
"?sql=SELECT sum(alerts) AS alerts, sum(cumulative_emissions) AS cumulative_emissions, sum(above_ground_carbon_loss) AS above_ground_carbon_loss, sum(percent_to_emissions_target) AS percent_to_emissions_target, sum(percent_to_deforestation_target) AS percent_to_deforestation_target, sum(loss_ha) AS loss, sum(cumulative_deforestation) AS cumulative_deforestation, year as year, country_iso, week FROM data WHERE %s AND year IN ('%s') AND week <= 53 GROUP BY week, country_iso ORDER BY week ASC";
var WEEKS_YEAR = 53;
var InsightsGladAlerts = Backbone.View.extend({
events: {
'click .js-selector': '_changeVisualizations',
'click .js-share': '_openShare',
'click .js-year-nav': '_changeYear',
'change .js-country-selector': '_changeDataByCountry',
'change .js-year-selector': '_changeDataByYear'
},
el: '#insights',
template: Handlebars.compile(tpl),
templateLegend: Handlebars.compile(tplLegend),
defaults: {
selectedClassEl: '-selected',
filter: 'carbon_emissions',
country: '',
year: 2018,
weekStep: 1,
desforestationFilter: 'deforestation',
loadingClassEl: 'is-loading',
mainVisSwitchEl: 'main-vis-switch',
loadedClassEl: 'loaded',
countryLabelClassEl: 'js-country-label',
countrySelectorClassEl: 'js-country-selector',
legendSelectoClassEl: 'js-legend',
noDataClassEl: 'no-data',
imageURI: window.gfw.config.GFW_DATA_S3 + 'climate/glad_maps'
},
initialize: function(params) {
this.defaults = _.extend(this.defaults, params);
this.legends = [];
this.chartConfig = [];
this.locations = [];
this.images = {};
this.currentStep = this.defaults.weekStep;
this.currentCountry = this.defaults.country;
this.currentYear = this.defaults.year;
this.filter = this.defaults.filter;
this.imageURI = this.defaults.imageURI;
// Get the visualization's configuration
$.when(this._getConfig()).done(this._initVisualization.bind(this));
},
_getConfig: function() {
return $.ajax({
url: API + ENDPOINT_CONFIG,
type: 'GET'
});
},
_initVisualization: function(config) {
this.config = this._parseConfig(config);
this.render();
this._setListeners();
// Info window
new SourceModalView();
},
_parseConfig: function(config) {
var data =
config && config.data && config.data[0] ? config.data[0] : {};
if (data) {
this.chartConfig =
typeof data.vizsetup === 'string'
? JSON.parse(data.vizsetup)
: data.vizsetup;
this.locations =
typeof data.locations === 'string'
? JSON.parse(data.locations)
: data.locations;
}
if (!this.currentCountry) {
this.currentCountry = this.chartConfig.defaultSelection;
mps.publish('Router/goInsight', [this.currentCountry]);
}
},
render: function() {
this.$el.html(
this.template({
locations: _.sortBy(this.locations, 'name')
})
);
this.$el.removeClass(this.defaults.loadingClassEl);
this.$el.addClass(this.defaults.loadedClassEl);
// Set default selection from the config
var defaultCountry = _.findWhere(this.locations, {
iso: this.currentCountry
});
if (defaultCountry) {
this._setCurrentCountry(defaultCountry.name, defaultCountry.iso);
}
this._renderMainChart();
},
_setListeners: function() {
Backbone.Events.on(
'insights:glad:update',
this._updateLegends.bind(this)
);
},
_renderMainChart: function() {
var $el = this.el.querySelector('#visMain');
var $vis = $el.querySelector('.visualization');
var $chartEl = $el.querySelector('.chart');
var url = this._getDataQuery();
this._clearVisualization();
$chartEl.innerHTML = '';
$vis.classList.add(this.defaults.loadingClassEl);
$.ajax({
url: url,
type: 'GET',
success: function(res) {
var data = res.data;
if (data.length) {
$el.classList.remove(this.defaults.noDataClassEl);
this._createVisualization(data);
} else {
$el.classList.add(this.defaults.noDataClassEl);
this._renderNoDataPlaceHolder();
}
$vis.classList.remove(this.defaults.loadingClassEl);
}.bind(this)
});
},
_getDataQuery: function() {
var iso = this.currentCountry;
var year = this.currentYear;
var filter = " country_iso ='" + iso + "'";
var locationData = _.findWhere(this.locations, {
iso: iso
});
if (locationData && locationData.groupBy) {
filter = " state_iso IN('" + locationData.groupBy.join("', '") + "')";
}
return API + _.str.sprintf(ENDPOINT_DATA, filter, year);
},
_getCurrentLocation: function() {
return _.findWhere(this.locations, {
iso: this.currentCountry
});
},
_createVisualization: function(data) {
var el = this.el.querySelector('#visMain');
var chartEl = el.querySelector('.chart');
var legendEl = el.querySelector('.legend');
this._setMaxWeek(data);
this.visMain = new InsightsGladAlertsChart({
el: chartEl,
params: {
data: this._parseData(data),
filter: this.filter,
currentStep: this.currentStep,
iso: this.currentCountry,
year: this.currentYear,
currentLocation: this._getCurrentLocation(),
desforestationFilter: this.defaults.desforestationFilter
}
});
el.classList.remove(this.defaults.loadingClassEl);
this._createLegend(legendEl, data, 'main');
},
_renderNoDataPlaceHolder: function() {
var el = this.el.querySelector('#visMain');
var chartEl = el.querySelector('.chart');
chartEl.innerHTML = "There's no data available for this selection";
},
_parseData: function(data) {
return _.map(
data,
function(d) {
var locationData = _.findWhere(this.locations, {
iso: this.currentCountry
});
if (locationData) {
d.carbon_average =
locationData.targets['carbon_emissions'].average;
d.carbon_target = locationData.targets['carbon_emissions'].target;
d.deforestation_average =
locationData.targets['deforestation'].average;
d.deforestation_target =
locationData.targets['deforestation'].target;
}
if (d.iso === this.currentCountry) {
d.selected = true;
}
return d;
}.bind(this)
);
},
_setMaxWeek: function(data) {
var lastValue =
data && data[data.length - 1] ? data[data.length - 1] : [];
if (lastValue) {
this.currentStep = lastValue.week * 1;
}
},
_getIso: function() {
var country = this.currentCountry;
var re = /(\d+)/g;
var subst = '-$1';
country = country.replace(re, subst);
return country;
},
_createLegend: function(el, data, category) {
this.legends.push({
element: el,
data: data,
category: category,
filter: this.filter
});
this._renderLegend(el, data, this.filter);
},
_renderLegend: function(el, data, filter) {
var current = _.filter(
data,
function(d) {
return d.week * 1 === this.currentStep;
}.bind(this)
)[0];
if (current) {
var target = Math.round(current.carbon_target * 1);
var emissions = Math.round(current.cumulative_emissions * 1);
var deforestation = Math.round(current.cumulative_deforestation * 1);
var target_deforestation = Math.round(
current.deforestation_target * 1
);
var alerts = Math.round(current.alerts);
var annual_budget = Math.round(emissions / target * 100);
var annual_budget_deforestation = Math.round(
deforestation / target_deforestation * 100
);
var co2Equivalency = Math.round(
current.above_ground_carbon_loss * 1 * 10000000 / 4.7
);
var date = moment.utc().year(this.currentYear);
var weeksInYear = date.weeksInYear();
var currentWeek =
weeksInYear > WEEKS_YEAR ? this.currentStep + 1 : this.currentStep;
var dateWithWeek = date.week(currentWeek);
if (weeksInYear > WEEKS_YEAR) {
dateWithWeek.subtract(1, 'days');
} else {
dateWithWeek.add(1, 'days');
}
var begin = dateWithWeek.clone().format('YYYY-MM-DD');
var end = dateWithWeek
.clone()
.add(6, 'days')
.format('YYYY-MM-DD');
el.innerHTML = this.templateLegend({
isDesforestation: filter === this.defaults.desforestationFilter,
emissions: NumbersHelper.addNumberDecimals(emissions),
deforestation: NumbersHelper.addNumberDecimals(deforestation),
annual_budget: NumbersHelper.addNumberDecimals(annual_budget),
annual_budget_deforestation: NumbersHelper.addNumberDecimals(
annual_budget_deforestation
),
alerts: NumbersHelper.addNumberDecimals(alerts),
co2Equivalency: NumbersHelper.addNumberDecimals(co2Equivalency),
begin: begin,
end: end,
iso: this._getIso(),
week: this.currentStep,
url: GFW_URL
});
this.legendImage = el.querySelector('.image');
this.showImage();
}
},
_updateLegends: function(state) {
if (this.currentStep !== state.step) {
this.currentStep = state.step;
this.maxData = state.maxData;
this.legends.forEach(
function(legend) {
this._renderLegend(legend.element, legend.data, legend.filter);
}.bind(this)
);
}
},
_changeVisualizations: function(ev) {
var current = ev.currentTarget;
var filter = current.dataset.filter;
this._changeMainVis(filter);
},
_changeMainVis: function(filter) {
this._toggleFilter(this.defaults.mainVisSwitchEl, filter);
this.visMain.updateByFilter(filter);
var legend = _.findWhere(this.legends, {
category: 'main'
});
legend.filter = filter;
this._renderLegend(legend.element, legend.data, legend.filter);
},
_clearVisualization: function() {
if (this.visMain) {
this.visMain.remove();
this.legends = [];
this.images = {};
var legend = this.el.querySelector(
'.' + this.defaults.legendSelectoClassEl
);
legend.innerHTML = '';
}
},
_changeDataByCountry: function(ev) {
var current = ev.currentTarget;
var iso = current.value;
var selected = current.querySelector('[data-iso="' + iso + '"]');
mps.publish('Router/goInsight', [iso]);
this._setCurrentCountry(selected.text, iso);
this._renderMainChart();
},
_setCurrentCountry: function(text, iso) {
var label = this.el.querySelector(
'.' + this.defaults.countryLabelClassEl
);
var selector = this.el.querySelector(
'.' + this.defaults.countrySelectorClassEl
);
label.innerHTML = text;
selector.value = iso;
this.currentCountry = iso;
},
_changeDataByYear: function(ev) {
var current = ev.currentTarget;
this.currentYear = parseInt(current.value, 10);
this._renderMainChart();
},
_changeYear: function(ev) {
var action = ev.currentTarget.dataset.action;
var $parent = ev.currentTarget.parentNode;
var $selector = $parent.querySelector('.js-year-selector');
var options = $selector.options;
var current = $selector.selectedIndex;
var max = $selector.length - 1;
var newIndex = current;
var $add = $parent.querySelector('.-js-year-nav-right');
var $sub = $parent.querySelector('.-js-year-nav-left');
if (action === 'add') {
newIndex++;
} else if (action === 'sub') {
newIndex--;
}
if (newIndex < 0) {
newIndex = max;
} else if (newIndex > max) {
newIndex = 0;
}
if (newIndex < max) {
$add.classList.remove('-disabled');
} else {
$add.classList.add('-disabled');
}
if (newIndex === 0) {
$sub.classList.add('-disabled');
} else {
$sub.classList.remove('-disabled');
}
$selector.selectedIndex = newIndex;
this.currentYear = parseInt(options[newIndex].text, 10);
this._renderMainChart();
},
_toggleFilter: function(element, filter) {
var parent = this.el.querySelector('.' + element);
var selected = parent.querySelector(
'.' + this.defaults.selectedClassEl
);
var newSelection = parent.querySelector(
'[data-filter="' + filter + '"]'
);
selected.classList.remove(this.defaults.selectedClassEl);
newSelection.classList.add(this.defaults.selectedClassEl);
this.filter = filter;
},
_openShare: function(event) {
var shareView = new ShareView().share(event);
$('body').append(shareView.el);
},
/**
* Shows the image of the current step
*/
showImage: function() {
var maxData = this.maxData || this.currentStep;
if (!this.images[this.currentStep] && this.currentStep <= maxData) {
var image = new Image();
var currentStep = this.currentStep;
image.onload = this._onImageLoad(currentStep, image);
image.onerror = this._onImageError(currentStep);
image.src =
this.imageURI +
'/' +
this.currentCountry.toUpperCase() +
'_' +
this.currentYear +
'_' +
NumbersHelper.padNumberToTwo(this.currentStep) +
'.png';
} else {
var img = this.images[this.currentStep];
if (img && img.image) {
this._renderLegendImage(img.image);
} else {
this._renderLegendImage(false);
}
}
},
/**
* On image error
* @param {Number} current step
*/
_onImageError: function(currentStep) {
return function(step) {
return function(e) {
this.images[step] = {
loaded: false,
image: null
};
if (step === this.currentStep) {
this._renderLegendImage(false);
}
}.bind(this);
}.bind(this)(currentStep);
},
/**
* Updates the tooltip of the current step
* @param {Number} current step
* @param {Object} image object
*/
_onImageLoad: function(currentStep, image) {
return function(step, image) {
return function(e) {
this.images[step] = {
loaded: true,
image: image
};
if (step === this.currentStep) {
this._renderLegendImage(image);
}
}.bind(this);
}.bind(this)(currentStep, image);
},
/**
* Renders the image or the error message
* @param {Object} image object
*/
_renderLegendImage: function(image) {
this.legendImage.innerHTML = '';
this.legendImage.classList.remove('-no-data');
if (image) {
this.legendImage.appendChild(image);
} else {
this.legendImage.innerHTML = 'Not available';
this.legendImage.classList.add('-no-data');
}
}
});
return InsightsGladAlerts;
}
);