I have the following d3.js fiddle that prints sentences in a wavy line.
As you can see it prints them all overlapping each other. How can I instead achieve the following effect aka fitting them in as best they could going from top to bottom (with some randomness and wavyness)?
The key is of course in the line:
.append("path").attr("d", "M 10,90 Q 100,15 200,70 Q 340,140 400,30");
but how can I generate these strings to do what I want?
EDIT: Sorry, just fixed wrong js fiddle link!
You can set the transform attribute to move the coordinate system of the elements you append. This way, you can offset each new element by a random amount:
svg.append("g")
.attr("transform", "translate(" + (Math.random() * 50) + "," + (i * (50 + Math.random() * 100)) + ")")
Complete demo here. You may have to tweak the numbers to get exactly what you want.
Related
Is it possible with dc.js to draw two x-axis of a graph i.e. one is below and one is above. One Dimension/ x-axis contain a b and above x-axis contain 1 (a b with below a-axis) 2 (a b with below x-axis). An img is attached to explain the view. If it is possible kindly give some suggestion.
Regards.
As for adding lines between the box plots, here is a hacky solution that works ok. Would probably need some work to make it general.
Assume we have the domain (['1A', '1B', '2A, '2B', ...]) in a variable called domain.
We can add a pretransition handler that draws lines after every second box:
function x_after(chart, n) {
return (chart.x()(domain[n]) + chart.x()(domain[n+1])) / 2 + chart.margins().left + 7; // why 7?
}
chart.on('pretransition', chart => {
let divide = chart.g().selectAll('line.divide').data(d3.range(domain.length/2));
divide.exit().remove();
divide = divide.enter()
.append('line')
.attr('class', 'divide')
.attr('stroke', 'black')
.merge(divide);
divide
.attr('x1', n => x_after(chart, n*2 + 1))
.attr('x2', n => x_after(chart, n*2 + 1))
.attr('y1', chart.margins().top)
.attr('y2', chart.margins().top + chart.effectiveHeight())
})
This uses the D3 general update pattern to add a vertical line after every other box (specifically those with odd index number).
It takes the average of the X position of 1B and 2A, 2B and 3A, etc. I have no idea why I had to add 7, so probably I am missing something.
demo fiddle.
My code is safely stored on my bl.ocks site
I've effectively converted a scatter plot into a hexagon plot by defining a polyline shape with 6 edges and referencing HEX in the code instead of circle. However, my scatter plot started neatly at (0,0) on the origin of my graph scales. My hex plot starts offset by -50,-20 and I can't find a way around it to revert it back to (0,0).
Here is the scatter plot code for info
You need to remove the transform on this line.
svg.append("g")
.attr("class", "path.hex")
.attr("transform", "translate(50, 20)")
.call(hex);
And potentially the 50,20 translating on this line (I think you may still need the scales)
hexs.attr("transform", function(d) {
return "translate(" + xScale(d.Board) + 50 + "," + yScale(d.Alight) + 20 + ")" + " scale(" + rScale(d.Totals) + ")"
})
I'm currently working on a quite basic graph using 2 ordinal axes. X axis shows 4 categories, Y axis shows 3. For some reason, the plotted circles don't align with the plotted axes.
An example can be seen at http://jsfiddle.net/SrdY6/. Problem seems to be translation-related, but the only translation in there is applied to the large containing <g> element:
var lunchgraph = svg.append("g")
.attr("class", "lunchgraph")
.attr("transform", "translate(" + lunchmargin.left + "," + lunchmargin.top + ")");
I've been looking at this for some time now, but can't spot where things go wrong... Anyone with more insight?
Nothing like putting a question out there and risking public shame, only to find out the answer within minutes after posting.
For ordinal axes configured with rangeBands or rangeRoundBands, the scale function returns the lower value of the given input. To have the plot align with the exact categorical labels, you need to add half of the rangeBand to the calculated coordinate.
So: no problem with the translations or anything, but with the computation of cx and cy coordinates for placing the circles in the graph.
Correct code:
.attr("cx", function(d) { return x(d.label) + x.rangeBand()/2 ;} )
.attr("cy", function(d) { return y(d.sqid) + y.rangeBand()/2 ; } )
I've been extending the code in this example:
http://bl.ocks.org/mbostock/5914438
But I can't seem to figure out how to place points on this map in a manner in which they can be integrated in the zoom. Paths work fine but it seems like the modified projection can't be used to project the coordinates of the points, and in the zoomed function, the scale() addition to the transform() when applied to the element containing the points seems to scale the points so large that they fill the entire screen. Here's my additional points:
var sitesG = svg.append("g").attr("id","sitesG");
var osites = sitesG.selectAll(".sites")
.data(sites)
.enter()
.append("g")
.attr("transform", function(d) {return "translate(" + projection([d.x,d.y]) + ")scale(" + projection.scale() + ")"})
osites.append("circle").attr("r", 10)
And here's the function in zoomed():
d3.select("#sitesG")
.attr("transform", "translate(" + zoom.translate() + ")scale(" + zoom.scale() + ")")
I've tried this with individual elements for the sites as well, but with no success. Does anyone have an example that puts points on geo.tile?
I managed to figure it out. My main problem was that I was not scaling the "r" attribute of the circles to match the zoom.scale() when I was properly projecting the points, which caused them to have 13,000px+ radii.
You can see it working here:
http://bl.ocks.org/emeeks/6147081
LIVE DEMO
So I have this notion that all single axis data should be allowed to be displayed in all the basic ways; and at the very least from a pie to a bar. Ideally this would be an animated transition, but thats were the difficulty comes in.
Getting a pie chart to work is easy enough, as is getting a bar chart. Here is what I have so far:
# fields
width = 750
height = width/2
margin = 20
radius = (height-(margin*2))/2
# helpers
pie = d3.layout.pie().value (d) -> d
arc = d3.svg.arc()
.outerRadius(radius)
.innerRadius(radius/4)
x = d3.scale.linear().domain([0, 100]).range [0, width]
$http.get('/Classification_Top_10_by_Count.json').success (data) ->
percents = (parseFloat item.Percent for item in data).sort d3.ascending
svg = d3.select('#svgStage').append('svg')
.attr('width', width+(margin*2))
.attr('height', height+(margin*2))
svg.data([percents])
g = svg.append('g')
.attr('transform', "translate(#{radius},#{radius})")
paths = g.selectAll 'path'
paths.data(pie).enter().append('path')
.attr('d', arc)
toBars = ->
g.selectAll('path').transition().duration(2000)
.attr 'd', (d, index) ->
# this is over complex because I was playing with it.
cord =
tl : [0, index*20]
tr : [d.value*20, index*20]
br : [d.value*20, index*20-20]
bl : [0, index*20-20]
oCord = [
cord.tl
cord.tr
cord.br
cord.bl
]
"M #{oCord[0][0]}, #{oCord[0][2]}
A 0, 0 0 0, 0 #{oCord[1][0]}, #{oCord[1][3]}
L #{oCord[2][0]}, #{oCord[2][4]}
A 0, 0 0 0, 0 #{oCord[3][0]}, #{oCord[3][5]}
Z"
Obviously for this to work its got to be path element to path element, and the transition is working now. Problem is it looks like crap. The moment it starts it looks garbled, until it over and becomes decent bar chart.
I've been looking at this : http://d3-example.herokuapp.com/examples/showreel/showreel.html
Which demonstrates a bar transitioning to a donut in much the way I would like. Looking at the source code, this is accomplished through a custom tween. (view source line 518)
Now I'm in over my head. What is going on here? How can I get this transition to work? Has anyone else out there dealt with this problem?
UPDATE
Just to be clear, below illustrations the intention of my transition abit more clearly.
Bounty clarity. I added a bounty to this question because I need an explanation of what was going wrong. Superboggly did that, so he got the bounty. However Amit Aviv's approach is superior, so I accept his answer as the most correct. I have also +1ed both.
Here is my take: http://jsfiddle.net/amitaviv99/x6RWs/42/
My approach was to approximate both the arcs & bars using cubic bezier curves, with the exact same number of control points. The code is somewhat complicated, and need some work. But the result is quite smooth.
Here is an excerpt (SO requires..)
var bezierArc = function(radiusIn, radiusOut, startAngle, endAngle){
var arcIn = makeCompArc(radiusIn, startAngle, endAngle);
var arcIOut = makeCompArc(radiusOut, startAngle, endAngle);
var lines = makeBezierDoubleLine(radiusIn, radiusOut, startAngle, endAngle);
var path = [arcIn, lines[0], arcOut, lines[1]].join(' ');
return path;
}
D3 does a pretty good job of interpolating between paths, but it was having trouble with your original before and after path so instead of taking over the whole tweening process myself I thought maybe we could come up with better paths to make the job easier for D3. My result.
The first thing is to look at the svg arc path element. It basically goes like this:
A rx,ry a f1,f2 x,y
you can read the details here. This will draw an arc from wherever you are (previous final coordinate) to the coordinates x,y. But the things to focus on are that the first two numbers are the implied ellipse's radii and the last part before the end coordinates, that I've marked f1,f2, are flags and so not interpolate-able.
So the main weirdness in the transition from your code is because you are trying to interpolate between
A rx,ry, 0 0,1
A 0,0 0 0,0
You will immediately see a smoother transition if you set your end-path to A0,0 0 0,1 in the one case.
To make the pieces fit together a bit better I animated the pie's inner radius so that the segments looked more like the bars but curved, then I let D3 figure out the curve-to-bar transition but without switching the arc flag. Then you want the bars to have flat ends. The path will have a flatter arc if you increase your implied ellipses radii! So I simply used 100,100. My final transition-to path for the bars looks like:
"M " + oCord[0][0] + "," + oCord[0][1] +
"A100,100 0 0,1 " + oCord[1][0] + "," + oCord[1][1] +
"L " + oCord[2][0] + "," + oCord[2][1] +
"A100,100 0 0,0 " + oCord[3][0] + "," + oCord[3][1] +
"Z";
Then To actually, properly, flatten the endpoints I have a second transition (they run serially) to zero the Arc segments of the path. I suspect there is a better way to do this kind of cleanup with D3 transitions, but a transition with duration 0 also works.
To get the reverse to work nicely I set the paths to the flattened-arc-curves from above. Having the large radius and correct flags means the D3-computed transition back to the doughnut chart works well. Then I simply animate the inner radius back out.