D3js: Draw pack layout without the outermost circle - javascript

I am trying to draw a pack layout in d3.js without outermost variable.
I want to draw a pack layout without outer most parent circle. Is there any way to do it?

Yes, there is. I would suggest following approach: you leave all circle pack initialization intact. You only change the point of code where circles are actually appended to DOM/SVG tree. I'll show this in a couple of examples. This jsfiddle is an example of "regular" circle pack:
The key code responsible for adding circles to the DOM tree is this:
var circles = vis.append("circle")
.attr("stroke", "black")
.style("fill", function(d) { return !d.children ? "tan" : "beige"; })
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.attr("r", function(d) { return d.r; });
If one adds just this line between "vis" and ".append("circle")": (another jsfiddle available here)
.filter(function(d){ return d.parent; })
the root node will disappear:
If one adds this line:
.filter(function(d){ return !d.children; })
all nodes except leave nodes (in other words, those without children) will disappear:
And, a little bit more complex, this line
.filter(function(d){ return (d.depth > 1); })
will make the root parent circle and all its direct children disappear:

Related

How to remove the outer circle in D3 bubble chart

I am trying to get rid of the outer circle of the bubble chart. But actually now I am at my wit's end... It seems there is few tutorial online on how to plot bubble chart using csv Data. Please check out my working PLUNK and help me out.
PLUNK: http://plnkr.co/edit/87WLm3OmK1jRtcq8p96u?p=preview
d3.csv("count_s.csv", function(csvData) {
var years = [2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014];
pack.value(function(d) {
return +d["count" + years[i]];
});
var data = {
name: "city",
children: csvData
};
var node = svg1.selectAll("g.node")
.data(pack.nodes(data), function(d) {
return d.city;
});
The code responsible for circle creation in your example is this (file bubble.js, lines 63-70):
//Add the Circles
var circles = nodeEnter.append("circle")
.attr("r", function(d) {
return d.r;
})
.style("fill", function(d) {
return color1(d.city);
});
All you need to do is to put the line
.filter(function(d){ return d.parent; })
before call to append(), like this:
//Add the Circles
var circles = nodeEnter
.filter(function(d){ return d.parent; })
.append("circle")
.attr("r", function(d) {
return d.r;
})
.style("fill", function(d) {
return color1(d.city);
});
and you will get:
The explanation of the solution is that added line simply excludes any circle that does not have a parent (which is actually only the outermost circle) from rendering.
Modified plunk is here.
NOTE: The text in the middle of the outer circle is still displayed. If you do not want it either, you may apply similar code solutions as the one used for the circle itself.

Doughnut graph is not working in d3 with two Arc object dynamically

I'm trying to create a doughnut graph, I have two entities to represent on the dougnut graph and need to show one with thick line i.e wider arc and one with narrow arc.
I tried to achieve like this. Here is the FIDDLE
I made two arc in beginning arc and arc2 and in the function
var slice_path = slice_group.append("path")
.attr("fill", function(d, i) { return color(i); })
.attr("d", function(d,i){console.log(d); return arc;})
.each(function(d) { this._current = d; })
here in the third line I want to do something like this
.attr("d", function(d,i){
if(based on condition in d.series)
return arc;
else
return arc2;
})
But I'm not able to make it work, If i write it as
.attr("d", function(d,i){console.log(d); return arc;})
It do not work ,but if i write as
.attr("d", arc)
It works.
Could you guide me in case... !..

Using D3 data().enter()

The following toy problem illustrates my issue. I have an array of "locations", say a treasure map. Each item in the array for example monsters or treasure could exist at multiple locations on the map. e.g.
locations = [
{name:'treasure', color: 'blue', coords:[[100,100], [200,300]]},
{name:'monsters', color: 'red', coords:[[100,150], [220,420], [50,50]]}
]
Now I want to plot these using D3. The bad/naive approach (that works - see here for fiddle), would look like this:
for location in locations
for coords in location.coords
svg.append('circle')
.attr('cx', coords[0])
.attr('cy', coords[1])
.attr('r', 8)
.style('fill', location.color)
.datum(location)
However, when I modify the contents of the data, I don't want to have to run this naive code each time. It appears that using data() and enter() is the "correct" way to do it, but I can't figure out how it works with the sub-coordinates. e.g.
svg.selectAll('circle').data(locations).enter().append('circle')
.attr('cx', (d) -> d.coords[0][0])
.attr('cy', (d) -> d.coords[0][1])
.attr('r', 8)
.style('fill', (d) -> d.color)
This works great, but as you can see I am only printing the FIRST coordinate for each location, where I want to print them all. I suspect the only way to do this is to flatten my data array so there are 5 entries in total - 3 monsters and 2 treasure items.
Just wondering if there is a way to handle this better using D3.
For this, you need nested selections. The idea is that instead of appending a single element per data item, you append several. In code, it looks like this:
// append a `g` element for each data item to hold the circles
var groups = svg.selectAll("g.circle").data(locations)
.enter().append("g").attr("class", "circle");
// now select all the circles in each group and append the new ones
groups.selectAll("circle")
// the d in this function references a single data item in locations
.data(function(d) { return d.coords; })
.enter().append("circle")
.attr("cx", function(d) { return d[0]; })
.attr("cy", function(d) { return d[1]; });
It works the same for update and exit selections.

Processing a multidimensional data array with d3.js

Currently I am learning some "D3.js" and attempting to get my head around the way data is processed and selected.
I'm stuck on the following task I've created for myself.
Ideally, I want something that is functionally equivalent to:
<svg>
<circle r=​"20.5" cx=​"100" cy=​"200">​</circle>​
<circle r=​"20.5" cx=​"300" cy=​"10">​</circle>​
</svg>
What I have currently (with my logic) is:
var matrix = [ [{ "x": 100, "y": 200 }], [{ "x": 300, "y": 10 }]];
var result = d3.select("body").append("svg") // Append SVG to end of Body
.data(matrix) // select this data
.selectAll("g") //g is a svg grouping tag
.data(function (d) { return d; }) //Unwrap the first part of the array
.enter() // Grab all the data that is new to the selection in each array
.selectAll("g")
.data(function (d) { return d;}) // foreach each item inside the 1D array
.enter() // For all the data that doesn't exist already in the SVG
.append("circle") // Append Circle to the DOM with the following attributes
.attr("r", 20.5)
.attr("cx", function (d) { return d.x; })
.attr("cy", function (d) { return d.y; });
};
Weirdly enough the following :
var result = d3.select("body").append("svg")
.data(matrix)
.selectAll("g")
.enter()
.append("circle")
.attr("r", 20.5)
.attr("cx", function (d) { return d.x; })
.attr("cy", function (d) { return d.y; });
};
Seems somehow able to get the first item in the array but doesn't iterate correctly. I'm not quite sure how it's entering the array.
D3 seems to be quite a big step away from the programming paradigms I'm used to, and more difficult to debug so it would be awesome if someone could explain where I'm going wrong.
Oh, and while the example is quite useless and I could flatten it using the merge command - for the purposes of fully understanding D3 manipulation. I'd like to draw the couple of circles without the merge :)
Thanks!
Seeing you mention that you're new to d3 I'll make a few comments on the basics.
The first is that we're trying to place some svg elements on the DOM, so first we have to have a svg canvas to work on. Typically its set up early in the code and looks something like this:
var svg = d3.select("body")
.append("svg")
.attr("width", 350)
.attr("height", 250);
Note that it would be best to define variables for height and width (but I'm being lazy).
So now you have your canvas lets look at how d3 iterates. d3 iterates over an array, so you don't have to have an array within an array for your example as in:
var matrix = [ { "x": 100, "y": 200 }], [{ "x": 300, "y": 10 }];
Now you're second block of code is almost there, with just a bit of rearrangement. The first thing we need to do is t create placeholders for the circles in your svg canvas using svg.selectAll("circle"). Next we introduce the data to the empty placeholders using data(matrix) and this is bound using 'enter()`. Now all we have to do is append the circles and give them some attributes which is all the rest of the code does
svg.selectAll("circle")
.data(matrix)
.enter()
.append("circle")
.attr("r", 20.5)
.attr("cx", function (d) {
return d.x;
})
.attr("cy", function (d) {
return d.y;
});
You can see this all put together in this fiddle with some minor changes.
If you want to get to know d3 I'd really recommend get Scott Murray book on d3 it's an excellent introduction

Infinite Recursion in D3 Quadtree

I am trying to add an element to a quadtree in D3 but I am getting a "RangeError: Maximum stack size exceeded' error in the console whenever I add more than one element. My quadtree declaration looks like this (in CoffeeScript):
quadtree = d3.geom.quadtree()
.x((d) -> x_scale(d[dim_1]))
.y(0)
quadroot = quadtree([])
... later in another function (quadtree and quadroot are both scoped globally) ...
quadtree.extent([[x_scale.range()[0], 0],[x_scale.range()[1], 0]])
datapoints = datapoints_g.selectAll("circle")
.data(vis_data)
datapoints.exit()
.remove()
# now add / transition datapoints
datapoints.enter()
.append("circle")
.attr("cx", x_scale.domain()[0])
.attr("cy", y_scale.domain()[0])
.on("mouseover", showDetails)
.on("mouseout", hideDetails)
datapoints
.transition()
.duration(1000)
.attr("r", (d) ->
if sizeBy == 'none'
return datapoint_size
else
return size_scale(d[sizeBy]))
.attr("cx", (d) -> x_scale(d[dim_1]))
.attr("cy", (d) ->
if dim_2 != 'none'
y_scale(d[dim_2])
else
quadroot.add(d)
# calculateOffset(maxR)
)
I am attempting to modify the example from http://fiddle.jshell.net/6cW9u/8/, just FYI; however, I still get the infinite recursion error upon adding the second element to the quadroot even when I comment out all of the other code from the example.
Any guidance would be appreciated greatly!
Thanks in advance.

Categories