Vizzuality/gfw-climate

View on GitHub
app/assets/javascripts/widgets/indicators/bars/BarChart.js

Summary

Maintainability
C
1 day
Test Coverage
define(
  [
    'd3',
    'handlebars',
    'helpers/NumbersHelper',
    'text!widgets/templates/indicators/bars/barschart-tooltip.handlebars',
    'text!widgets/templates/indicators/bars/barschart-legend.handlebars'
  ],
  function(d3, Handlebars, NumbersHelper, tooltipTpl, legendTpl) {
    var tooltipTemplate = Handlebars.compile(tooltipTpl);
    var legendTemplate = Handlebars.compile(legendTpl);

    var barsChart = function(params) {
      var elem = params.elem;
      var elemAttr = elem.replace(/[#]|[.]/g, '');
      var $el = $(elem);
      var contentWidth = $el.width();
      var contentHeight = $el.height();
      var data = params.data;
      var hover = true;
      var loader = params.loader || null;
      var infoWindow = params.infoWindowText || '';
      var decimals = params.decimals || 0;
      var unit = params.unit || '';
      var unitZ = params.unitZ || '';
      var barWidth = params.barWidth || 10;
      var barSeparation = params.barSeparation || 10;
      var xIsDate = params.xIsDate || false;
      var hasLine = params.hasLine || false;
      var interpolate = params.interpolate || 'linear';
      var transition = 200;
      var margin = params.margin || {
        top: 0,
        right: 0,
        bottom: 65,
        left: 0,
        xaxis: 10,
        tooltip: 1.8
      };

      var width = contentWidth,
        height = contentHeight;

      var width = width - margin.left - margin.right,
        height = height - margin.top - margin.bottom;

      if (xIsDate) {
        data.forEach(function(d) {
          d.date = parseDate(d.date);
        });
      }

      if (hasLine) {
        var line = d3.svg
          .line()
          .x(function(d, i) {
            return (barWidth + barSeparation) * i;
          })
          .y(function(d) {
            return y(d.z);
          })
          .interpolate(interpolate);
      }

      var svgBars = d3
        .select(elem)
        .append('svg')
        .attr('class', '')
        .attr('width', width + margin.left + margin.right)
        .attr('height', height + margin.top + margin.bottom)
        .append('g');

      var x = d3.scale.ordinal().rangeBands([0, width], 0.2);

      var x2 = d3.scale.ordinal().rangeBands([0, width], 0.2);

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

      var yMin = 0;
      var yMax = d3.max(data, function(d) {
        return d.y;
      });

      var y = d3.scale
        .linear()
        .domain([yMin, yMax])
        .range([height, yMin])
        .nice();

      if (hasLine) {
        var zMin = d3.min(data, function(d) {
          return d.z;
        });
        var zMax = d3.max(data, function(d) {
          return d.z;
        });
        var z = d3.scale
          .linear()
          .domain([zMin, zMax])
          .range([height, 0])
          .nice();

        var y = d3.scale
          .linear()
          .domain([yMin, yMax])
          .range([height, 0])
          .nice();
      }

      var line = d3.svg
        .line()
        .x(function(d, i) {
          return x2(d.x) + i;
        })
        .y(function(d) {
          return z(d.z);
        })
        .interpolate(interpolate);

      svgBars
        .selectAll('.bar')
        .data(data)
        .enter()
        .append('rect')
        .attr('class', 'bar')
        .style('fill', function(d) {
          return d.color;
        })
        .attr('x', function(d) {
          return x(d.x);
        })
        .attr('width', x.rangeBand())
        .attr('y', function(d) {
          return y(Math.max(0, d.y));
        })
        .attr('height', function(d) {
          return yMin >= 0
            ? Math.abs(height - y(d.y))
            : Math.abs(y(d.y) - y(0));
        });

      if (hasLine) {
        svgBars
          .append('g')
          .attr('class', 'unit z')
          .attr('x', function(d) {
            return 0;
          })
          .attr('y', '-10')
          .attr('dy', '.35em')
          .attr('text-anchor', 'start')
          .text(function(d) {
            return unitZ;
          });

        svgBars
          .append('g')
          .attr('transform', function(d, i) {
            return 'translate(0, 0)';
          })
          .append('path')
          .datum(data)
          .attr('class', 'line')
          .attr('stroke', function(d) {
            return d.lineColor;
          })
          .attr('d', line);
      }

      // Legend
      var totalLine = _.reduce(
        data,
        function(memo, d) {
          return memo + d.z;
        },
        0
      );
      var totalBars = _.reduce(
        data,
        function(memo, d) {
          return memo + d.y;
        },
        0
      );
      d3.select(elem + ' .graph-legend').html(
        legendTemplate({
          lineTotalValue: NumbersHelper.addNumberDecimals(
            Math.round(d3.format('.1f')(totalLine))
          ),
          barsTotalValue: NumbersHelper.addNumberDecimals(
            Math.round(d3.format('.1f')(totalBars))
          )
        })
      );

      // Toolttio
      var tooltipEl = elem + '-tooltip';
      var tooltip = d3
        .select(elem)
        .insert('div', 'svg')
        .attr('id', elemAttr + '-tooltip')
        .attr('class', 'tooltip-graph');

      tooltip.append('span').attr('class', 'tooltip-year');

      d3.selectAll(elem + ' .bar').on('mousemove', function(d) {
        var element = d3.select(elem + ' svg');
        var cords = d3.mouse(element.node());

        d3
          .select(tooltipEl)
          .style('top', cords[1] + 'px')
          .style('left', cords[0] + 'px')
          .style('display', 'block');

        d3
          .select(tooltipEl)
          .select('.tooltip-year')
          .html(
            tooltipTemplate({
              lineValue: d3.format(',f')(d.z),
              barValue: d3.format(',f')(d.y),
              year: d.x
            })
          );
      });

      d3.selectAll(elem + ' .bar').on('mouseout', function() {
        d3.select(tooltipEl).style('display', 'none');
      });
    };

    return barsChart;
  }
);