I'm a D3.js newbie, so I beg your pardon if I ask something too basic.
I have a simple array as a dataset:
[0, 10, 15, 20, 24, 35, 58]
For every element of my array I want to create a circle and horizontally put those circles at the same distance (12 pixel) starting from x=10 (and y being constantly 50). How can I do?
Thanks in advance.
Basic things you need to know about d3.js is, it binds data to svg elements and creates visualization.
To get to the basics of d3. You can google around.
Here are some sites that I prefer for d3 learning.
d3 - Ofcourse the official website. With all the codes and examples.
Dashing D3 - Free for basic and you can also get a paid extension for further d3 tutorials.
d3noob - You get a free PDF with all the line by line briefing in the PDF. The one I prefer the most for the beginners.
Here's the answer for the question - fiddle.
It's very simple on creating circles in d3.
Here's the code I've used.
var svg = d3.select('.circle')
.append('svg')
.attr('height', 500)
.attr('width', 500)
First off I append the svg element to the .circle div.
svg.selectAll('circle')
.data(data)
.enter()
.append('circle')
.attr("cx", function(d, i) { return 60 * i })
.attr("cy", 60)
.attr("r", 20)
And then I add the circles depending on the number of data using the above block of code.
Note : .attr is that I'm adding attribute.
Here I've fixed the radius to 20.
But you can change the radius according to the data like this. function(d) { return d; }.
And ofcourse you can color the circles by using .style("fill", "#ddd") and other css stuffs using .style().
You can get more of this info from the websites I've provided.
Hope this helps.
Related
I'm trying to find a way to have an image inside a circle in D3 and a text next to it.
I'm using force directed graph if it makes any difference.
So far i can find only partial solutions (only image, only text, only circle), but non that combines them all.
Any idea?
Can this example help?
http://bl.ocks.org/mbostock/950642
The related code is here:
node.append("image")
.attr("xlink:href", "https://github.com/favicon.ico")
.attr("x", -8)
.attr("y", -8)
.attr("width", 16)
.attr("height", 16);
I am trying to adapt Mike Bostock's Focus+Context via Brushing chart at: bl.ocks.org/mbostock/1667367 to include a drag icon on both vertical lines of the brush rectangle. These should appear once a selection is made and act as a visual cue to shrink or expand the selected/brushed area. I see the placement of the images being dynamic i.e. moving fluidly with the brushed area as opposed to an update after the brushed area is reset. What seems most reasonable to me would be to add an svg image to the context rectangle like so:
//original code
context.append("g")
.attr("class", "x brush")
.call(brush)
.selectAll("rect")
.attr("y", -6)
.attr("height", height2 + 7)
//additional code
.append("svg:image")
.attr("xlink:href", "icon.png")
.attr("width", 10)
.attr("height", 10)
.style("opacity",1)
I've tried playing around with the x and y positioning of both images with no luck getting them to appear, but i conceptually see it working as
y axis: height of context chart divided by 2
x axis: each image respectively corresponding to the min and max x values of the brushed area
Any help would be appreciated.
Lars, thanks for the pointer which generally led me in the right direction although I ended up directly adapting from an example at https://engineering.emcien.com/2013/05/7-d3-advanced-brush-styling. That example is a bit more complex so i borrowed from a subset relevant to my purposes.
Steps i followed include
1.) Creating two images appended to the context g element and initializing their position somewhere that doesn't give the impression that the chart is brushed on loading {i put them halfway (vertically) and close together around the end of the context(horizontally)}.
var leftHandle = context.append("image")
.attr("xlink:href", "icon.gif")
.attr("width", 11)
.attr("height", 27)
.attr("x",x2(data[data.length-6].date))
.attr("y", (height2/2)-15);
var rightHandle = context.append("image")
.attr("xlink:href", "icon.gif")
.attr("width", 11)
.attr("height", 27)
.attr("x",x2(data[data.length-1].date))
.attr("y", (height2/2)-15);
2.) Within the brush function, i created a variable to hold brush.extent(), then tied the x attributes of the images to its min and max x values.
var ext = brush.extent();
leftHandle.attr("x",x2(ext[0]));
rightHandle.attr("x",x2(ext[1]));
One things i'm not completely satisfied with is that when i initially created the images after the brush rectangle, they sat on top of it, preventing me from being able to brush if i hovered over the images (which is the intuitive reaction desired). I ended up placing the images behind the rectangle which has a low opacity. Not the 100% accurate visual representation sought, but it works just fine.
I'm building my first line graph in d3:
http://jsfiddle.net/j94RZ/
I want to know how to utilize either the scale or axis allow me to draw a grid (of, presumably rectangles) where I can set a different background colour for each of the section of the grid...so I can alternate colours for each cell of the grid. I want the grid to be drawn and be constrained by the axes of my graph and then also adapt if the spacing of the axes ticks change (i.e. the axes changes like this: http://bl.ocks.org/mbostock/1667367). So if my graph has an x axis with 4 ticks and a y axis of 7 ticks then my graph will have a background grid that's 7 blocks high and 4 blocks wide.
I've been playing with the idea of using a range which starts at zero and ends at the full width of the graph but I don't know what value I can use for the step. Is there any way to sort of query the axis and return how many ticks there are?
var gridRange = d3.range(0, width, step?);
A better approach than your current solution would be to use scale.ticks() explicitly to get the tick values. The advantage of that is that it will still work if you change the number of ticks for some reason.
To get an alternating grid pattern instead of a single fill, you can use something like this code.
.attr("fill", function(d, i) {
return (i % 2) == 1 ? "green" : "blue";
})
Finally, to get the full grid pattern, you can either use an explicit loop as you've suggested, or nested selections. The idea here is to first pass in the y ticks, create a g element for each and then pass the x ticks to each one of these groups. In code, this looks something like this.
svg.selectAll("g.grid")
.data(y.ticks()).enter().append("g").attr("class", "grid")
.selectAll("rect")
.data(x.ticks()).enter().append("rect");
To set the position, you can access the indices within the top and bottom level data arrays like this.
.attr("x", function(d, i) {
return xScale(i);
})
.attr("y", function(d, i, j) {
return yScale(j);
})
To set the x position, you need the index of the inner array (passed to the set of g elements), which can be accessed through the second argument of your callback. For the outer array, simply add another argument (j here).
And that's really all there is to it. Complete jsfiddle here. To update this grid dynamically, you would simply pass in the new tick values (gotten from scale.ticks()), match with the existing data, and handle the enter/update/exit selections in the usual manner.
If you want to do without the auxiliary scales (i.e. without .rangeBand()), you can calculate the width/height of the rectangles by taking the extent of the range of a scale and dividing it by the number of ticks minus 1. Altogether, this makes the code a bit uglier (mostly because you need one fewer rectangle than ticks and therefore need to subtract/remove), but a bit more general. A jsfiddle that takes this approach is here.
So after a few helpful comments above I've got close to a solution. Using Ordinal rangebands get me close to where I want to go.
I've created the range bands by using the number of ticks on my axis as a basis for the range of the input domain:
var xScale = d3.scale.ordinal()
.domain(d3.range(10))
.rangeRoundBands([0, width],0);
var yScale = d3.scale.ordinal()
.domain(d3.range(4))
.rangeRoundBands([0, height],0);
I've then tried drawing the rectangles out like so:
svg.selectAll("rect")
.data(p)
.enter()
.append("rect")
.attr("x", function(d, i) {
return xScale(i);
})
.attr("y", function(d,i) {
0
})
.attr("width", xScale.rangeBand())
.attr("height", yScale.rangeBand())
.attr("fill", "green").
attr('stroke','red');
This gets me the desired effect but for only one row deep:
http://jsfiddle.net/Ny2FJ/2/
I want,somehow to draw the green blocks for the whole table (and also without having to hard code the amount of ticks in the ordinal scales domain). I tried to then apply the range bands to the y axis like so (knowing that this wouldn't really work though) http://jsfiddle.net/Ny2FJ/3/
svg.selectAll("rect")
.data(p)
.enter()
.append("rect")
.attr("x", function(d, i) {
return xScale(i);
})
.attr("y", function(d,i) {
return yScale(i);
})
.attr("width", xScale.rangeBand())
.attr("height", yScale.rangeBand())
.attr("fill", "green").
attr('stroke','red');
The only way I can think to do this is to introduce a for loop to run the block of code in this fiddle http://jsfiddle.net/Ny2FJ/2/ for each tick of the y axis.
I try to do my first project with d3.js - but I got stuck ...
What I want to do:
Depending on the data, rectangles should get a different color. But: There are 187 datapoints, therefore 187 rectangles. To be all shown, my svg needs to have a width of 9000px - a lot of horizontal scrolling ...
Is there a way to kind of get a forced "line break" of the rectangles?
Obviously, increasing height doesn't work.
something like this might work. I can't be sure without actually seeing your code
rectangles
.attr("x", function(d, i) {return (i % barsPerRow) * barWidth;})
.attr("y", function(d, i) {return Math.floor(i / barsPerRow) * maxBarHeight;})
;
Like #meetamit said, svg does not have an html style layout.
I am attempting to create a vertical timeline using d3.js that is linked to a map so that any item(s) contained in the brush will also be displayed in the map. Kind of like http://code.google.com/p/timemap/ but with d3 instead of SIMILE and a vertical timeline rather than horizontal.
I can successfully create an svg with vertical bars representing time ranges, legend, ticks, and a brush. The function handling brush events is getting called and I can obtain the extent which contains the y-axis start and stop of the brush. So far so good...
How does one obtain the datums covered by the brush? I could iterate over my initial data set looking for items within the extent range but that feels hacky. Is there a d3 specific way of getting the datums highlighted by a brush?
var data = [
{
start: 1375840800,
stop: 1375844400,
lat: 0.0,
lon: 0.0
}
];
var min = 1375833600; //Aug 7th 00:00:00
var max = 1375919999; //Aug 7th 23:59:59
var yScale = d3.time.scale.utc().domain([min, max]).range([0, height])
var brush = d3.svg.brush().y(yScale).on("brush", brushmove);
var timeline = d3.select("#myDivId").append("svg").attr("width", width).attr("height", height);
timeline.selectAll("rect")
.data(data)
.enter().append("rect")
.attr("x", function(datum, index) {return index * barSize})
.attr("y", function(datum, index) {return yScale(datum.start)})
.attr("height", function(datum, index) {return yScale(datum.end) - yScale(datum.start)})
.attr("width", function() {return barSize})
timeline.append("g")
.attr("class", "brush")
.call(brush)
.selectAll("rect")
.attr("width", width);
function brushmove() {
var extent = brush.extent();
//How do I get the datums contained inside the extent????
}
You'll need to do some kind of iteration to figure out what points live inside the brush extent. D3 doesn't automatically do this for you, probably because it can't know what shapes you're using to represent your data points. How detailed you get about what is considered "selected" and what isn't is quite application specific.
There are a few ways you can go about this:
As you suggest, you can iterate your data. The downside to this is that you would need to derive the shape information from the data again the same way you did when you created the <rect> elements.
Do a timeline.selectAll("rect") to grab all elements you potentially care about and use selection.filter to pare it down based on the x, y, height and width attributes.
If performance is a concern because you have an very large number of nodes, you can use the Quadtree helper to partition the surface and reduce the number of points that need to be looked at to find the selected ones.
Or try Crossfilter, there you pass the extent from the brush to a dimension filter and then you fetch filtered and sorted data by dimension.top(Infinity).
(A bit late answer, buy maybe useful for others, too.)