I have a plunker here https://plnkr.co/edit/hBWoIIyzcHELGyewOyZE?p=preview
I'm using this as starting point to create a stacked bar chart
https://bl.ocks.org/d3indepth/30a7091e97b03eeba2a6a3ca1067ca92
I need the chart to be vertical and to have axis
In my example I have the the chart vertical but I'm stuck getting it to start from the base and go upwards.
I know it's because the y attr but I'm stuck getting it to work.
.attr('y', function(d) {
return d[0];
})
try basing the y attribute as the difference between height and d[1]:
.attr('y', function(d) {
return height - d[1];
})
your plunker works for me (i think as desired?) with this correction.
explanation: these coordinates are relative to point 0, 0 (upper left hand corner) so for the y-axis of a graph like this you always have to flip it around... you captured it correctly in your axis scales (e.g. for the x: [0, width] and y: [height, 0])
Related
I have created a scatter chart in D3 using images and now need to add a horizonal line to the chart based on the value of the "yave" field from my dataset which equals 5
I have added the following script that I know has worked in previous assignments but now I can't seem to align it to the y-axis:
//y-average-line
var yaveline = function(d) {
return data[0].yave;
}
var line = svg.append("line")
.attr("x1", 0)
.attr("x2", width)
.attr("y1", yaveline)
.attr("y2", yaveline)
.attr("stroke-width", 1)
.attr("stroke", "black")
.attr("stroke-dasharray", "3,3");
A line is created but it appears near the top of the y-axis rather than at y = 5
Any ideas would be greatly appreciated. My fiddle is here
It appears at the top because the 5 is interpreted by the SVG as pixel coordinates, so it is at pixel y=5 (SVG counts from top to bottom). In your case, the 5 is not in pixel space, but in data space. To convert from the data space to the pixel space you have to use the yScale, the same way you do with the circles on the scatterplot:
var yaveline = function(d) {
return yScale(data[0].yave);
}
This will convert data[0].yave from data to pixel, that is, it will return the pixel that is associated with the data value.
How can I make the transition of an area originate at the bottom of an svg rather than the top? When changing the height using this area function it will originate at the top.
area: function(width, height) {
var x = this.xScale(width),
y = this.yScale(height);
return d3.svg.area()
.x(function(d) { return x(d.x); })
.y0(height)
.y1(function(d) { return y(d.y); });
}
Transition with new height h:
var area = this.area(w, h);
svg.datum(data)
.transition()
.ease('linear', 1, .3)
.duration(1000)
.attr('d', area);
I've found this SO question but can't translate it to my problem:
D3.js Transitions
Update
Here is the code: http://jsfiddle.net/g3yS5/12/
The area a1 is situated at the top and when transitioning to a2 it pushes from top to bottom. I guess the solution to the problem involves getting the area to render to the bottom initially? If so how could I do this?
Is this what you want?
svg.select('path')
.datum(data)
.attr('transform', 'translate(0, 80)') // <---- here
.transition()
.ease('linear', 1, .3)
.duration(1000)
.attr('transform', 'translate(0, 0)') // <---- then here
.attr('d', a2);
It first translates the path to be at the bottom of the new height, then transitions that back to its natural position (NOTE: the translation is hard coded to be 80; you would want to compute it based on the height delta). This results in the path jumping to the new position, which might be what you expect.
Otherwise, you can also transition it in two steps. See this jsFiddle.
Transitions start from whatever value the attribute you're animating currently is, so you can simply change that value before the transition starts:
svg.datum(data)
.attr('d', startingArea) // set initial 'state'
.transition().ease('linear', 1, .3).duration(1000)
.attr('d', area);
In this case, startingArea could look a lot like your existing area function, but y0 would be 0 perhaps, or maybe y1 would be height. I'd have to see your code for more a more specific solution.
I have produced a scatter chart using D3. It works okay. Now I want to cover the points plotted with an ellipse.
I've tried to get the xOval value by looking for the max value in the dataset, same for yOval.
var xOval = d3.scale.linear()
.domain([0,d3.max(dataset, function(d) { return d[0]; })])
var yOval = d3.scale.linear()
.domain([0,d3.max(dataset, function(d) { return d[1]; })])
Further down in the body after the scatter is plotted I have :
svg.append("ellipse")
.attr("cx", function(d) {
return xOval(d[0]);
})
.attr("cy", function(d) {
return yOval(d[1]);
})
.attr("rx", 10)
.attr("ry", 10);
This fails to work.
Any ideas where I'm going wrong?
This is quite difficult to do with an ellipse, as you have to make sure that everything is within the path defined by it. Here's how to do it with a circle, which should get you started.
The easiest way to do this is to use D3's polygon methods for this. In particular, you can get the centroid of the polygon defined by all points as the center of the circle. Then all you have to do is find the distance to the point that is farthest away from that and you have your radius.
The code looks something like this, assuming suitable data structures.
var center = d3.geom.polygon(dataset).centroid(),
r = d3.max(dataset, function(d) {
return Math.sqrt(Math.pow(xScale(d[0]) - xScale(center[0]), 2) + Math.pow(yScale(d[1]) - yScale(center[1]), 2));
}) + rScale;
rScale here is the radius of the circles that represent a point. The radius of the covering circle needs to be extended by that to make sure that not only the center, but the entire data point circle is covered.
Complete example here. Note that this is not optimal in the sense that there would be a smaller circle that covers all data points.
I'm trying to draw a bar chart with padding between the bars, but I can't get rangeRoundBands to insert padding - though the docs tell me this is possible, so I'm doing something wrong.
This is my code:
var x0 = d3.scale.ordinal()
.rangeRoundBands([margin.left, width], 0.1, 0.1);
...
ap_bars.transition().duration(1000)
.attr("x", function(d, i) {
return i * x0.rangeBand();
})
.attr("width", x0.rangeBand());
But the bars are all stacked together.
JSFiddle here: http://jsfiddle.net/6pnem/5/
You're changing the size of the band with padding correctly, but the way you're plotting the bars will also put them side by side right next to each other. As you increase the padding, you'll see the that the bars just get skinnier and skinnier but stay pinched together.
Instead of using a constant based on the data index (i * x0.rangeBand()) you need to position the bar based on scaled data: (x0(d)). You'll probably want to adjust the position of the bar so that center of the bar is the center of each band: (x0(d) - x0.rangeBand() * 0.5).
I'm trying to wrap my head around the log scales provided by D3.js. It should be noted that as of yesterday, I had no idea what a logarithmic scale was.
For practice, I made a column chart displaying a dataset with four values: [100, 200, 300, 500]. I used a log scale to determine their height.
var y = d3.scale.log()
.domain([1, 500])
.range([height, 1]);
This scale doesn't work (at least not when applied to the y-axis as well). The bar representing the value 500 does not reach the top of the svg container as it should. If I change the domain to [100, 500] that bar does reach the top but the axis ticks does not correspond to the proper values of the bars. Because 4e+2 is 4*10^2, right?
What am I not getting here? Here is a fiddle.
Your scale already reverses the range to account for the SVG y-coordinates starting at the top of the screen -- ie, you have domain([min, max]) and range([max, min]). This means your calcs for the y position and height should be reversed because your scale already calculated y directly:
bars.append("rect")
.attr("x", function (d, i) { return i * 20 + 20; })
.attr("y", function (d) { return y(d); })
.attr("width", 15)
.attr("height", function (d) { return height - y(d); });
Here's an updated Fiddle: http://jsfiddle.net/findango/VeNYj/2/