app/assets/javascripts/insights/views/InsightsGladAlertsView.js
define([
'backbone',
'mps',
'handlebars',
'd3',
'underscore',
'_string',
'insights/views/InsightsGladAlertsChartView',
'views/ShareView',
'views/SourceModalView',
'helpers/NumbersHelper',
'text!insights/templates/insights-glad-alerts.handlebars',
'text!insights/templates/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: 2017,
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);
},
_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,
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;
});