D3: clustered force layout with fixed centers - d3.js

I'm trying to accomplish something similar to the image below
example http://www.magora-systems.com/media/good.png
which I found on this page. The author gives some explanation on how he does this, using a clustered force technique from http://bl.ocks.org/mbostock/7882658, but not enough for me to fully understand it
What I do not know is how to set the centers of each cluster to a predefined location (where I define these center locations in a variable for example)?
My second, but less important question is about an extra feature that is mentioned on the blog where I found the image: "it’s been decided that a bubble with the largest diameter in a group will become central".
Does anybody know how to do this?
Thank you for any help!

It is an interesting cluster view that I haven't seen before with D3, thanks for that. I looked at the pages you linked, and the author describes a guide found here.
The Grants by Year tab for the example puts 3 clusters organized by year. The example code is calling vis.coffee and defining the year center locations like this :
#year_centers = {
"2008": {x: #width / 3, y: #height / 2},
"2009": {x: #width / 2, y: #height / 2},
"2010": {x: 2 * #width / 3, y: #height / 2}
}
And I see a method for moving the circles to their year_centers like this:
# move all circles to their associated #year_centers
move_towards_year: (alpha) =>
(d) =>
target = #year_centers[d.year]
d.x = d.x + (target.x - d.x) * (#damper + 0.02) * alpha * 1.1
d.y = d.y + (target.y - d.y) * (#damper + 0.02) * alpha * 1.1
You may want to poke around in the vis.coffee or vis.js files, but the vis.coffee file is the one referenced with the source example.

Related

Box plot with dual dimension and dual x-axis using dc.js

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.

D3 Chloropleth zoom works when only with states, but breaks when I draw it with counties

I have something similar to Mike Bostock’s Zoom to Bounding in my code, however I wanted to add the ability to also drag/mousewheel zoom. Getting that working was fine, however I would end up in the situation where the svg transform/translate to zoom wouldn't play well with mousewheel zoom, because the mousehwheel zoom wouldnt be aware of the scale of the zooming as done in Mikes example.
So to fix this I try to make the click function use the zoom event also. This works fine on some parts of the map but not others and I dont know why. An example displaying what I am talking about:
https://jsfiddle.net/72g1wL0t/8/
The west hand side of the map seems to work fine when clicking on it. However if you move over to the east coast, it all goes to hell.
Even stranger, if I change the line that is drawing counties in the jsfiddle to be us.objects.states and just draw the states, the zoom then works fine for the whole map.
Anyone give me any guidance here? I know some of the zooming stuff in d3v4 would probably be the better way to do this, but I am stuck on d3v3.
var bounds = path.bounds(d),
dx = bounds[1][0] - bounds[0][0],
dy = bounds[1][1] - bounds[0][1],
x = (bounds[0][0] + bounds[1][0]) / 2,
y = (bounds[0][1] + bounds[1][1]) / 2;
var scale = .9 / Math.max(dx / width, dy / height);
var translate = [width / 2 - scale * x, height / 2 - scale * y];
svg.transition()
.duration(750)
.call(zoom.translate(translate)
.scale(scale).event);
So by more closely following this example here: https://gist.github.com/mbostock/9656675
I was able to get the desired functionality working.

d3.js geo worldmap - merge russia (shift small part from the left next to america to the right)

I created a simple worldmap with d3 as you can see here: http://bl.ocks.org/wiesson/ef18dba71256d526eb42
Is there a simple way to shift the small part of russia (as illustrated in the picture) to the right, without creating a new topojson? If not, any other idea?
Okay, the answer was straightforward. As explained in the api docs, the method rotate can turn the map.
So, rotate([-11, 0]) "rotated" the map in the position I was looking for.
var projection = d3.geo.mercator().scale(width / 2 / Math.PI)
.rotate([-11, 0])
.translate([(width) / 2, height * 1.35 / 2])
.precision(.1);

d3.js dynamically generated sentence map with curved text

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.

d3 transitioning from bar to pie and back

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.

Resources