I'm trying to make a timeline where the:
circle radius = visit duration
x-position = time of visit
.attr('cx', function(d,i) { /* insert code here */ } )
basically what I want is tell d3 to align the circle to "startDate", however there's a problem with this as circles with bigger radii will shift towards the left, making it look inaccurate since it's aligned to the center.
what are possible solutions for this?
here's the code: http://jsfiddle.net/jg4v1ymx/2/
edit - instead of the radius: if data is bigger, tell d3 to increase circle size by diameter? the problem is I don't want big radii circles overlap with circles in nearby dates
You just need to pass the x scale the date, as in:
.attr('cx', function(d,i) {
return x(d.date);
} )
You also need to make sure that you have set the domain before you do this, so move the x.domain(/*stuff*/) above the chart1 line.
You can see a working fiddle
Related
I am trying to draw a vertical line marker in my graph in D3. It is modeled off of this example: https://bl.ocks.org/mbostock/34f08d5e11952a80609169b7917d4172
My issue is that after I draw my line, it doesn't move as I zoom/scroll the graph. An example is shown below:
Currently, I have it calculated as a d3.area().
this.pastDateArea = d3.area()
.x(function(d) { return this.x(this.props.pastDate.toDate()) }.bind(this))
.y0(0)
.y1(function(d) { return this.height }.bind(this))
It is appended as
var pastDateData = [{x:this.props.pastDate.toDate(), y:150}]
this.focus.append("path")
.datum(pastDateData)
.attr("class", "area")
.attr("d", this.pastDateArea)
and zoomed/brushed using
//zoom
var t = d3.event.transform;
this.x.domain(t.rescaleX(this.x2).domain());
//brush
this.svg.select(".zoom").call(this.zoom.transform, d3.zoomIdentity
.scale(this.width / (s[1] - s[0]))
.translate(-s[0], 0));
I know there are similar questions to this one (namely, Draw a vertical line representing the current date in d3 gantt chart) but none of them include the zooming/panning features I have in my graph.
Please let me know if you need more information and thanks!
The issue is that you are not updating the vertical bar with each zoom event. Using the code of the example you show, several things are done when the chart is zoomed, including as you note:
x.domain(t.rescaleX(x2).domain()); // update x scale
focus.select(".area").attr("d", area); // redraw chart area
While you do give the new area the class of area, d3.select will only pick the first matching element. So, on zoom, only one .area element is updated (the first encountered, generally the first appended). But, replacing this with d3.selectAll(".area") will not generate the intended results as the area function referenced (.attr("d",area) ) is only used for the first area (that of the graph, not of the vertical bar).
A solution is to select each area (the chart and the bar) independently and update the area with their respective area generators. To do so, append the vertical bar with a unique class name, or an id and use that to select it later. Then when updating the graph on zoom or brush you can use:
x.domain(s.map(x2.invert, x2)); // update x scale
focus.select(".area").attr("d", area); // redraw chart area
focus.select(".bar").attr("d", pastDateArea);// redraw vertical bar
Remember that this needs to be done for both zoom and brush. Also, in the given example, a clip path is assigned in the css for .area, so you need to keep that in mind as well.
Here's a modified example.
I'm new to D3 and I hope you can help out. I'm working on a sunburst chart divided into inner and outer layers, where inner layer represents a group and outer layers represent subgroups. Here's a working example for reference: jsfiddle.net/9gpL308y/1/ (and here's the original fiddle I used as a starting point: jsfiddle.net/j9WnB/64/)
Currently, each inner layer adds up to a number based on its categories' values in the outer layer and categories behave the same with their subcategories. What I need is for each layer to display as percentage (out of 100 that all groups on the same layer should add up to) and scale to appropriate size. Take this image for example:
image of the wanted chart
Working example would be extremely helpful. I found some topics on this problem but I couldn't get it to work with provided advice. To be honest I still don't understand fully what's going on in here (I was never good at geometry).
tl;dr: How to make sunburst chart from the fiddle above display data as percentage and have arcs scale appropriately?
Thanks.
There is a little to do with geometry in this case (except positioning your label, but this is what arc.centroid provides). To get your percentages you just need to divide an extent of the child node by the extent of its parent.
var center = arc.centroid(d);
g.append("text")
.attr("text-anchor", "middle")
.attr("transform", "translate(" + center + ")")
.text(function(_) {
if (d.parent == null)
{
return;
}
var percentage = 100 * d.dx / d.parent.dx; // that's it!
return d3.format(",.2f")(percentage) + "%";
})
Working example here: http://jsfiddle.net/9gpL308y/2/
For educational purposes you can dump all the nodes of the partition layout and then find how do they match visual picture. You can also read about those parent, dx, etc. at the documentation on partition layout: https://github.com/d3/d3-3.x-api-reference/blob/master/Partition-Layout.md
I created a scatter plot using D3.js that updates every 20 seconds. It also pans and zooms. The problem is the data lags behind the axis during the pan. I've looked for examples of a similar implementation but all I can find are ones that either do zoom/pan or do intervals, not both. I can't find the source of the problem. A simplified demo of my code can be found here: http://jsbin.com/yurik/1/edit. Any help is appreciated.
The synchronization issue comes from the fact that you are using a transition to move the circles and not using a transition to update the x-axis. Here's the relevant snippet from the draw function:
circles.transition()
.attr("cx", function(d) { return x(dateFn(d)) })
.attr("cy", function(d) { return yValueFn(d) });
svg.selectAll("g.x.axis").call(xAxis);
Because D3 has a default transition duration of 250 milliseconds, the circles are lagging behind the axis, which is updated instantly. You can synchronize the two by reducing the transition duration to 0 like this:
circles.transition().duration(0)
That should make the x-axis and circles move synchronously.
http://jsfiddle.net/HF57g/ is an example of a single event(blue rect) placed on the timeline. If i zoom into the timeline, rect's placement on the x axis changes in harmony with the ticks on axis. but at the same time, rect is scaled horizontally.
if i modify the following section of the code
svg.selectAll(".item").attr("transform", "translate(" + d3.event.translate[0]+", 0)scale(" + d3.event.scale+", 1)");
to
svg.selectAll(".item").attr("transform", "translate(" + d3.event.translate[0]+", 0)scale(1, 1)");
as in http://jsfiddle.net/HF57g/1/ to disable horizontal scaling, then the rect's position changes much more than the axis' during zoom in/out.
How can i zoom in/out time.scale without scaling other related shapes?
I ended up adding an update function to rearrange the rects' places depending on the new positions returned by scale(x).
function update_events(){
return svg.selectAll("rect.item")
.attr("x", function(d){return scale(d);})
}
Here's the final version: http://jsfiddle.net/HF57g/2/
I have a simple d3 bar chart and I'm trying to draw horizontal lines in the background.
The bars on the chart represent percentages, with a full-height bar being 100%. I want to show 4 bars, at 25%, 50%, 75% and 100% levels. But if I do this:
svg.selectAll('.rule')
.data(y.ticks(4))
.enter().append('line')
.attr('class', 'rule')
.attr('x1', 0)
.attr('x2', width)
.attr('y1', y)
.attr('y2', y);
then the y.ticks(4) shows 5 lines. If I change it to 3 I get 2 lines. If I change it to 5 then I still get 5 lines. Here's an example on JSFiddle.
Also, at the end of that script, I tried adding a y-axis, but it only displays horizontally - how do I get it to appear vertically? This seems to Just Work in all the examples I've seen.
The param of the ticks() method of d3.scale.linear() is just a hint. The method uses this as an approximation, but it will pick "pretty" values. (see here: https://github.com/mbostock/d3/wiki/Quantitative-Scales#wiki-linear_ticks)
If you want to set the values explicitly, you need to instantiate a d3.svg.axis() and use axis.tickValues() (like you did further down).
Regarding your 2nd question. The axis has a default orientation (which you're seeing), for using as a horizontal, X axis. You need to call .orient("left") or .orient("right") to get a vertical axis. Note, if you use "left" orientation, you won't actually see an axis, bc its numbers will run off the left edge of the SVG and get cropped. So it's a good idea in general to add some padding to the SVG and shift all your graphics within it. If you use "right" orientation you'll see the numbers, but typically in this case you'd translate the axis to the right side of the graph.