Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.1k views
in Technique[技术] by (71.8m points)

graph - JavaScript charting library to handle shading area between two lines

I'm looking for a JavaScript charting library that supports shading the area between two lines. ChartDirector handles this quite nicely (see: http://www.advsofteng.com/gallery_line2.html - Inter-line Coloring), but I require a more interactive charting library.

I've looked into various JavaScript libraries. Flot and Highcharts come close, but still have their limitations:

  • Flot supports shading between two lines using the fillBetween plugin, but it does not support shading with multiple colors depending on which line is on top.
  • One can achieve shading between two lines with Highcharts using stacked area charts, but it does not handle the case where the two lines intersect.

Any suggestions?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I ended up using Highcharts. I took this example ElementStacks and modified it to handle the intersections. See 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();
});
});

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...