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.
Related
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'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
I have a map of the US, with cities plotted. I'm trying to use d3-tip to provide tooltips to my cities on hover. The problem I'm running into is that d3-tip isn't accounting for the projection, and I'm not sure how to apply it.
The projection I'm using:
var projection = d3.geo.mercator()
.scale(150)
.translate([width / 2, height / 1.5]);
I apply it to my circle elements by the following:
.attr("cx", function(d) {
return projection([d.Longitude, d.Latitude])[0];
})
.attr("cy", function(d) {
return projection([d.Longitude, d.Latitude])[1];
})
Problem is d3-tip doesn't seem to take x and y attributes, it grabs them from the node issuing the event and doesn't seem to account for it having the projection applied; which doesn't make sense to me.
I usually do tooltips with d3 by appending a div with absolute positioning and hiding it in the body.
Then on mousing over an element, I move the div to the location of the mouse pointer and unhide it. I then hide the div once the mouseout event occurs.
This allows me to control X and Y as well as the styling and text of the tooltip.
Here's a great example: https://gist.github.com/biovisualize/1016860
Svg line not displayed on google charts?
Refering this question i want to find out point at which svg line cuts google chart.
This is fiddle link to http://jsfiddle.net/nc6uf/
i want to find point at which mouse intersects google chart in mouse move event
graph.on('mousemove', function() {
line.attr("y1", d3.event.y - 50);
line.attr("y2", d3.event.y - 50);
});
Each Google Chart is drawn in an SVG that is in its own div. SVG objects can only be drawn within an element, and since you have two SVG canvases in your example (one on top and one on the bottom) your line will only be drawn in the element to which you appended the line. Because you used d3.select() you selected the first element that satisfied your condition "svg".
You can see the second SVG canvas interact with your mousemove by changing this line in your example:
var graph = d3.select('svg');
to:
var graph = d3.selectAll('svg');
If you wanted the line to pass through both charts, you'd need the charts to be drawn on the same SVG canvas.
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.