Source: histogram_module.js



/**
 * Módulo histograma, utilizado para crear y gestionar los histogramas en nicho ecológico y comunidad ecológica.
 *
 * @namespace histogram_module
 */
var histogram_module = (function(verbose) {

    var _VERBOSE = verbose;
    var _table_module_decil, _language_module_nicho;
    var _highlight_color = "#48D7D5";
    var _iTrans;
    var _NUM_DECIL = 10;



    /**
     * Método setter del módulo table.
     *
     * @function setTableModule
     * @public
     * @memberof! histogram_module
     * 
     * @param {object} tableModule - Módulo table
     */
    function setTableModule(tableModule) {
        _table_module_decil = tableModule;
    }


    /**
     * Método setter del módulo de internacionalización.
     *
     * @function setLanguageModule
     * @public
     * @memberof! histogram_module
     * 
     * @param {object} languageModule - Módulo de internacionalización.
     */
    function setLanguageModule(languageModule) {
        _language_module_nicho = languageModule;
        _iTrans = _language_module_nicho.getI18();
    }
    
    /**
     * Éste método inicializa variables globales y componentes visuales que son necesarios para la creación de histogramas.
     *
     * @function _initilizeHistogram
     * @private
     * @memberof! histogram_module
     * 
     */
    function _initilizeHistogram() {

        _VERBOSE ? console.log("_initilizeHistogram") : _VERBOSE;
        
        
        

    }


    /**
     * Éste método crea el histograma de score decil del sistema de nicho, genera la interacción de tooltips, genera la interacción con histogramas y genera un line chart cuando el proceso de validación esta activado. Además genera una tabla de información por decil.
     *
     * @function createMultipleBarChart
     * @public
     * @memberof! histogram_module
     * 
     * @param {array} json_decil - Array con el resultado de los grupos de vartiables seleccionados por decil
     * @param {array} array_recall - Contiene los resultados por grupo de variables por decil. Contiene verdaderos positivos, falsos negativos y recall. Este array solo es utilizado en el proceso de validación
     * @param {String} idComponent - Id del contenedor del histograma
     * @param {map} nameMap - Mapa de los grupos de variables seleccionados en el análisis de nicho ecológico
     */
    function createMultipleBarChart(json_decil, array_recall, idComponent, nameMap) {

        _VERBOSE ? console.log("createMultipleBarChart") : _VERBOSE;

        $("#" + idComponent.id).empty();

        var margin = {top: 30, right: 40, bottom: 50, left: 40},
        width = $(".myScrollableBlockEpsilonDecil").width() - margin.left - margin.right,
                height = $(".myScrollableBlockEpsilonDecil").height() - margin.top - margin.bottom - 15; // 10px del icono de info


        var x0 = d3.scale.ordinal()
                .rangeRoundBands([0, width], .1);

        var x1 = d3.scale.ordinal();

        var y = d3.scale.linear()
                .range([height, 0]);

        var color_array = [];

        // variable que sirve para conocer el decil donde ya existen valores prar generar la gráfica
        var value_decil_ready = 0;

        for (value_decil_ready = 0; value_decil_ready < _NUM_DECIL; value_decil_ready++) {
            try {
                length_names = json_decil[value_decil_ready].names.length;
                break;
            } catch (e) {
                _VERBOSE ? console.log("sin valores decil: " + value_decil_ready) : _VERBOSE;
            }
        }

        _VERBOSE ? console.log("value_decil_ready: " + value_decil_ready) : _VERBOSE;
        _VERBOSE ? console.log("length_names: " + length_names) : _VERBOSE;


        for (i = 0; i < length_names; i++) {
            color_array.push(_randomColor());
        }
        // _VERBOSE ? console.log(color_array) : _VERBOSE;


        var color = d3.scale.ordinal()
                .range(color_array);

        var xAxis = d3.svg.axis()
                .scale(x0)
                .orient("bottom");

        var yAxis = d3.svg.axis()
                .scale(y)
                .orient("left")
                .tickFormat(d3.format(".2s"));
        // .ticks(10);  


        var svg = d3.select("#" + idComponent.id).append("svg")
                .attr("width", width + margin.left + margin.right)
                .attr("height", height + margin.top + margin.bottom)
                .append("g")
                .attr("transform", "translate(" + margin.left + "," + margin.top + ")");


        svg.append("text")
                .attr("id", idComponent.legend)
                .attr("y", -22)
                .attr("x", width / 2 + 40)
                .attr("dy", ".71em")
                .style("font-size", "20px")
                .style("text-anchor", "end")
                .text(_iTrans.prop('titulo_hist_score_decil'));



        var tip = d3.tip()
                .attr('class', 'd3-tip')
                .offset([-10, 0])
                .html(function(d) {

                    var_group_label = "<strong>" + d.name.p + "</strong><br/>" +
                            "<strong>" + _iTrans.prop('lb_score_conjunto') + ":</strong> <span >" + parseFloat(d.value.p).toFixed(2) + "</span><br/><br/>" +
                            "<strong>" + _iTrans.prop('lb_conformado') + ":</strong><br/><br/>";

                    d.name.s.forEach(function(item, index) {

                        var_group_label += "<strong>" + _iTrans.prop('lb_grupo') + ":</strong> <span >" + item + "</span><br/>" +
                                "<strong>" + _iTrans.prop('tip_tbl_score') + ":</strong> <span >" + d.value.s[index] + "</span><br/><br/>"
                    });

                    return  var_group_label;

                });

        svg.call(tip);

        // nameMap es mayor que 0 cuando se realiza el proceso de validación
        var ageNames = []; //nameMap.values();

        _VERBOSE ? console.log(nameMap) : _VERBOSE;

        json_decil[value_decil_ready].names.forEach(function(name, index) {

            var obj;

            if (nameMap.values().length > 0) {
                arg_nulos = nameMap.get(name.p).recall_nulo;
                // _VERBOSE ? console.log(arg_nulos) : _VERBOSE;
                // si al menos existe un valor calculado de recall (diferente a cero) se realiza el line chart del recall
                fin_recall = ($.inArray(false, arg_nulos) != -1) ? false : true;
                _VERBOSE ? console.log("name: " + name.p + " fin_recall: " + fin_recall) : _VERBOSE;

                obj = {name: name.p, nulos: nameMap.get(name.p).nulos, recall_nulo: fin_recall};
            }
            else {
                obj = {name: name.p};
            }

            ageNames.push(obj);

        });

        _VERBOSE ? console.log(ageNames) : _VERBOSE;


        json_decil.forEach(function(d, index) {
            // no retorna el objeto json cuando el decil no tiene valores
            if (value_decil_ready <= index) {
                d.ages = d.names.map(function(name, i) {
                    return {name: name, value: d.values[i], decil: d.decil, species: d.species[i], gridids: d.gridids[i]};
                });
            }
            // asigna valores falsos a los deciles ausentes
            else {
                // d.decil_nulo = true;
                d.ages = json_decil[value_decil_ready].names.map(function(name, i) {
                    return {name: name, value: [], decil: "" + (_NUM_DECIL - index), species: [], gridids: [], decil_nulo: true};
                });
            }
        });

        _VERBOSE ? console.log(json_decil) : _VERBOSE;


        x0.domain(json_decil.map(function(d) {
            return d.decil;
        }));
        // _VERBOSE ? console.log("x0: " + x0.rangeBand()) : _VERBOSE;
        x1.domain(ageNames.map(function(item, i) {
            return item.name;
        })).rangeRoundBands([0, x0.rangeBand()]);



        totalmax_avg = d3.max(json_decil.map(function(d, index) {
            if (value_decil_ready <= index) {
                return d3.max(d.values, function(d) {
                    return parseFloat(d.p)
                });
            }
        }));

        totalmin_avg = d3.min(json_decil.map(function(d, index) {
            if (value_decil_ready <= index) {
                return d3.min(d.values, function(d) {
                    return parseFloat(d.p)
                });
            }
        }));

        // que ocurre cuando min y max son iguales, no se esta generando la gráfica... cehcar validacion
        if (totalmax_avg == totalmin_avg) {
            if (totalmax_avg < 0) {
                totalmax_avg = 0;
            }
            else {
                totalmin_avg = 0;
            }
        }


        totalmin_nice = Math.floor(totalmin_avg);
        totalmax_nice = Math.ceil(totalmax_avg);


        // TODO: analizar cuando son solo possitovos, solo negativos o combinación
        // solo negativos
        if (totalmax_avg < 0 && totalmin_avg < 0) {
            NEG_DECIL = -1;
        }
        // combinado
        else if (totalmin_avg < 0) {
            NEG_DECIL = 0;
        }
        // solo positivos
        else {
            NEG_DECIL = 1;
        }

        // NOTA: En d3 el max de -0.12 y -0.86 es "-0.86", obtiene el minimo de los negativos
        maxmin_avg = d3.max(json_decil.map(function(d, index) {
            if (value_decil_ready <= index) {
                return d3.max(d.values, function(d) {
                    if (d.p < 0)
                        return d.p
                });
            }
        }));


        // si existen valores combinados el axis se divide de forma equitativa
        if (NEG_DECIL == 0) {

            if (Math.abs(totalmin_avg) < Math.abs(totalmax_avg)) {

                y.domain([-totalmax_avg, totalmax_avg])
                        .nice([totalmin_nice, totalmax_nice]);

            }
            else {

                y.domain([totalmin_avg, -totalmin_avg])
                        .nice([totalmin_nice, totalmax_nice]);

            }

        }
        else {

            y.domain([totalmin_avg, totalmax_avg])
                    .nice([totalmin_nice, totalmax_nice]);

        }


        // set the x axis in middle of the chart when there are negative values.
        // solo negativos
        if (NEG_DECIL == -1) {

            translate_axis = y(totalmax_avg); // valor de cero, ajusta en la parte superior del histograma
        }
        // combinado
        else if (NEG_DECIL == 0) {

            translate_axis = y(Math.max(0, maxmin_avg)); // ajusta en la parte superior del histograma
        }
        // solo positivos
        else {

            translate_axis = height; // valor maximos, ajusta en la parte inferior del histograma
        }

        svg.append("g")
                .attr("class", "x axis")
                .attr("transform", "translate(0," + translate_axis + ")")
                .call(xAxis)
                .append("text")
                .attr("id", idComponent.xaxis)
                .attr("y", margin.bottom - 10)
                .attr("x", width / 2)
                .attr("dy", ".71em")
                .style("text-anchor", "end")
                .text(_iTrans.prop('lb_xaxis_decil'));

        svg.append("g")
                .attr("class", "y axis")
                .call(yAxis)
                .append("text")
                .attr("id", idComponent.yaxis)
                .attr("y", -10)
                .attr("x", margin.left - 40)
                .attr("dy", ".71em")
                .style("text-anchor", "end")
                .text(_iTrans.prop('tip_tbl_score'));



        var state = svg.selectAll(".state")
                .data(json_decil)
                .enter().append("g")
                .attr("class", "state")
                .attr("transform", function(d) {
                    return "translate(" + x0(d.decil) + ",0)";
                });

        state.selectAll(".rect")
                .data(function(d) {
                    return d.ages;
                })
                .enter().append("rect")
                .attr("class", "bar")
                .attr("width", x1.rangeBand())
                .attr("x", function(d) {

                    // _VERBOSE ? console.log("decil_nulo: " + d.decil_nulo) : _VERBOSE;
                    // _VERBOSE ? console.log(x1(d.name.p)) : _VERBOSE;
                    return x1(d.name.p);
                })
                .attr("y", function(d) {


                    _VERBOSE ? console.log("decil_nulo: " + d.decil_nulo) : _VERBOSE;

                    if (d.decil_nulo)
                        return y(0);

                    // locate the bars in the chart correctly when there are negative values.
                    if (NEG_DECIL == -1) {
                        return y(totalmax_avg);
                    }
                    else if (NEG_DECIL == 0) {
                        return y(Math.max(0, d.value.p));
                    }
                    else {
                        return y(d.value.p);
                    }
                })
                .attr("height", function(d) {

                    _VERBOSE ? console.log("decil_nulo: " + d.decil_nulo) : _VERBOSE;
                    var val_h = d.decil_nulo ? 0 : d.value.p;

                    // locate the bars in the chart correctly when there are negative values.
                    if (NEG_DECIL == -1) {

                        return Math.abs(y(val_h) - y(totalmax_avg))
                    }
                    else if (NEG_DECIL == 0) {

                        return Math.abs(y(val_h) - y(0))
                    }
                    else {

                        return height - y(val_h);
                    }
                })
                .style("fill", function(d) {

                    // _VERBOSE ? console.log(d.name.p) : _VERBOSE;

                    return color(d.name.p);
                })
                .style("opacity", 0.7)
                .on('mouseover', function(d) {

                    tip.show(d);

                })
                .on('mouseout', function(d) {

                    tip.hide(d);

                })
                .on("click", function(d) {

                    _VERBOSE ? console.log("decil_nulo: " + d.decil_nulo) : _VERBOSE;
                    if (d.decil_nulo)
                        return;

                    // _VERBOSE ? console.log(d.gridids.p) : _VERBOSE;

                    g = d3.select("#grid_map");
                    feature = g.selectAll("path");

                    feature.each(function(cell, i) {

                        if ($.inArray(cell.properties.gridid, d.gridids.p) != -1) {
                            d3.select(this).style("stroke", _highlight_color);
                            d3.select(this).style("stroke-width", 2);
                        } else {
                            d3.select(this).style("stroke", "none");
                            d3.select(this).style("stroke-width", "none");
                        }

                    });

                    state.selectAll("rect.bar").each(function(bar, i) {
                        d3.select(this).style("stroke", "none");
                    });
                    d3.select(this).style("stroke", "black");
                    d3.select(this).style("stroke-width", 3);

                    decil_list = [];
                    d.species.p.forEach(function(sp, index) {

                        // spid, label, epsilon, score, occ(nj), occ_decil
                        params = sp.split("|");
                        // _VERBOSE ? console.log(params) : _VERBOSE;

                        occ = parseFloat(params[4]);
                        occ_decil = parseFloat(params[5])
                        per_decil = parseFloat(occ_decil / occ * 100).toFixed(2) + "%";

                        // console.log(params[1]);
                        // console.log(params[4]);
                        // console.log(params[5]);

                        decil_list.push({decil: d.decil, species: params[1], epsilons: params[2], scores: params[3], occ: per_decil});
                    });

                    // _VERBOSE ? console.log(decil_list) : _VERBOSE;

                    _table_module_decil.createDecilList(decil_list);

                });

        decil_list = [];
        $.each(json_decil, function(index, value) {

            _VERBOSE ? console.log("decil_nulo: " + value.decil_nulo) : _VERBOSE;
            if (value.decil_nulo)
                return false;

            value.species[0].p.forEach(function(sp, index) {

                // spid, label, epsilon, score, occ(nj), occ_decil
                params = sp.split("|");
                // _VERBOSE ? console.log(params) : _VERBOSE;

                // EJEMPLO Panthera leo - occ_total = 2 y occ_decil = 3 => 1.5 => 150% (ERROR)
                occ_total = parseFloat(params[4]);
                occ_decil = parseFloat(params[5]);
                per_decil = parseFloat(occ_decil / occ_total * 100).toFixed(2) + "%";

                decil_list.push({decil: value.decil, species: params[1], epsilons: params[2], scores: params[3], occ: per_decil});
            });

            return false;
        });


        _table_module_decil.createDecilList(decil_list);


        /***************** rect legend */
        var yTextPadding = -5;

        state.selectAll(".rect")
                .data(function(d) {
                    return d.ages;
                })
                .enter()
                .append("text")
                .style("font-size", "8px")
                .attr("text-anchor", "middle")
                .attr("fill", "black")
                .attr("x", function(d, i) {
                    return x1(d.name.p) + 10;
                })
                .attr("y", function(d, i) {

                    if (d.decil_nulo)
                        return y(0) + yTextPadding;
                    // locate the bars in the chart correctly when there are negative values.
                    if (NEG_DECIL == -1) {
                        return y(totalmax_avg) + yTextPadding;
                    }
                    else if (NEG_DECIL == 0) {
                        return y(Math.max(0, d.value.p)) + yTextPadding;
                    }
                    else {
                        return y(d.value.p) + yTextPadding;
                    }

                })
                .text(function(d) {
                    // // console.log(d);
                    // if( parseFloat(d.value.p) == 0.0)
                    // 	return "";
                    // else
                    return d.value.p;
                });



        var legend = svg.selectAll(".legend")
                .data(ageNames.slice().reverse())
                .enter().append("g")
                .attr("class", "legend")
                .attr("transform", function(d, i) {
                    return "translate(0," + (height + 20) + ")";
                });

        legend.append("rect")
                .attr("x", function(d, i) {
                    return (width - 18) - (i * 130);
                })
                .attr("y", 3)
                .attr("width", 20)
                .attr("height", 20)
                .style("fill", function(d, i) {

                    if (d.recall_nulo) {
                        return "black";
                    }
                    else {
                        return color(d.name);
                    }

                })
                .style("opacity", 0.7);

        legend.append("text")
                .attr("x", function(d, i) {
                    return (width - 24) - (i * 130);
                })
                .attr("y", 9)
                .attr("dy", ".35em")
                .style("text-anchor", "end")
                .text(function(d) {
                    return d.name;
                });

        if (nameMap.values().length > 0) {

            legend.append("text")
                    .attr("x", function(d, i) {
                        return (width - 24) - (i * 130);
                    })
                    .attr("y", 20)
                    .attr("dy", ".35em")
                    .style("text-anchor", "end")
                    .text(function(d) {
                        _VERBOSE ? console.log(d.recall_nulo) : _VERBOSE;

                        prom = 0;
                        $.each(d.nulos, function() {
                            prom += this;
                        });

                        if (d.recall_nulo) {
                            _VERBOSE ? console.log("aplica recall nulo") : _VERBOSE;

                            return _iTrans.prop('recall_nulo');
                        }
                        else {
                            return _iTrans.prop('lb_nulos') + ": " + parseFloat(prom / d.nulos.length).toFixed(2);
                        }

                    });


        }


        if ($("#chkValidation").is(':checked')) {

            var tip_recall = d3.tip()
                    .attr('class', 'd3-tip')
                    .offset([-10, 0])
                    .html(function(d) {
                        recall_values = "<strong>" + _iTrans.prop('lb_recall_avg') + ":</strong> <span >" + parseFloat(d.recall * 100).toFixed(2) + "%</span><br/><br/>" +
                                "<strong>" + _iTrans.prop('lb_vp_avg') + ":</strong> <span >" + parseFloat(d.vp).toFixed(2) + "</span><br/>" +
                                "<strong>" + _iTrans.prop('lb_fn_avg') + ":</strong> <span >" + parseFloat(d.fn).toFixed(2) + "</span><br/>";
                        // "<strong>CSS prom:</strong> <span >" + parseFloat(d.ausentes).toFixed(2) + "%</span><br/>"
                        return  recall_values;
                    });

            svg.call(tip_recall);


            var y_right = d3.scale.linear()
                    .range([translate_axis, 0]);

            y_right.domain([0, 1]);

            var yRightAxis = d3.svg.axis()
                    .scale(y_right)
                    .orient("right")
                    .ticks(5, "%");

            // ** si el label cambia, es necesario agregar id **
            svg.append("g")
                    .attr("class", "y axis")
                    .attr("transform", "translate(" + width + " ,0)")
                    .call(yRightAxis)
                    .append("text")
                    .attr("y", -10)
                    .attr("x", margin.right - 40)
                    .attr("dy", ".71em")
                    .style("text-anchor", "end")
                    .text(_iTrans.prop('lb_recall'));

            // adding recall line
            _VERBOSE ? console.log(array_recall) : _VERBOSE;

            $.each(array_recall, function(i, recall_item) {

                var line = d3.svg.line()
                        .x(function(d, i) {
                            // _VERBOSE ? console.log("movement: " + x1.rangeBand() * (ageNames.length/2)) : _VERBOSE;
                            // _VERBOSE ? console.log("x0: " + x0(array_recall.length - i) + x1.rangeBand()) : _VERBOSE;
                            // (x1.rangeBand() * (ageNames.length/2)) ---> Moves the line to the center of the bars
                            return x0(recall_item.length - i) + (x1.rangeBand() * (ageNames.length / 2));
                        })
                        .y(function(d) {
                            // _VERBOSE ? console.log("y: " + y_right(d.recall)) : _VERBOSE;
                            return y_right(d.recall);
                        });

                svg.append("path")
                        .datum(recall_item)
                        .attr("class", "line")
                        .attr("d", line)
                        .style("stroke", function(d) {
                            // _VERBOSE ? console.log(color(d[0].group_name)) : _VERBOSE;

                            // _VERBOSE ? console.log( nameMap.get(d[0].group_name.name).recall_nulo ) : _VERBOSE;

                            if ($.inArray(false, nameMap.get(d[0].group_name.name).recall_nulo) != -1) {
                                return color(d[0].group_name.name);
                            }
                            else {
                                return "none";
                            }



                        })

                svg.selectAll("dot")
                        .data(recall_item)
                        .enter().append("circle")
                        .attr("class", "dot")
                        .attr("r", 3.5)
                        .attr("cx", function(d, i) {
                            return x0(recall_item.length - i) + (x1.rangeBand() * (ageNames.length / 2));
                        })
                        .attr("cy", function(d) {
                            return y_right(d.recall);
                        })
                        .on('mouseover', function(d) {

                            d3.select(this).attr("r", function(d) {
                                return 8 + Math.pow(d.recall / Math.PI, 0.5);
                            });
                            tip_recall.show(d)
                        })
                        .on('mouseout', function(d) {

                            d3.select(this).attr("r", function(d) {
                                return 3.5;
                            });
                            tip_recall.hide(d)
                        })
                        .style("stroke", function(d) {

                            // _VERBOSE ? console.log( nameMap.get(d.group_name.name).recall_nulo ) : _VERBOSE;

                            if ($.inArray(false, nameMap.get(d.group_name.name).recall_nulo) != -1) {
                                return color(d.group_name.name);
                            }
                            else {
                                return "none";
                            }

                        })


            });

        }


    }


    /**
     * Éste método crea el histograma de las ocurrencias que tiene la especie objetivo selecionada en el análisis de nicho ecológico.
     *
     * @function createBarChartFecha
     * @public
     * @memberof! histogram_module
     * 
     * @param {array} distinctPoints - Array con las ocurrencias que tiene la especie objetivo selecionada en el análisis de nicho ecológico.
     */
    function createBarChartFecha(distinctPoints) {

        var puntos_json = d3.map([]);
        
        $.each(distinctPoints, function(index, item) {

            fecha_ano = item.fechacolecta == null || item.fechacolecta.split("-")[0] == "" ? 0 : parseInt(item.fechacolecta.split("-")[0]);

            if (puntos_json.has(fecha_ano)) {

                var item_colecta = puntos_json.get(fecha_ano);
                puntos_json.set(fecha_ano, {"fechacolecta": fecha_ano, "cantidad": (item_colecta.cantidad + 1)});

            }
            else {

                puntos_json.set(fecha_ano, {"fechacolecta": fecha_ano, "cantidad": 1});

            }

        });

        var colectas = puntos_json.values();
        var colectas_zero = [];
        var colectas_dif = [];
        $.each(colectas, function(index, item) {

            if (item.fechacolecta > 0) {
                colectas_dif.push(item);
            }
            else {
                colectas_zero.push(item);
            }

        })
        
        // VERIFICAR SI SE PUEDEN DESPLEGAR LOS REGISTROS SIN FECHA
        // if(colectas_zero.length == 0){
        // 	$("#lb_regfecha").text("0");
        // }
        // else{
        // 	$("#lb_regfecha").text(colectas_zero[0].cantidad);
        // }

        var max_fecha = d3.max(colectas_dif.map(function(d) {
            return d.fechacolecta;
        }));
        var min_fecha = d3.min(colectas_dif.map(function(d) {
            return d.fechacolecta;
        }));

        var NUM_BARS = d3.range(1, 11, 1);

        var rangofechas = d3.scale.quantize().domain([min_fecha, max_fecha]).range(NUM_BARS);

        var rango_fechas = d3.map([]);
        $.each(NUM_BARS, function(index, item) {

            var rango = parseInt(rangofechas.invertExtent(item)[0]) + " - " + parseInt(rangofechas.invertExtent(item)[1]);
            rango_fechas.set(item, {"fechas": rango, "cantidad": 0});

        })

        $.each(colectas_dif, function(index, item) {

            if (rango_fechas.has(rangofechas(item.fechacolecta))) {

                var it_temp = rango_fechas.get(rangofechas(item.fechacolecta));
                it_temp.cantidad = it_temp.cantidad + item.cantidad;

            }

        });


        var data = rango_fechas.values();
       
        $("#hist_fecha_container").empty();
       
        var margin = {top: 10, right: 10, bottom: 20, left: 30},
        width = $("#hist_fecha_container").width() - margin.left - margin.right,
                height = $("#hist_fecha_container").height() - margin.top - margin.bottom;


        var x = d3.scale.ordinal()
                .rangeRoundBands([0, width], .1);

        var y = d3.scale.linear()
                .range([height, 0]);


        x.domain(data.map(function(d) {
            return d.fechas;
        }));
        y.domain([0, d3.max(data, function(d) {
                return d.cantidad;
            })]);


        var xAxis = d3.svg.axis()
                .scale(x)
                .orient("bottom");

        var yAxis = d3.svg.axis()
                .scale(y)
                // .ticks(10)
                // .tickSize(-width, 0)
                .orient("left");



        var svg = d3.select("#hist_fecha_container").append("svg")
                .attr("width", width + margin.left + margin.right)
                .attr("height", height + margin.top + margin.bottom)
                .append("g")
                .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

        var tip = d3.tip()
                .attr('class', 'd3-tip')
                .offset([-10, 10])
                .html(function(d) {
                    return  "<strong>Fechas:</strong> <span >" + d.fechas + "</span><br/>" +
                            "<strong>Registros:</strong> <span >" + d.cantidad + "</span>";
                });
        svg.call(tip);



        svg.append("g")
                .attr("class", "xaxis")
                .attr("transform", "translate(0," + height + ")")
                .call(xAxis)
                // .text("Fecha colecta")
                .selectAll("text")
                .style("font-size", "0px")


        svg.append("g")
                .attr("class", "yaxis")
                .call(yAxis)
                // .ticks(5);
                // .text("Registros")
                .selectAll("text")
                .style("font-size", "8px")


        svg.selectAll(".bar")
                .data(data)
                .enter().append("rect")
                .attr("class", "bar")
                .attr("x", function(d) {
                    return x(d.fechas);
                })
                .attr("y", function(d) {
                    return y(d.cantidad);
                })
                .attr("height", function(d) {
                    return height - y(d.cantidad);
                })
                .attr("width", x.rangeBand())
                .on("mouseover", function(d) {
                    tip.show(d);
                })
                .on("mouseout", function(d) {
                    tip.hide(d);
                });


    }


    /**
     * Crea los histogramas de épsilon especie, score especie y score celda en el análisis de nicho ecológico.
     *
     * @function createBarChart
     * @public
     * @memberof! histogram_module
     * 
     * @param {String} idComponent - Id del contenedor del histograma
     * @param {array} data - Array con los resultados del histograma por especie y por celda en el análisis de nicho ecológico
     * @param {String} f_legend - Título del histograma
     */
    function createBarChart(idComponent, data, f_legend) {

        _VERBOSE ? console.log("createBarChart") : _VERBOSE;

        $("#" + idComponent.id).empty();
        // _VERBOSE ? console.log($(window).width()) : _VERBOSE;
        // _VERBOSE ? console.log($(".myScrollableBlockEpsilon").height()) : _VERBOSE;

        var margin = {top: 30, right: 40, bottom: 60, left: 40},
        width = $(".myScrollableBlockEpsilonSmallHist").width() - margin.left - margin.right,
                height = $(".myScrollableBlockEpsilonSmallHist").height() - margin.top - margin.bottom;

        // _VERBOSE ? console.log(width) : _VERBOSE;
        // _VERBOSE ? console.log(height) : _VERBOSE;

        var x = d3.scale.ordinal()
                .rangeRoundBands([0, width], .1);

        var y = d3.scale.linear()
                .range([height, 0]);

        var xAxis = d3.svg.axis()
                .scale(x)
                .orient("bottom");
        // .ticks(5);

        var yAxis = d3.svg.axis()
                .scale(y)
                .orient("left")
                .ticks(10, "%");

        var svg = d3.select("#" + idComponent.id).append("svg")
                .attr("width", width + margin.left + margin.right)
                .attr("height", height + margin.top + margin.bottom)
                .append("g")
                .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

        svg.append("text")
                .attr("id", idComponent.legend)
                .attr("y", -22)
                .attr("x", width / 2 + 60)
                .attr("dy", ".71em")
                .style("font-size", "20px")
                .style("text-anchor", "end")
                .text(f_legend);


        // svg.append("rect")
        //   	.attr("y", -22)
        //     	.attr("x", width/2 + 70)
        //     	.attr("width", "20px")
        //     	.attr("height", "20px")
        //     	.attr("fill", "#3B6A93")
        //     	.append("title")
        //     		.text("test")
        //     	.style("margin-left", "20px")
        // svg.append("text")
        //   	.attr("y", -8)
        //     	.attr("x", width/2 + 77)
        //     	.style("fontColor","white")
        //     	.style("fontWeight","bold")
        //   	.text("?")




        var tip = d3.tip()
                .attr('class', 'd3-tip')
                .offset([-10, 0])
                .html(function(d) {
                    return  "<strong>" + _iTrans.prop('lb_rango') + ":</strong> <span >" + d.title + "</span><br/><br/>" +
                            "<strong>" + _iTrans.prop('lb_frecuencia') + ":</strong> <span >" + parseInt(d.frequency * 100) + " %</span>";
                });

        svg.call(tip);


        x.domain(data.map(function(d) {
            return d.bcenter;
        }));

        max_freq = d3.max(data.map(function(d) {
            return parseFloat(d.frequency);
        }));
        // _VERBOSE ? console.log(max_freq) : _VERBOSE;

        y.domain([0, max_freq]);

        svg.append("g")
                .attr("class", "xaxis")
                .attr("transform", "translate(0," + height + ")")
                .call(xAxis)
                .selectAll("text")
                .attr("id", idComponent.xaxis)
                .style("text-anchor", "end")
                .style("font-size", "10px")
                .attr("dx", "-.8em")
                .attr("dy", "-.55em")
                .attr("transform", function(d) {
                    return "rotate(-90)"
                });
        // .append("text")
        //   .attr("y", margin.bottom-10)
        //   .attr("x", width/2)
        //   .attr("dy", ".71em")
        //   .style("text-anchor", "end")
        //   .text(legend);

        svg.append("g")
                .attr("class", "yaxis")
                .call(yAxis)
                .append("text")
                .attr("id", idComponent.yaxis)
                .attr("transform", "rotate(-90)")
                .attr("y", 6)
                .attr("dy", ".71em")
                .style("text-anchor", "end")
                .text(_iTrans.prop('lb_frecuencia'));

        svg.selectAll(".bar")
                .data(data)
                .enter().append("rect")
                .attr("class", "bar")
                .attr("x", function(d) {
                    return x(d.bcenter);
                })
                .attr("width", x.rangeBand())
                .attr("y", function(d) {
                    return y(parseFloat(d.frequency));
                })
                .attr("height", function(d) {
                    return height - y(parseFloat(d.frequency));
                })

                .on('mouseover', function(d) {
                    tip.show(d)
                })
                .on('mouseout', function(d) {
                    tip.hide(d)
                })


    }

    
    /**
     * Genera colores aleatorios para asignarlo al histograma de decil en el análisis de nicho ecológico.
     *
     * @function _randomColor
     * @private
     * @memberof! histogram_module
     * 
     */
    var _randomColor = (function() {
        var golden_ratio_conjugate = 0.618033988749895;
        var h = Math.random();

        var hslToRgb = function(h, s, l) {
            var r, g, b;

            if (s == 0) {
                r = g = b = l; // achromatic
            } else {
                function hue2rgb(p, q, t) {
                    if (t < 0)
                        t += 1;
                    if (t > 1)
                        t -= 1;
                    if (t < 1 / 6)
                        return p + (q - p) * 6 * t;
                    if (t < 1 / 2)
                        return q;
                    if (t < 2 / 3)
                        return p + (q - p) * (2 / 3 - t) * 6;
                    return p;
                }

                var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
                var p = 2 * l - q;
                r = hue2rgb(p, q, h + 1 / 3);
                g = hue2rgb(p, q, h);
                b = hue2rgb(p, q, h - 1 / 3);
            }

            return '#' + Math.round(r * 255).toString(16) + Math.round(g * 255).toString(16) + Math.round(b * 255).toString(16);
        };

        return function() {
            h += golden_ratio_conjugate;
            h %= 1;
            return hslToRgb(h, 0.5, 0.60);
        };
    })();


    /**
     * Retorna un objeto de tipo Chart para generar el histograma en el anáisis de comunidad ecológica.
     *
     * @function createBarChartNet
     * @public
     * @memberof! histogram_module
     * 
     * @param {json} json - Json que contiene los nodos y los enlaces resultado del análisis de communidad ecológica
     * @param {object} display_obj - Referencia al controlador de comunidad ecológica
     */
    function createBarChartNet(json, display_obj) {

        _VERBOSE ? console.log("createBarChartNet") : _VERBOSE;

        var chart_array = [
            BarChart(json, display_obj)
                    .group(display_obj.group_eps_freq)
                    .x(display_obj.x)
        ];

        var chart_component = d3.selectAll(".chart")
                .data(chart_array)
                .each(function(chart) {
                    chart.on("brushend", display_obj.renderAll);
                });
        
        $("#lb_body_info").text(_iTrans.prop('lb_msg_hist_epsilon'));
        $('#lb_header_info').text(_iTrans.prop('titulo_hist_eps'));
        

        return chart_component;

    }


    /**
     * Clase tipo Chart para generar instacias que tienen interacción con el módulo de red.
     *
     * @function BarChart
     * @public
     * @memberof! histogram_module
     * 
     * @param {json} json - Json que contiene los nodos y los enlaces resultado del análisis de communidad ecológica
     * @param {object} display_obj - Referencia al controlador de comunidad ecológica
     */
    function BarChart(json, display_obj) {

        _VERBOSE ? console.log("BarChart") : _VERBOSE;

        var margin = {top: 5, right: 20, bottom: 55, left: 20};
        var width = $("#hist").width() - margin.left - margin.right;
        var height = $("#hist").height() - margin.top - margin.bottom;


        if (!BarChart.id)
            BarChart.id = 0;

        var y = d3.scale.linear()
                .range([100, 0]);
        // .domain([0, max_eps]);

        var x = d3.scale.ordinal()
                .rangeRoundBands([margin.left, width - margin.left], .1);

        var xAxis = d3.svg.axis()
                .scale(x)
                .orient("bottom");

        var yAxis = d3.svg.axis()
                .scale(y)
                .orient("left")
                .ticks(5, "%");

        var id = BarChart.id++,
                brush = d3.svg.brush(),
                brushDirty, dimension, group, round;
        //margin = {top: 10, right: 10, bottom: 20, left: 10}
        var brushStart = 0;
        var brushEnd = display_obj.NUM_BEANS - 1;


        function chart(div) {

            height = y.range()[0];
            data = group.all();
            
            // it contains an array from 1 to 20, create key missing elements and set value to 0
            display_obj.epsilon_beans.forEach(function(d) {
                exists = false;
                $.each(data, function(index, value) {
                    if (d == value.key) {
                        exists = true;
                        return false;
                    }
                });
                if (exists == false) {
                    data.push({key: parseInt(d), value: 0});
                }
            });

            // Sort by price high to low
            data.sort(_sort_by('key', false, parseInt));
            _VERBOSE ? console.log(data) : _VERBOSE;

            $.each(data, function(index, value) {

                tvalue = value.value;

                if (tvalue != 0)
                    value.value = tvalue / all.reduceCount().value();
                
            });


            _VERBOSE ? console.log(display_obj.epsRange) : _VERBOSE;
            _VERBOSE ? console.log(epsRange) : _VERBOSE;


            x.domain(data.map(function(d) {
                avg = parseFloat((display_obj.epsRange.invertExtent(d.key)[0] + display_obj.epsRange.invertExtent(d.key)[1]) / 2).toFixed(2);
                return avg;
            }));



            y.domain([0, group.top(1)[0].value]);


            div.each(function() {

                _VERBOSE ? console.log("div.each chart") : _VERBOSE;

                var div = d3.select(this),
                        g = div.select("g");

                // Create the skeletal chart.
                if (g.empty()) {

                    _VERBOSE ? console.log("g.empty") : _VERBOSE;


                    g = div.append("svg")
                            .attr("width", width + margin.left + margin.right)
                            .attr("height", height + margin.top + margin.bottom)
                            .append("g")
                            .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

                    // legend
                    g.append("text")
                            .attr("id", "title_barnet")
                            .attr("y", height + 40)
                            .attr("x", width / 2 + 60)
                            .attr("dy", ".71em")
                            .style("font-size", "12px")
                            .style("text-anchor", "end")
                            .text(_iTrans.prop('titulo_hist_eps'));




                    g.append("clipPath")
                            .attr("id", "clip-" + id)
                            .append("rect")
                            .attr("width", width)
                            .attr("height", height);

                    g.selectAll(".foreground.bar")
                            .attr("clip-path", "url(#clip-" + id + ")");

                    // **** Axis

                    g.append("g")
                            .attr("class", "axis")
                            .attr("transform", "translate(0," + height + ")")
                            .call(xAxis)
                            .selectAll("text")
                            .style("text-anchor", "end")
                            .style("font-size", "10px")
                            .attr("dx", "-.8em")
                            .attr("dy", "-.55em")
                            .attr("transform", function(d) {
                                return "rotate(-90)"
                            });


                    g.append("g")
                            .attr("class", "axis")
                            .attr("transform", "translate(" + margin.left + ",0)")
                            .call(yAxis)
                            .append("text")
                            .attr("id", 'yaxis_net')
                            .attr("transform", "rotate(-90)")
                            .attr("y", 6)
                            .attr("dy", ".71em")
                            .style("text-anchor", "end")
                            .text(_iTrans.prop('lb_frecuencia'));

                    // ** Adding bars

                    g.selectAll(".bar")
                            .data(data)
                            .enter().append("rect")
                            .attr("class", "bar")
                            .attr("x", function(d) {
                                // _VERBOSE ? console.log(d) : _VERBOSE;
                                return x(parseFloat((display_obj.epsRange.invertExtent(d.key)[0] + display_obj.epsRange.invertExtent(d.key)[1]) / 2).toFixed(2));
                            })
                            .attr("width", x.rangeBand())
                            .attr("y", function(d) {
                                return y(parseFloat(d.value));
                            })
                            .attr("height", function(d) {
                                return height - y(parseFloat(d.value));
                            })
                            .attr("fill", function(d) {

                                left = display_obj.epsRange.invertExtent(d.key)[0]
                                right = display_obj.epsRange.invertExtent(d.key)[1]
                                if (left <= 0 && right > 0)
                                    min_val = 0;
                                else {
                                    min_val = Math.min(Math.abs(left), Math.abs(right));
                                }
                                
                                if (display_obj.ep_th < min_val) {
                                    return d3.rgb(102, 184, 243);
                                    // return "steelblue";
                                }
                                else {
                                    return d3.rgb(213, 215, 223);
                                }
                            });

                    // **** Initialize the brush component with pretty resize handles.
                    var gBrush = g.append("g")
                            .attr("class", "brush")
                            .call(brush);

                    gBrush.selectAll("rect")
                            .attr("height", height);

                    gBrush.selectAll(".resize")
                            .append("path")
                            .attr("d", resizePath);


                }

            });



            function resizePath(d) {

                _VERBOSE ? console.log("resizePath") : _VERBOSE;
                // configure the values of the extent component, clips and shadow selection

                var e = +(d == "e"),
                        x = e ? 1 : -1,
                        y = height / 3;

                return "M" + (.5 * x) + "," + y
                        + "A6,6 0 0 " + e + " " + (6.5 * x) + "," + (y + 6)
                        + "V" + (2 * y - 6)
                        + "A6,6 0 0 " + e + " " + (.5 * x) + "," + (2 * y)
                        + "Z"
                        + "M" + (2.5 * x) + "," + (y + 8)
                        + "V" + (2 * y - 8)
                        + "M" + (4.5 * x) + "," + (y + 8)
                        + "V" + (2 * y - 8);
            }


        } // function chart(div) closed


        brush.on("brush.chart", function(e) {

            _VERBOSE ? console.log("brush.chart") : _VERBOSE;

            var y = d3.scale.linear()
                    .domain([margin.left, width - margin.left])
                    .range([0, display_obj.NUM_BEANS]);


            b = brush.extent();
            // _VERBOSE ? console.log(b) : _VERBOSE;

            // d3.round(y(b[1], 0) for rounded values
            var localBrushStart = (brush.empty()) ? brushStart : y(b[0]),
                    localBrushEnd = (brush.empty()) ? brushEnd : y(b[1]);

            // Snap to rect edge
            d3.select("g.brush").call((brush.empty()) ? brush.clear() : brush.extent([y.invert(localBrushStart), y.invert(localBrushEnd)]));


            // Fade all years in the histogram not within the brush
            d3.selectAll("rect.bar").style("opacity", function(d, i) {
                // _VERBOSE ? console.log(d.key) : _VERBOSE;

                if (d.key < localBrushStart || d.key >= localBrushEnd || brush.empty()) {
                    return "0.4";
                }
                else {
                    return "1";
                }
            });

            // dimension.filterRange([localBrushStart,localBrushEnd]);

        });


        brush.on("brushend.chart", function() {

            _VERBOSE ? console.log("brushend.chart") : _VERBOSE;

            var y = d3.scale.linear()
                    .domain([margin.left, width - margin.left])
                    .range([0, display_obj.NUM_BEANS]);


            b = brush.extent();
            
            var localBrushStart = (brush.empty()) ? brushStart : y(b[0]),
                    localBrushEnd = (brush.empty()) ? brushEnd : y(b[1]);

            // Snap to rect edge
            d3.select("g.brush").call((brush.empty()) ? brush.clear() : brush.extent([y.invert(localBrushStart), y.invert(localBrushEnd)]));


            if (brush.empty()) {

                dim_eps_freq.filterAll();

                d3.selectAll("rect.bar").style("opacity", function(d, i) {
                    return "1";
                });

            }
            else {

                if (d3.round(localBrushStart, 0) == 0)
                    left_extent = 1
                else
                    left_extent = d3.round(localBrushStart, 0)
                if (d3.round(localBrushEnd, 0) == 21)
                    rigth_extent = 20
                else
                    rigth_extent = d3.round(localBrushEnd, 0)


                _VERBOSE ? console.log(display_obj.epsRange.invertExtent(left_extent)) : _VERBOSE;
                _VERBOSE ? console.log(display_obj.epsRange.invertExtent(rigth_extent)) : _VERBOSE;

                display_obj.dim_eps_freq.filterFunction(function(d) {
                    // _VERBOSE ? console.log(d) : _VERBOSE;

                    if (d > display_obj.epsRange.invertExtent(left_extent)[0] && d < display_obj.epsRange.invertExtent(rigth_extent)[1] + 0.1)
                        return true;

                });

                // Fade all years in the histogram not within the brush
                d3.selectAll("rect.bar").style("opacity", function(d, i) {
                    if (d.key < localBrushStart || d.key > localBrushEnd) {
                        return "0.4";
                    }
                    else {
                        return "1";
                    }
                });

            }

            // d3.event.sourceEvent.stopPropagation();

        });


        chart.margin = function(_) {

            _VERBOSE ? console.log("chart.margin") : _VERBOSE;

            if (!arguments.length)
                return margin;
            margin = _;
            return chart;
        };

        chart.x = function(_) {

            _VERBOSE ? console.log("chart.x") : _VERBOSE;
           
            if (!arguments.length)
                return x;

            x = _;

            xAxis.scale(x);
            brush.x(x);
            return chart;
        };

        chart.y = function(_) {

            _VERBOSE ? console.log("chart.y") : _VERBOSE;

            if (!arguments.length)
                return y;
            y = _;
            return chart;
        };

        chart.dimension = function(_) {

            _VERBOSE ? console.log("chart.dimension") : _VERBOSE;
            // _VERBOSE ? console.log(_) : _VERBOSE;

            if (!arguments.length)
                return dimension;
            dimension = _;
            return chart;
        };

        chart.filter = function(_) {

            _VERBOSE ? console.log("chart.filter") : _VERBOSE;

            if (_) {
                brush.extent(_);
                dimension.filterRange(_);
            } else {
                brush.clear();
                dimension.filterAll();
            }
            brushDirty = true;
            return chart;
        };

        chart.group = function(_) {

            _VERBOSE ? console.log("chart.group") : _VERBOSE;
            // _VERBOSE ? console.log(_) : _VERBOSE;

            if (!arguments.length)
                return group;
            group = _;
            return chart;
        };

        chart.round = function(_) {

            _VERBOSE ? console.log("chart.round") : _VERBOSE;

            if (!arguments.length)
                return round;
            round = _;
            return chart;
        };

        return d3.rebind(chart, brush, "on");
    }



    /**
     * Realiza el ordenamiento de un objeto tipo Json por un atributo específico.
     *
     * @function _sort_by
     * @private
     * @memberof! histogram_module
     * 
     * @param {String} field - Nombre del parámetro para hacer el ordenamiento
     * @param {boolean} reverse - Bandera para hacer el ordenameinto descendiente o ascendente
     * @param {function} primer - Función para realizar el ordenamiento
     */
    function _sort_by(field, reverse, primer) {

        var key = primer ?
                function(x) {
                    return primer(x[field])
                } :
                function(x) {
                    return x[field]
                };

        reverse = !reverse ? 1 : -1;

        return function(a, b) {
            return a = key(a), b = key(b), reverse * ((a > b) - (b > a));
        }
    }


    /**
     * Función que inicializa las variables necesarias para la creación de histogramas.
     *
     * @function startHistogramModule
     * @public
     * @memberof! histogram_module
     * 
     */
    function startHistogramModule() {
        _VERBOSE ? console.log("startHistogramModule") : _VERBOSE;
        _initilizeHistogram();
    }

    // Añadir los miembros públicos
    return{
        startHistogramModule: startHistogramModule,
        createMultipleBarChart: createMultipleBarChart,
        setTableModule: setTableModule,
        setLanguageModule: setLanguageModule,
        // updateLabels: updateLabels,
        createBarChart: createBarChart,
        createBarChartNet: createBarChartNet,
        createBarChartFecha: createBarChartFecha
    }


});