I'm stuck at trying to bind two-dimensional data in d3. I want to display a matrix of green squares. I'm building a matrix like this:
var size = 10;
dataset = [];
for(var y = 0; y<size; y++){
var tempData = [size];
for(var x = 0; x<size; x++){
tempData[x] = 5;
};
dataset.push(tempData);
};
I'm not sure how to bind the data correctly. I sort of understand Mike Bostock's tutorial on nested selections, but he's binding a matrix of fixed size to already existing elements. How would I use enter() to create new rectangles? This is how I tried to apply the tutorial's advice to first bind the outer, then the inner arrays.. not surprised that it doesn't work but I also don't know where to go from here..
svg.selectAll("rect")
.data(dataset)
.selectAll("rect")
.data(function (d,i) {return d;})
.enter()
.append("rect")
.attr("x", function(d,i){
return i*20})
.attr("y", function(d,i){
return i*20;})
.attr("height", 15)
.attr("width", 15)
.attr("fill", "green");
There are two problems. First, you have the second .selectAll() immediately after the first .data(), which means that you'll be operating on the update selection. This is empty as there are no elements in the DOM to start with. You need to operate on the enter selection instead (and it's good practice to use g elements here for the first level):
svg.selectAll("rect")
.data(dataset)
.enter()
.append("g")
.selectAll("rect")
.data(function (d,i) {return d;})
Second, you're putting the rectangles along the diagonal (same x and y coordinates), so even though the correct number of rect elements is there, you don't see all of them because they overlap. To fix, you need to take the index in the parent group into account for one of the coordinates (using the secret third argument):
.append("rect")
.attr("x", function(d,i){
return i*20;
})
.attr("y", function(d, i, j){
return j*20;
})
Complete demo here.
I am basically trying to clip a chart containing "path" elements representing data points.
It seems like, path elements are not initially "transformed" and remain at (0,0) and are thus clipped by rectangle surrounding entire chart.
Basically, I have replaced "circle" elements as drawn in D3 Brush Example with "path" elements of symbol type "circle".
.append("circle")
.attr("class", "point")
.attr("clip-path", "url(#clip)")
.attr("r", function(d){return Math.floor(Math.random() * (20 - 5 + 1) + 5);})
.attr("cx", function(d) { return x(d.index); })
.attr("cy", function(d) { return y(d.value); })
replaced with
.append("path")
.attr("class", "point")
.attr("clip-path","url(#clip)")
.attr("d", d3.svg.symbol().type("circle"))
Here is jsfiddle. As you can see, only lower right quarter of the circle is visible and rest have been clipped out. What is going on? What is the solution?
Edit:
Also, the actual "clipping" is not working for those circles(paths). Why is that? Why does it work for "circle" but not for "path"?
It would be nice to learn D3. After reading many examples, I think I understand it. My first project is to make a color wheel, without transitions for simplicity. But it appears even that is not simple enough for my first project! For project zero, I am trying to get something to show on the screen. Hopefully something I wrote (and dear read has fixed), and not an example.
What did I do wrong? http://jsfiddle.net/aGdMX/1/
var arc = d3.svg.arc()
.innerRadius(40)
.outerRadius(100)
.startAngle(0)
.endAngle(1)
;
var chart = d3.select("body").append("svg:svg")
.attr("class", "chart")
.attr("width", 420)
.attr("height", 420).append("svg:g")
.attr("transform", "translate(200,200)")
;
chart.selectAll("path")
.data(data)
.enter().append("svg:path")
.attr("fill", function(d, i){
return d3.rgb("black");
})
.attr("d", arc)
;
Thank you
Your example here doesn't have any data defined. If you just want to draw the svg statically, skip the selectAll() and data() bindings:
chart
.append("svg:path")
.attr("fill", function(d, i){
return d3.rgb("black");
})
.attr("d", arc)
;
Or define some data and use that to drive the drawing:
http://jsfiddle.net/findango/aGdMX/2/
(plus .attr("fill"... should be .style("fill"...)
I've created a little test line chart using D3, but since I am quite new to the library I am not sure what the best way would be to add multiple lines to a chart, at the moment I only have one line displayed in this fiddle.
I would like to display 2 lines on the chart, but I am unsure of how to achieve that without copy pasting code, which I am sure would be very inefficient as I would like to update/animate the graph at regular intervals based on user selection.
Instead of this,
var data = [12345,22345,32345,42345,52345,62345,72345,82345,92345,102345,112345,122345,132345,142345];
I would like to display something like this,
var data = [
[12345,42345,3234,22345,72345,62345,32345,92345,52345,22345], // line one
[1234,4234,3234,2234,7234,6234,3234,9234,5234,2234] // line two
];
Would this be a possibility? If so, what would be the best way to approach this, so that I can easily update/animate the graph when needed?
Note: I am merely trying to learn and to familiarize myself with D3 best practices and the library as a whole. Thanks.
This is possible and reasonable.
There is a tutorial that approaches this at the
D3 Nested Selection Tutorial
which describes nesting of data.
Below is code that I hacked from your fiddle to demonstrate this.
var data = [
[12345,42345,3234,22345,72345,62345,32345,92345,52345,22345],
[1234,4234,3234,2234,7234,6234,3234,9234,5234,2234]
];
var width = 625,
height = 350;
var x = d3.scale.linear()
.domain([0,data[0].length]) // Hack. Computing x-domain from 1st array
.range([0, width]);
var y = d3.scale.linear()
.domain([0,d3.max(data[0])]) // Hack. Computing y-domain from 1st array
.range([height, 0]);
var line = d3.svg.line()
.x(function(d,i) { return x(i); })
.y(function(d) { return y(d); });
var area = d3.svg.area()
.x(line.x())
.y1(line.y())
.y0(y(0));
var svg = d3.select("body").append("svg")
//.datum(data)
.attr("width", width)
.attr("height", height)
//.append("g");
var lines = svg.selectAll( "g" )
.data( data ); // The data here is two arrays
// for each array, create a 'g' line container
var aLineContainer = lines
.enter().append("g");
/*svg.append("path")
.attr("class", "area")
.attr("d", area);*/
aLineContainer.append("path")
.attr("class", "area")
.attr("d", area);
/*svg.append("path")
.attr("class", "line")
.attr("d", line);*/
aLineContainer.append("path")
.attr("class", "line")
.attr("d", line);
/*svg.selectAll(".dot")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("cx", line.x())
.attr("cy", line.y())
.attr("r", 3.5);*/
// Access the nested data, which are values within the array here
aLineContainer.selectAll(".dot")
.data( function( d, i ) { return d; } ) // This is the nested data call
.enter()
.append("circle")
.attr("class", "dot")
.attr("cx", line.x())
.attr("cy", line.y())
.attr("r", 3.5);
One deficiency is that I computed the domain for the x and y axes off the first array, which is a hack, but not pertinent to your question exactly.
A set of rectangles is drawn initially with the following enter/append/exit/remove sequence, no problem. When I pass different data (meant to replace the existing data entirely) the new rectangles are drawn on top of the existing rectangles.
I am selecting "lgnds" instead of rect, because I have drawn other rectangles that I don't wish to disturb.
var svg = d3.select("#graph").append("svg")
elements = svg
.selectAll("lgnds")
.data(data, function(d){return d;});
elements
.enter()
.append("rect")
.attr("width", 15)
.attr("height", rectHeight)
.attr("x", 5)
.attr("y", function (d,i){return ((i*rectHeight)+(gap*(i+1)));})
.style("fill", function(d){ return d.color;});
elements
.exit()
.remove();