Skip to content
Snippets Groups Projects
Select Git revision
  • main
  • master
2 results

multiseries.js

Blame
  • elimeri's avatar
    elimeri authored
    9e5b2a28
    History
    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 `-`;
        }
    }