build area between 2 lines (or 2 set of points)

1,861 views
Skip to first unread message

kaiji2012

unread,
Mar 15, 2012, 10:36:32 AM3/15/12
to d3...@googlegroups.com
Hello !

I am trying to figure it out how to build an svg area between 2 sets of points...or 2 lines, to be more specific...
Apparently is easy to construct areas between one line and the axis, but I couldn't figure it out how to do it between
2 lines...

Thanks!

Mike Bostock

unread,
Mar 15, 2012, 10:58:48 AM3/15/12
to d3...@googlegroups.com
It sounds like you want a d3.svg.area:

https://github.com/mbostock/d3/wiki/SVG-Shapes#wiki-area

Each layer in the streamgraph is defined by two lines (the baseline,
y0, and the topline, y1):

http://mbostock.github.com/d3/ex/stream.html

Mike

kaiji2012

unread,
Mar 20, 2012, 6:06:26 AM3/20/12
to d3...@googlegroups.com
Hello all,

I've tried several area examples, and while I understood most of them,
I still don't understand how to draw an area between 2 lines that are given through their set of points.
It's quite clear how to draw areas when I define the lines through their equations, but not when I have all the points.

First of all area takes a single data parameter, whereas I would need 2 datasets, one for one line, and the second
for the other line....
Here's a modified example built by adapting http://www.janwillemtulp.com/2011/04/01/tutorial-line-chart-in-d3/

I pretty much want an area between the 2 lines defined by the 2 arrays: data and data2.
For a streamgraph, stacked bar, arcs or other graphs it's pretty clear to me how to use areas and I have used them, but for some
reason I don't seem to understand how to build an area between 2 lines defined through their points not through their equations...

Thanks!
 

Mike Bostock

unread,
Mar 20, 2012, 11:36:28 AM3/20/12
to d3...@googlegroups.com
> First of all area takes a single data parameter, whereas I would need 2
> datasets, one for one line, and the second for the other line....

There are a variety of ways you can accomplish this.

One method is to bind the path element (which you will be using to
render the area) to an array of indices. These represent your
x-values, which are shared by the two arrays for the topline and
baseline. An easy way to generate an array of indices is using
d3.range:

var indexes = d3.range(data0.length);

Next, you can use the index to lookup the corresponding value from
data0 and data1, your two data arrays that represent the baseline and
topline, respectively.

var area = d3.svg.area()
.x(x)
.y0(function(d) { return y(data0[d]); })
.y1(function(d) { return y(data1[d]); });

Note that `d` here will be an index (since we'll bind the path element
to the array `indexes`). I would typically define the corresponding
scales like so:

var margin = {top: 10, right: 10, bottom: 30, left: 10},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;

var x = d3.scale.linear()
.domain([0, data0.length - 1])
.range([0, width]);

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

Likewise, standard convention is to create the SVG element with an
inner G element that offsets the origin by the margins, as described
in the D3 workshop slides. Like this:

var svg = d3.select("body").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 + ")");

OK, lastly you can add your path element, and render the area:

svg.append("path")
.datum(indexes)
.attr("class", "area")
.attr("d", area);

Another way you could accomplish this would be to merge your two data
arrays into a data structure that represents the data for the area as
a single object, rather than multiple objects. This allows you to
avoid the lookup into global variables. What you want is a structure
like this, for each data point:

{x: …, y0: …, y1: …}

You can create this using array.map:

https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/map

Something like this:

var data = data0.map(function(d, i) { return {x: i, y0: d, y1: data1[i]}; });

Then you would change your area definition correspondingly:

var area = d3.svg.area()
.x(function(d) { return x(d.x); })
.y0(function(d) { return y(d.y0); })
.y1(function(d) { return y(d.y1); });

Mike

kaiji2012

unread,
Mar 20, 2012, 3:28:22 PM3/20/12
to d3...@googlegroups.com

Hi Mike,

Thanks, the second method is easier and works just fine.

I will play more with these kind of transformations.

Thanks!
Reply all
Reply to author
Forward
0 new messages