JavaScript图表库用于处理两行之间的着色区域

时间:2012-04-02 19:54:15

标签: javascript graph charts highcharts flot

我正在寻找一个JavaScript图表库,它支持对两行之间的区域进行着色。 ChartDirector处理得非常好(参见:http://www.advsofteng.com/gallery_line2.html - 行间着色),但我需要一个更具交互性的图表库。

我研究了各种JavaScript库。 Flot和Highcharts接近,但仍有其局限性:

  • Flot支持使用fillBetween插件在两行之间进行着色,但它不支持使用多种颜色进行着色,具体取决于哪一行位于顶部。
  • 可以使用堆叠区域图表使用Highcharts在两条线之间实现阴影,但它不能处理两条线相交的情况。

有什么建议吗?

1 个答案:

答案 0 :(得分:1)

我最终使用了Highcharts。我采用了这个例子ElementStacks并对其进行了修改以处理交叉点。请参阅Negative Area

$(function() {
var Intersection = function (d1, d2) {
  var self = this;

  this.init = function () {
    this.d1 = this.sortLine(d1);
    this.d2 = this.sortLine(d2);

    if (this.d1.length != this.d2.length) {
      throw 'd1 and d2 expected to be same size';
    }

    this.dps = _.zip(d1, d2);

    hasUnmatchedIndex = _.any(this.dps, function(dp_pair) {
      return dp_pair[0][0] != dp_pair[1][0];
    });

    if (hasUnmatchedIndex)
      throw 'd1 and d2 do not have same indices';
  };

  this.sortLine = function(line) {
    return _.sortBy(line, function(dp) { return dp[0]; });
  };

  this.transitions = function() {
    return _.map(this.dps, function(dp_pair) {
      a = dp_pair[0];
      b = dp_pair[1];
      result = null;
      if (a[1] < b[1])
        result = -1;
      else if (a[1] > b[1])
        result = 1;
      else
        result = 0;

      return [a[0], result];
    });
  };

  this.dropTransitions = function() {
    prev = null;
    drops = [];

    _.each(this.transitions(), function(curr) {
      if (prev && prev[1] != curr[1] && prev[1] != 0 && curr[1] != 0)
        drops.push([prev, curr])
      prev = curr;
    });

    return drops;
  };

  this.data = function() {
    //self = this;
    _d1 = this.sortLine(this.d1.concat(this.intersections()));
    _d2 = this.sortLine(this.d2.concat(this.intersections()));
    d1_g = [];
    d2_g = [];
    d_min = [];
    dps = _.zip(_d1, _d2);
    _.each(dps, function(dp_pair,i) {
      index = dp_pair[0][0];
      dpv1 = dp_pair[0][1];
      dpv2 = dp_pair[1][1];

      if (dpv1 == null || dpv2 == null) {
        d1_g.push([index, null]);
        d2_g.push([index, null]);
      } else {
        diff = Math.abs(dpv1 - dpv2);
        if (dpv1 > dpv2) {
          d1_g.push([index, diff]);
          d2_g.push([index, 0]);
        } else if (dpv2 > dpv1) {
          d1_g.push([index, 0]);
          d2_g.push([index, diff]);
        } else {
          d1_g.push([index, diff]);
          d2_g.push([index, diff]);
        }
      }
      d_min.push([index, Math.min(dpv1, dpv2)]);
    });

    return [d1_g, d2_g, d_min];
  };

  this.intersections = function() {
    //self = this;

    return _.map(this.dropTransitions(), function(dt) {
      line1 = _.filter(self.d1, function(dp) {
        return dp[0] == dt[0][0] || dp[0] == dt[1][0];
      });

      line2 = _.filter(self.d2, function(dp) {
        return dp[0] == dt[0][0] || dp[0] == dt[1][0];
      });

      return self.findIntersection(line1, line2);
    });
  };

  this.findIntersection = function(line1, line2) {
    eq1 = this.lineEquation(line1);
    eq2 = this.lineEquation(line2);

    m1 = eq1.m;
    b1 = eq1.b;

    m2 = eq2.m;
    b2 = eq2.b;

    x = (b2 - b1) / (m1 - m2)
    y = (m1 * x) + b1
    return [x,y];
  };

  this.lineEquation = function(line) {
    p1 = _.map(line[0], function(n) { return parseFloat(n); });
    p2 = _.map(line[1], function(n) { return parseFloat(n); });

    x1 = p1[0];
    y1 = p1[1];

    x2 = p2[0];
    y2 = p2[1];

    m = (y1 - y2) / (x1 - x2);
    b = y1 - (m*x1);
    eq = {'m': m, 'b': b};
    return eq;
  };

  this.print = function (obj) { alert(JSON.stringify(obj)); };
  this.init(d1, d2);
};

var d1r = [10, 8, 7, 6, 5,  4, 3, 5,  3, 9, 10, 11,  2];
var d2r = [ 5, 6, 7, 8, 9, 10, 9, 8, 12, 3,  2,  1, 20];

var d1 = _.map(d1r, function(e,i) { return [i, e-10]; });
var d2 = _.map(d2r, function(e,i) { return [i, e-10]; });

var t = new Intersection(d1, d2);
var data = t.data();
var values = _.map(data, function(dps) {
    return _.map(dps, function(dp) {
        return dp[1];
    });        
});
var minValue = _.min(_.flatten(values));
// Need to find threshold to handle negative stacking values
var threshold = minValue < 0 ? minValue : 0;


var dp1 = t.d1;

var dp2 = t.d2;

var dp1_g = data[0];

var dp2_g = data[1];

var dp_min = data[2];

var chart = new Highcharts.Chart({
    chart: {
        renderTo: 'container',
        type: 'area',
        animation: false
    },
    plotOptions: {
        area: {
            stacking: true,
            lineWidth: 0,
            shadow: false,
            marker: {
                enabled: false
            },
            enableMouseTracking: false,
            showInLegend: false        
        },
        line: {
            zIndex: 5
        },
        series: {
            threshold: threshold
        }
    },
    series: [{
        type: 'line',
        color: 'red',
        data: dp1

    },{
        type: 'line',
        color: 'black',
        data: dp2

    },{
        color: 'orange',
        data: dp1_g

    },{
        color: 'grey',
        data: dp2_g

    },{
        id: 'transparent',
        color: 'rgba(255,255,255,0.0)',
        data: dp_min

    }]
}, function(chart){
    chart.get('transparent').area.hide();
});
});