Reputation: 323
for those working with D3... I have a multi-line chart and managed to fill the area between two series. The second part I've been trying to figure out is if it's possible to pass conditional statements so that the filled area changes color when the data point value of one set is greater than that of the other.
var w = 200,
h = 200,
p = 50;
var ddd = [[1,.22,.12],[2,.45,.09],[3,.54,.14],[4,.32,.18],[5,.89,.19],[6,.76,.22],[7,.72,.29],[8,.68,.34],[9,.73,.22],[10,.88,.37],[11,.92,.44],[12,.95,.89],[13,.97,.97],[14,.99,1.21],[15,1.11,1.99],[16,1.01,2.38],[17,1.13,2.11],[18,1.18,2.11],[19,1.22,2.18], [20,1.29,2.03]],
xmin = d3.min(ddd,function(d){return d[0];}),
xmax = d3.max(ddd,function(d){return d[0];}),
ymin = 0,
ymax = 2.25;
var xscale = d3.scale.linear()
.domain([xmin,xmax])
.range([p,w-p]);
var yscale = d3.scale.linear()
.domain([ymin,ymax])
.range([h-p,p]);
var xaxis = d3.svg.axis()
.scale(xscale)
.orient("bottom");
var yaxis = d3.svg.axis()
.scale(yscale)
.orient("left");
var central = d3.select("div#z")
.append("svg")
.attr("width",w)
.attr("height",h);
var line1 = d3.svg.line()
.x(function(d) {return xscale(d[0])})
.y(function(d) {return yscale(d[1])});
var line2 = d3.svg.line()
.x(function(d) {return xscale(d[0])})
.y(function(d) {return yscale(d[2])});
var x = d3.scale.linear().range([p,w-p]),
y = d3.scale.linear().range([h-p,p]);
var area = d3.svg.area()
.x(function(d){return xscale(d[0])})
//.y0(h-p)
.y0(function(d){return yscale(d[2])})
.y1(function(d){return yscale(d[1])});
central.append("g")
.attr("class","axis")
.attr("transform","translate(0," + (h-p) + ")")
.call(xaxis);
central.append("g")
.attr("class","axis")
.attr("transform","translate(" + (p) + ",0)")
.call(yaxis);
central.append("svg:path")
.attr("d",line1(ddd))
.attr("stroke","red")
.attr("stroke-width",2)
.attr('fill','none');
central.append("svg:path")
.attr("d",line2(ddd))
.attr("stroke","blue")
.attr("stroke-width",2)
.attr("fill","none");
central.append("path")
.datum(ddd)
.attr("class", "area")
//function(d){if (d[1] > d[2]) {return area} else {return area51};})
.attr("d",area);
.axis path,
.axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 9px;
}
.area {
fill: lightsteelblue;
stroke-width: 0;
}
.area51 {
fill: lightsteelblue;
stroke-width: 0;
}
.area:hover {
fill: grey;
stroke-width: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="z"></div>
So based on the chart, how can I get the right side of the chart filled in with a different color?
Upvotes: 2
Views: 932
Reputation: 32327
I am using library....an awesome library to detect path intersection (courtesy: @thelonious)
//the red line path
var rl = central.append("svg:path")
.attr("d",line1(ddd))
.attr("id", "linered")
.attr("stroke","red")
.attr("stroke-width",2)
.attr('fill','none');
//the blue line path
var bl = central.append("svg:path")
.attr("d",line2(ddd))
.attr("id", "lineblue")
.attr("stroke","blue")
.attr("stroke-width",2)
.attr("fill","none");
To get point of intersection do
var shape1 = new Path(rl.node());
var shape2 = new Path(bl.node());
var overlays = Intersection.intersectShapes(shape1, shape2);
Make left area and right area chart separately as below and add different classes
if (overlays.points.length > 0){
var d = overlays.points[0]
var midx = xscale.invert(d.x);
var midy = yscale.invert(d.y);
//assuming that the curve will have single y values for an x
var left = ddd.filter(function(k){ return (k[0] <= midx);});
var right = ddd.filter(function(k){ return (k[0] > midx);});
//inserting the new middle point
right.unshift([midx,midy,midy]);
//left area
central.append("path")
.datum(left)
.attr("class", "area")
.attr("d",area);
//right area
central.append("path")
.datum(right)
.attr("class", "area51")
.attr("d",area);
}
Working code here
Hope this helps!
Upvotes: 1