Select Git revision
multiseries.js
elimeri authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
multiseries.js 16.72 KiB
/**
* @file Archivo sobre los graficos de la visualizacion, el grafico de lineas
* @author Elisa Merino Gonzalez
*/
/*________________________________________________ CONSTANTS _________________________________________________*/
/********* Entidades ********/
const COLORS = {
"Spain":'rgb( 255, 0, 0)',
"World": 'rgb( 190, 190, 190 )', "Africa": 'rgb(0,0,0)',"Asia": 'rgb(206, 197, 0 )',"Europe": 'rgb(103, 236, 10)',
"NorthAmerica": 'rgb(194, 16, 53)', "CentralAmerica": 'rgb( 160, 64, 0)', "SouthAmerica": 'rgb( 255, 169, 38)',
"Oceania": 'rgb(0, 195, 192)',
"HighIncome": 'rgb(17, 158, 0)', "LowerMiddleIncome": 'rgb(84, 103, 163)', "UpperMiddleIncome": 'rgb(201, 0, 186)'
};
const NAMES_SIZE = "6.5pt";
const TAB_SIZE= 15;
/******* Lines & points ******/
const HIDDEN_OPACITY = 0.3;
const LINES_WIDTH = 1.8;
const POINT_RADIUS = 2.2;
/***** Info-boxes ******/
const INFOBOX_WIDTH = 149;
const INFOBOX_HEADER_H = 20;
const INFOBOX_BODY_H = 40;
const INFOBOX_TEXT_SIZE = "9.5px";
const INFOBOX_BACKG_COLOR = "lightblue";
/****** SVG *******/
const X_MARGIN_RIGHT = 80;
const X_MARGIN_LEFT = 120;
const Y_MARGIN_TOP = 60;
const Y_MARGIN_BOTTOM = 30;
const X_AXIS_TICKS = 8;
const Y_AXIS_TICKS = 10;
/**
* Main function. It builds and draws the linear graphic.
* @param {*} container id of the svg container
* @param {*} width desired width of the graphic
* @param {*} height desired height of the graphic
* @param {*} entidades list of selected entidades
*/
function drawMultiseries(container, width, height, entidades) {
let selectedData = null;
let lastYearData = null;
let butLastYearData = null;
let maxPorcentajes = null;
let w = width;
let h = height;
let x = d3.scaleLinear().domain([1965,2022]).range([0,w]);
let y = d3.scaleLinear().range([h,0]);
let xFormat = d3.format("d");
let xAxis = d3.axisBottom().scale(x).ticks(X_AXIS_TICKS).tickFormat(xFormat);
let yAxis = d3.axisLeft().scale(y).ticks(Y_AXIS_TICKS).tickFormat(xFormat);
var svg = d3.select(container).append("svg")
.attr("width",w+X_MARGIN_LEFT+X_MARGIN_RIGHT)
.attr("height",h+Y_MARGIN_TOP+Y_MARGIN_BOTTOM)
.append("g")
.attr("id","main-group")
.attr("transform", "translate("+X_MARGIN_LEFT+","+Y_MARGIN_TOP+")");
setEntidadesNamesColors();
/* Appends xAxis */
svg.append("g").attr("class","xAxis")
.attr("transform","translate(0,"+h+")")
.call(xAxis);
/* Appends the y-Axis title */
svg.append("text").attr("id","yAxis-title1").text("porcentajes")
.style("font-size","7pt").attr("x",-20).attr("y",-20);
svg.append("text").attr("id","yAxis-title2").text("%")
.style("font-size","7pt").attr("x",-5).attr("y",-10);
/* Unckecks every country and checks the selected entities */
d3.selectAll(".checkbox-country").property("checked",false);
entidades.forEach(function(d) {
d3.select("#checkbox-"+d).property("checked", true);
});
/* Adds points and lines */
d3.csv("./data/renewable-share-energy.csv")
.then(function (allData) {
selectedData = getSelectedData(allData, entidades); // Gets the data of the selected entidades only
lastYearData = getAYearData(selectedData, 2021); // Gets the selected data of the last year only
butLastYearData = excludeAYearData(selectedData, 2021); // Gets the selected data between 1970 y 2015. 2016 is omitted.
maxPorcentajes = getMaxPorcentajes(selectedData); //Obtains the maximum # of incidents among the selected entidades
/* Sets the domain of the y and appends the y-axis*/
y.domain([0,maxPorcentajes]);
svg.append("g").attr("class","yAxis").call(yAxis);
/*Adds the country names */
var names_group = svg.append("g").attr("id","names");
var names = names_group.selectAll(".name-country").data(lastYearData)
.enter()
.append("text")
.attr("class","name-country")
.attr("id",function(d) {
return "name-"+d.Entity;
})
.attr("x",function(d) {
let tabs = getTabs(lastYearData,""+d.Entity,+d.Porcentaje,Math.abs(y(1) - y(2)));
return x(2021) + tabs*TAB_SIZE;})
.attr("y", function(d) {
return y(+d.Porcentaje);
})
.style("font-size",NAMES_SIZE)
.attr("fill",function(d) {
let entity = "" + d.Entity;
return COLORS[entity];
})
.text(function(d) {
return ""+d.Entity;
});
/* Adds the lines */
var lines_group = svg.append("g").attr("id","lines");
var lines = lines_group.selectAll(".line").data(butLastYearData)
.enter()
.append("line")
.attr("class","line")
.attr("id",function(d) {
let nextYear = +d.Year +1;
return "line-"+d.Entity+"-"+d.Year+"-"+nextYear;
})
.style("stroke-width",LINES_WIDTH)
.style("stroke",function(d) {
return COLORS[""+d.Entity];
})
.attr("x1",function(d) {
return x(+d.Year);
})
.attr("x2",function(d) {
let nextYear = +d.Year+1;
return x(nextYear);
})
.attr("y1",function(d) {
return y(+d.Porcentaje);
})
.attr("y2",function(d) {
let nextYear = +d.Year+1;
let nextYearPorcentajes = getPorcentajes(selectedData,""+d.Entity,nextYear);
return y(nextYearPorcentajes);
})
.on("mouseover",onMouseoverLines)
.on("mouseout", onMouseoutLines);
/*Adds the points */
var points_group = svg.append("g").attr("id","points");
var points = points_group.selectAll(".point").data(selectedData)
.enter()
.append("circle")
.attr("class","point")
.attr("id", function(d) {
return "point-" +d.Entity +"-" + d.Year;
})
.attr("cx", function(d) {
return x(+d.Year);
})
.attr("cy", function(d) {
return y(+d.Porcentaje);
})
.attr("r",POINT_RADIUS+"px")
.attr("stroke",function(d) {
let entity = d.Entity +"";
return ''+COLORS[entity];
})
.attr("stroke-width",(3*POINT_RADIUS/4)+"px")
.attr("fill", "white")
.on("mouseout",onMouseoutPoints)
.on("mouseover", function() {
let id = this.id;
let pieces = id.split("-");
let entity = pieces[1];
let year = pieces[2]-0;
let porcentajes = getPorcentajes(selectedData,entity,year);
let variation = getVariation(selectedData,entity,year-1,year);
let variationStr = formatVariation(variation);
let infobox = svg.append("g").attr("class","info-box");
infobox.append("rect").attr("x",x(year)).attr("y",y(porcentajes)-(INFOBOX_HEADER_H+INFOBOX_BODY_H))
.attr("height",INFOBOX_HEADER_H).attr("width",INFOBOX_WIDTH)
.style("fill",INFOBOX_BACKG_COLOR).style("stroke","black");
infobox.append("text").text(entity+" - "+year).attr("x",x(year)+10).attr("y", y(porcentajes)-45)
.style("font-size",INFOBOX_TEXT_SIZE);
infobox.append("rect").attr("x",x(year)).attr("y",y(porcentajes)-INFOBOX_BODY_H)
.attr("height",INFOBOX_BODY_H).attr("width",INFOBOX_WIDTH)
.style("fill",INFOBOX_BACKG_COLOR).style("stroke","black");
infobox.append("text").text(porcentajes.toFixed(2)+" %").attr("x",x(year)+10)
.attr("y", y(porcentajes)-23).style("font-size",INFOBOX_TEXT_SIZE);
});
});
}
/* _____________________________________ EVENT HANDLERS __________________________________*/
/**
* Handles the 'mouse over' event on a line.
*/
function onMouseoverLines() {
let id = this.id;
let pieces = id.split("-");
let country = pieces[1]+"";
let lines = d3.selectAll(".line").filter(function(d) {
if(d.Entity +"" != country) { return d;}
});
let points = d3.selectAll(".point").filter(function(d) {
if(d.Entity +"" != country) { return d;}
});
let names = d3.selectAll(".name-country").filter(function(d) {
if(d.Entity +"" != country) { return d;}
});
lines.style("opacity",HIDDEN_OPACITY);
points.style("opacity",HIDDEN_OPACITY);
names.style("opacity",HIDDEN_OPACITY);
}
/**
* Handles the 'mouse out' event on a line.
*/
function onMouseoutLines() {
let lines = d3.selectAll(".line");
let points = d3.selectAll(".point");
let names = d3.selectAll(".name-country");
lines.style("opacity","1");
points.style("opacity","1");
names.style("opacity","1");
}
/**
* Handles the 'mouse over' event on an historical event
* mark.
*/
function onMouseoverEvents () {
let point_id = this.id;
let pieces = point_id.split("-");
let point = d3.select("#"+point_id);
let line_id = "#event-line-"+pieces[2];
let line = d3.select(line_id);
let description_id = "#event-description-"+pieces[2];
let description = d3.select(description_id);
let date_id = "#event-date-"+pieces[2];
let date = d3.select(date_id);
point.style("opacity",1).style("fill",EVENTS_MARKS_OVER_COLOR);
line.style("opacity",1).style("stroke",EVENTS_MARKS_OVER_COLOR);
description.style("opacity",1);
date.style("opacity",1);
}
/**
* Handles the 'mouse out' event on an historical event mark.
*/
function onMouseoutEvents() {
let points = d3.selectAll(".event-point");
let lines = d3.selectAll(".event-line");
let descriptions = d3.selectAll(".event-description");
let dates = d3.selectAll(".event-date");
points.style("opacity",EVENTS_DEFAULT_OPACITY).style("fill",EVENTS_MARKS_DEF_COLOR);
lines.style("opacity",EVENTS_DEFAULT_OPACITY).style("stroke",EVENTS_MARKS_DEF_COLOR);
descriptions.style("opacity",0);
dates.style("opacity",0);
}
/**
* Handles the 'mouse out' on a point.
*/
function onMouseoutPoints () {
d3.selectAll(".info-box").remove();
}
/* __________________________________ UTILITY FUNCTIONS _________________________________*/
/**
* Sets the color of every checkbox name.
*/
function setEntidadesNamesColors() {
let country;
let id = "#li-"
for(let entity in COLORS) {
country = d3.select(id+entity);
country.style("color",COLORS[entity]);
}
}
/**
* Obtains the tabs which a given country's name label needs.
* @param {*} data countrie's data
* @param {*} entity a three-letter entity
* @param {*} porcentajes number of incidents
* @param {*} scale
*/
function getTabs(data,entity,porcentajes,scale) {
let margin = 5*scale;
let tabs = 1;
let itsSelfFound = false;
for(let d of data) {
let entityTemp = "" +d.Entity;
let porcentajesTemp = +d.Porcentaje;
if( (porcentajesTemp < porcentajes+margin) && (porcentajesTemp > porcentajes-margin) ) {
if(entityTemp === entity) {
return tabs;
} else {
tabs+=1;
}
}
}
return tabs;
}
/**
* Obtains th number of terrorist incidents ocurred in a given country
* in a given year.
* @param {*} data
* @param {*} entity three-letter entity indicating the country
* @param {*} year
*/
function getPorcentajes(data,entity,year) {
for(let d of data) {
let entityTemp = "" +d.Entity;
if( entityTemp === entity && (+d.Year) === year) {
return +d.Porcentaje;
}
}
}
/* Gets the data of the selected entities only */
function getSelectedData(data, entidades) {
let selectedData = [];
for(let d of data) {
if(entidades.includes(d.Entity)) {
selectedData.push(d);
}
}
return selectedData;
}
/**
* Obtains the maximum number among the data.
* @param {*} data
*/
function getMaxPorcentajes(data) {
let maxPorcentajes = 0;
for(let d of data) {
if(+d.Porcentaje > maxPorcentajes) {
maxPorcentajes = +d.Porcentaje;
}
}
return maxPorcentajes;
}
/**
* Obtains a given year's data.
* @param {*} data all the data
* @param {*} year the wanted year
*/
function getAYearData(data, year) {
let aYearData = [];
for(let d of data) {
if(+d.Year === year) {
aYearData.push(d);
}
}
return aYearData;
}
/**
* Omits a given year's data.
* @param {*} data all the data
* @param {*} year the not wanted year that must be excluded.
*/
function excludeAYearData(data, year) {
let excludeAYearData = [];
for(let d of data) {
if(+d.Year !== year) {
excludeAYearData.push(d);
}
}
return excludeAYearData;
}
/**
* Calculatates the increase or decrease (%) in the number of terrorist incidents
* in a given country from one year to another.
* @param {*} data
* @param {*} entity the entity of the country
* @param {*} year the initial year
* @param {*} nextYear the final year
*/
function getVariation(data, entity, year, nextYear) {
let porcentajesYear = getPorcentajes(data,entity, year);
let porcentajesNextYear = getPorcentajes(data,entity,nextYear);
if(porcentajesYear === 0) {
return NaN;
}
let variation = (porcentajesNextYear - porcentajesYear) / porcentajesYear * 100;
return variation;
}
/**
* Converts the variation into a proper formatted string.
* @param {*} variation the variation in the incidents.
*/
function formatVariation(variation) {
if(variation === NaN || variation === Infinity || variation === -Infinity) {
return '';
}
if(variation > 0) {
return `+${variation.toFixed(2)}%`;
} else if (variation < 0) {
return `${variation.toFixed(2)}%`;
} else {
return `-`;
}
}