I'm testinf d3.js and i'm trying to add links between a root node (the center one in the JsFiddle) and child nodes. How can i achieve that simply ?
Here is the code that i have so far: http://jsfiddle.net/fLqekg12/2/
var container = d3.select("svg#svg");
var data = [2, 1, 1, 1, 1, 1, 1];
var dataTree = {
id: "root",
size: 12,
children: data.map(function (d) {
return {
size: 10,
parent: "root"
};
})
};
var maxRadius = 50,
padding = 40;
var radiusScale = d3.scale.sqrt()
.domain([0, 50 /* d3.max(data) */ ])
.range([0, 50]); // maxRadius
var roughCircumference = d3.sum(data.map(radiusScale)) * 2 + padding * (data.length - 1),
radius = roughCircumference / (Math.PI * 2);
// make a radial tree layouts
var tree = d3.layout.tree()
.size([360, radius])
.separation(function (a, b) {
return radiusScale(a.size) + radiusScale(b.size);
});
// create a holder group for all the graph nodes
var svgGroup = container.append('g')
.attr('transform', 'translate(' + 80 + ',' + 90 + ')');
var nodes = tree.nodes(dataTree),
links = tree.links(nodes); // and then... ?
// declare the nodes (this creates placed groups)
var svgNodes = svgGroup.selectAll('.node')
.data(nodes) // cut out the root node, we don't need it : nodes.slice(1)
.enter().append('g')
.attr('class', 'node')
.attr('transform', function (d) {
return "rotate(" + (d.x - 90) + ") translate(" + d.y + ")";
});
// append a cirl to all nodes groups
svgNodes.append('circle').attr('r', function (d) {
return d.size;
});
EDIT
Progress was made with this code.
var diagonal = d3.svg.diagonal.radial()
.projection(function (d) {
return [d.y, d.x / 180 * Math.PI];
});
var svgLinks = svgGroup.selectAll('path')
.data(tree.links(nodes))
.enter().append('svg:path')
.attr('class', 'link')
.attr('d', diagonal)
.attr("fill", "none")
.attr("stroke", "gray");
Fiddle update: http://jsfiddle.net/fLqekg12/4/
The only thing i need now is straight lines instead of curved ones. Anyone ?
After compute and create your nodes, you have to create your links as svg line element:
var link = svgGroup.selectAll('line.link')
.data(links)
.enter().append('svg:line')
.attr("class", "link")
.attr("style","stroke:black")
.attr("x1", function(d) { return ... (x coordinate source node) })
.attr("y1", function(d) { return ... (y coordinate source node) })
.attr("x2", function(d) { return ... (x coordinate target node) })
.attr("y2", function(d) { return ... (y coordinate target node) });
You juste have to find the good formula to compute position from x,y polar coordinate.
The two working solutions are:
Using paths (easiest one, but could not transform curves into straight lines) : http://jsfiddle.net/fLqekg12/4/
Using lines. The trick is that lines cannot be used directly in place of path (see why here) and if you transform nodes, it does not work.
The solution i found was found from this post: lines have to be transformed as well if your nodes are transformed:
Use d.target.x / 180 * Math.PI)on y1 and y2 because i want a radial projection and finally transform the lines again with :
svgLinks.attr("transform", function (d) {
return "rotate(" + (d.target.x - 90) + ")";
});
Full working example here:
http://jsfiddle.net/fLqekg12/6/
Related
If you see the existing code, https://jsfiddle.net/sheilak/9wvmL8q8 when the graph is loaded for first time links that connecting the parent and child node are from border of parent node but once its collapsed and expanded, you can see same links are from center of parent node. i don't want to link to be from center of the parent node.
code
var width = 960,
height = 500;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var force = d3.layout.force()
.size([width, height])
//gravity(0.2)
.linkDistance(height / 6)
.charge(function(node) {
if (node.type !== 'ORG') return -2000;
return -30;
});
// build the arrow.
svg.append("svg:defs").selectAll("marker")
.data(["end"]) // Different link/path types can be defined here
.enter().append("svg:marker") // This section adds in the arrows
.attr("id", function(d) {
return d;
})
.attr("viewBox", "0 -5 10 10")
.attr("refX", 12)
.attr("refY", 0)
.attr("markerWidth", 9)
.attr("markerHeight", 5)
.attr("orient", "auto")
.attr("class", "arrow")
.append("svg:path")
.attr("d", "M0,-5L10,0L0,5");
var json = dataset;
var edges = [];
json.edges.forEach(function(e) {
var sourceNode = json.nodes.filter(function(n) {
return n.id === e.from;
})[0],
targetNode = json.nodes.filter(function(n) {
return n.id === e.to;
})[0];
edges.push({
source: sourceNode,
target: targetNode,
value: e.Value
});
});
for(var i = 0; i < json.nodes.length; i++) {
json.nodes[i].collapsing = 0;
json.nodes[i].collapsed = false;
}
var link = svg.selectAll(".link");
var node = svg.selectAll(".node");
force.on("tick", function() {
// make sure the nodes do not overlap the arrows
link.attr("d", function(d) {
// Total difference in x and y from source to target
diffX = d.target.x - d.source.x;
diffY = d.target.y - d.source.y;
// Length of path from center of source node to center of target node
pathLength = Math.sqrt((diffX * diffX) + (diffY * diffY));
// x and y distances from center to outside edge of target node
offsetX = (diffX * d.target.radius) / pathLength;
offsetY = (diffY * d.target.radius) / pathLength;
return "M" + d.source.x + "," + d.source.y + "L" + (d.target.x - offsetX) + "," + (d.target.y - offsetY);
});
node.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
});
update();
function update(){
var nodes = json.nodes.filter(function(d) {
return d.collapsing == 0;
});
var links = edges.filter(function(d) {
return d.source.collapsing == 0 && d.target.collapsing == 0;
});
force
.nodes(nodes)
.links(links)
.start();
link = link.data(links)
link.exit().remove();
link.enter().append("path")
.attr("class", "link")
.attr("marker-end", "url(#end)");
node = node.data(nodes);
node.exit().remove();
node.enter().append("g")
.attr("class", function(d) {
return "node " + d.type
});
node.append("circle")
.attr("class", "circle")
.attr("r", function(d) {
d.radius = 30;
return d.radius
}); // return a radius for path to use
node.append("text")
.attr("x", 0)
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.attr("class", "text")
.text(function(d) {
return d.type
});
// On node hover, examine the links to see if their
// source or target properties match the hovered node.
node.on('mouseover', function(d) {
link.attr('class', function(l) {
if (d === l.source || d === l.target)
return "link active";
else
return "link inactive";
});
});
// Set the stroke width back to normal when mouse leaves the node.
node.on('mouseout', function() {
link.attr('class', "link");
})
.on('click', click);
function click(d) {
if (!d3.event.defaultPrevented) {
var inc = d.collapsed ? -1 : 1;
recurse(d);
function recurse(sourceNode){
//check if link is from this node, and if so, collapse
edges.forEach(function(l) {
if (l.source.id === sourceNode.id){
l.target.collapsing += inc;
recurse(l.target);
}
});
}
d.collapsed = !d.collapsed;
}
update();
}
}
There are two simple ways to address this that require little modification of your existing code.
The first is half done as the target of each link is already offset when you define your path data:
return "M" + d.source.x + "," + d.source.y + "L" + (d.target.x - offsetX) + "," + (d.target.y - offsetY);
});
You could extend this to offset from the source node quite easily, just add the offsets to sourceX and sourceY as here. This way it doesn't matter if the nodes are above or under the links because they don't overlap. (There might be slight overlap, so you could add a pixel or two to the offsets to account for link width).
The second option is possibly easier in d3v4+, as it features selection.raise() (docs). This method raises the selected item to the top of the SVG (as the last child of the parent element). The is equivalent to:
this.parentNode.appendChild(this);
In your click function, after you update the graph, we can use this line to ensure the node that was clicked on rises to the top (over the links). Here's an example of that.
As a follow up question of D3.js change width of container after it is drawn I create the rectangles that fits the text length, I want to link the rectangles from bottom. But I'm stuck in getting the width of rectangle when I draw the link.
This is the js code:
var rectW = 140, rectH = 40;
// Declare the nodes.
var node = draw.selectAll('g.node')
.data(nodes, function(d) { return d.id; });
// Enter the nodes.
var nodeLabel = node.enter().append('g')
.attr('transform', function(d) { return 'translate(' + source.x0 + ',' + source.y0 + ')'; });
var nodeRect = nodeLabel.append('rect')
.attr('width', rectW)
.attr('height', rectH);
var nodeText = nodeLabel.append('text')
.attr('x', rectW / 2)
.attr('y', rectH / 2)
.text(function (d) { return d.name; });
// This arranges the width of the rectangles
nodeRect.attr("width", function() {
return this.nextSibling.getComputedTextLength() + 20;
})
// This repositions texts to be at the center of the rectangle
nodeText.attr('x', function() {
return (this.getComputedTextLength() + 20) /2;
})
Next,I'd like to link the nodeRects. Linking the top left corner is ugly, so I adjust a bit:
link.attr('d', function (d) {
var sourceX = d.source.x + 0.5*d.source.getComputedTextlength() + 10,
sourceY = (d.source.y > d.target.y)? d.source.y: (d.source.y + rectH),
targetX = d.target.x + 0.5*d.target.getComputedTextlength() +10,
targetY = (d.source.y >= d.target.y)? (d.target.y + rectH) : d.target.y;
It returns error. Is there a way that I can get access to the target rect and source rect's textlength or width?
I find an answer by myself. d.source.width doesn't work because it is not defined.
Change
nodeRect.attr("width", function() {
return this.nextSibling.getComputedTextLength() + 20;
})
to
nodeRect.attr("width", function(d) {
d.width = this.nextSibling.getComputedTextLength() + 20;
return d.width;
})
Then use d.source.width works well.
I've got this legend:
As you can see, each legend entry is the same width. Instead, I'd like each legend entry's width to vary based upon the width of the entry's symbol and text. Ultimately, I want the same distance between the ends of the leading entry's text and the start of the following entry's symbol. In other words, I'd like the same distance between 'OA' and the plus sign as between the 'OI' and the diamond and the 'RARC' and the square. I need this to be based on pixels (string lengths won't suffice). I've been trying all sorts of stuff, but haven't been successful.
Here's my code:
var legendData = [["OA", "yellow", "circle"], ["OI", "blue", "cross"], ["RARC", "green", "diamond"], ["CAPE", "red", "square"], ["Other", "black", "triangle-down"]];
this.svg.selectAll('.legend').remove() //remove remnants of previous legend so new legend has clean slate...eliminates overlays during resizing
var legend = this.svg.append('g')
.attr("class", "legend")
.attr("height", 0)
.attr("width", 0)
.attr('transform', 'translate(' + (ScatterChart.Config.margins.left + (width * .008)) + ',' + (height += .40 * ScatterChart.Config.margins.bottom) + ')');
var legendRect = legend
.selectAll('g')
.data(legendData)
;
var labelLength = 0
var labelLengthPrevious = 0
var legendRectE = legendRect.enter()
.append("g")
.attr("transform", function (d, i) {
//labelLength = labelLengthPrevious //Need to figure out pixel lengths
//labelLengthPrevious += (d[0].length) + 50
//return 'translate(' + labelLength + ', ' + 0 + ' )'; // y is constant and x growing
return 'translate(' + (i * (.15 * width)) + ', ' + 0 + ' )'; // y is constant and x growing
})
;
legendRectE
.append('path')
.attr("d", d3.svg.symbol().type((d) => {
return d[2]
}
).size((d3.min([height, width]) * ScatterChart.Config.axisFontMultiplier) * (d3.min([height, width]) * ScatterChart.Config.symbolSizeMultiplier)))
.style("fill", function (d) {
return d[1];
})
.attr('stroke', 'black')
;
//This asserts legendRectE as a node...I think. I do this so I can use the width and height measurements of legendRectE.
var node: SVGElement = <SVGElement>legendRectE.node()
legendRectE
.append("text")
.attr("x", function (d) {
return node.getBoundingClientRect().width
})
.attr("y", function (d) {
return node.getBoundingClientRect().height / 2.25
})
.text(function (d) {
return d[0];
})
.style('font-size', function () { return d3.min([height, width]) * ScatterChart.Config.axisFontMultiplier + "px" })
;
I think the answer would have something to do with this line: return 'translate(' + (i * (.15 * width)) + ', ' + 0 + ' )'; // y is constant and x growing. Right now, it just shifts to the right by multiplying the index by 15% of the chart's width. I figure I need to somehow substitute the width of the legendRectE (or of legendRect or legend) in place of (I * (.15 * width)). I can't figure out how to do that.
You can see that I use the following to get the width of legendRectE later in the code: var node: SVGElement = <SVGElement>legendRectE.node(), followed by node.getBoundingClientRect().width.
node.getBoundingClientRect().width gives me a width value where you see it being used now, but when I use this same approach to determine a value for the translate I mentioned, it chokes; and when I use legendRect or legend instead of legendRectE I only get '0'.
I thought I'd be able to edit the transform function something like this:
var legendRectE = legendRect.enter()
.append("g")
.attr("transform", function (d, i) {
var node: SVGElement = <SVGElement>legendRectE.node()
return 'translate(' + node.getBoundingClientRect().width + ', ' + 0 + ' )'; // y is constant and x growing
})
;
Obviously, I was wrong. Any ideas/advice?
p.s. I'm using d3 v3.5.
The challenge is that it is (as far as I know) difficult to determine the transform when appending elements initially as the widths are unknown. But you could go back and calculate the width of each legend entry after they are all appended and then reposition the legend entries accordingly.
The snippet below positions everything overtop of each other to start, then calculates the svg width of each legend g using getBBox. Then, using d3.sum, calculates the width of each element that was appended before it (and thus should be to the left of it) and sets the translate value to the sum of those widths accordingly.
It can probably be cleaned up a bit probably, it's a little quick. If there is lag before the elements are positioned correctly, appending them transparently and then fading them in after they are positioned might be an elegant (visually, less so programatically) solution (or appending them initially outside of the view box).
d3v4:
var data = ['short text','much longer text','the longest text passage','short text'];
var svg = d3.select('body')
.append('svg')
.attr('width',800)
.attr('height',200);
var groups = svg.selectAll('g')
.data(data)
.enter()
.append('g');
var rect = groups.append('rect')
.attr('fill',function(d,i) { return d3.schemeCategory10[i];})
.attr('height',30)
.attr('width',30);
var text = groups.append('text')
.attr('y', 20)
.attr('x', 35)
.text(function(d) { return d; });
// Now space the groups out after they have been appended:
var padding = 10;
groups.attr('transform', function(d,i) {
return "translate("+(d3.sum(data, function(e,j) {
if (j < i) { return groups.nodes()[j].getBBox().width; } else return 0; }) + padding * i) + ",0)";
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.5.0/d3.min.js"></script>
d3v3:
var data = ['short text','much longer text','the longest text passage','short text'];
var svg = d3.select('body')
.append('svg')
.attr('width',800)
.attr('height',200);
var groups = svg.selectAll('g')
.data(data)
.enter()
.append('g');
var color = ["orange","red","purple","green"];
var rect = groups.append('rect')
.attr('fill',function(d,i) { return color[i];})
.attr('height',30)
.attr('width',30);
var text = groups.append('text')
.attr('y', 20)
.attr('x', 35)
.text(function(d) { return d; });
// Now space the groups out after they have been appended:
var padding = 10;
groups.attr('transform', function(d,i) {
return "translate("+(d3.sum(data, function(e,j) {
if (j < i) { return groups[0][j].getBBox().width; } else return 0; }) + padding * i) + ",0)";
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
I'm a newbie to the d3 library and javascript in general.
I'm trying to achieve something like
this, where I have a sunburst partition but each node has a different height with respect to the radial center - but the padding to its parent/child stays the same.
I've tried looking around and couldn't come up with any solutions.
(trying to change the innerRadius/outerRadius parameters didn't seem to work :( ).
Here is my code:
var vis = d3.select("#chart").append("svg")
.style("margin", "auto")
.style("position", "relative")
.attr("width", width)
.attr("height", height)
.append("svg:g")
.attr("id", "container")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var partition = d3.layout.partition()
.sort(function (a, b) { return d3.ascending(a.time, b.time); })
.size([2 * Math.PI, radius * radius])
.value(function(d) { return d.n_leaves+1; });
var arc = d3.svg.arc()
.startAngle(function(d) { return d.x; })
.endAngle(function(d) { return d.x + d.dx; })
.innerRadius(function(d) { return Math.sqrt(d.y); })
.outerRadius(function(d) { return Math.sqrt(d.y + d.dy); });
//read data from json file and visualize it
d3.text("5rrasx_out.json", function(text) {
var data = JSON.parse(text);
var json = buildHierarchy(data,'5rrasx');
createVisualization(json);
});
// Main function to draw and set up the visualization, once we have the data.
function createVisualization(json) {
// Bounding circle underneath the sunburst, to make it easier to detect
// when the mouse leaves the parent g.
vis.append("svg:circle")
.attr("r", radius)
.style("opacity", 0);
// For efficiency, filter nodes to keep only those large enough to see.
var nodes = partition.nodes(json);
var dataSummary = [{label: 'pos', count: totalPos}, {label: 'neg', count: totalNeg}];
//set title
$("#title").text(json.title.replace(/\[.*\]/g,""));
//set chart
var path = vis.data([json]).selectAll("path")
.data(nodes)
.enter().append("path")
.attr("class", "sunburst_node")
.attr("display", function(d) { return d.depth ? null : "none"; })
.attr("d", arc)
.attr("fill-rule", "evenodd")
.style("fill", function(d) { return (d.sentiment > 0) ? colors["pos"] : colors["neg"]; })
.style("opacity", 1)
.on("mouseover", mouseover)
.on("click", click);
};
Any help would be much appreciated.
Thanks!
I know this is not a proper answer to the question above, but in case someone needs a sunburst with different dimensions for each node, here I post how to do it in R using the ggsunburst package.
# install ggsunburst
if (!require("ggplot2")) install.packages("ggplot2")
if (!require("rPython")) install.packages("rPython")
install.packages("http://genome.crg.es/~didac/ggsunburst/ggsunburst_0.0.10.tar.gz", repos=NULL, type="source")
library(ggsunburst)
# one possible input for ggsunburst is newick format
# consider the following newick "(((A,B),C),D,E);"
# you can define the distance in node A with "A:0.5"
# you can define size in node E with "E[&&NHX:size=5]"
# adding both attributes to the newick
nw <- '(((A:0.5,B),C:3),D[&&NHX:size=5],E[&&NHX:size=5]);'
sb <- sunburst_data(nw)
sunburst(sb, rects.fill.aes = "name") + scale_fill_discrete(guide=F)
as you can see in the code, these attributes can be defined independently, and as you can see in the plot they affect the dimennsions of the correponding nodes:
node "A" is 0.5 times shorter than "B", which is defined by the attribute "distance"
E has an angle 5 times wider than C, which is defined by the attribute "size".
and here an attempt to resemble the example posted in the question with a newick tree
nw <- "(((.:0[&&NHX:support=1.0:dist=0.0:name=.:size=3],a3:1[&&NHX:color=2:support=1.0:dist=1.0:name=a3:size=1])1:1[&&NHX:color=-3:support=1.0:dist=1.0:name=a2])1:1[&&NHX:color=-1:support=1.0:dist=1.0:name=a1],b1:1.8[&&NHX:color=1:support=1.0:dist=1.8:name=b1:size=5],(((a4:1[&&NHX:color=1:support=1.0:dist=1.0:name=a4:size=1],b4:1.8[&&NHX:color=-1:support=1.0:dist=1.8:name=b4:size=1],c4:1.5[&&NHX:color=2:support=1.0:dist=1.5:name=c4:size=1],d4:0.8[&&NHX:color=-2:support=1.0:dist=0.8:name=d4:size=1])1:1[&&NHX:color=1:support=1.0:dist=1.0:name=b3:size=1])1:1[&&NHX:color=-3:support=1.0:dist=1.0:name=b2:size=1],(c3:1[&&NHX:color=1:support=1.0:dist=1.0:name=c3:size=1],(e4:1[&&NHX:color=-2:support=1.0:dist=1.0:name=e4:size=1])1:0.5[&&NHX:color=-1:support=1.0:dist=0.5:name=d3:size=1])1:0.5[&&NHX:color=1:support=1.0:dist=0.5:name=c2:size=1])1:1[&&NHX:color=-1:support=1.0:dist=1.0:name=c1:size=1],d1:0.8[&&NHX:color=3:support=1.0:dist=0.8:name=d1:size=20]);"
sb <- sunburst_data(nw, node_attributes = "color")
sunburst(sb, leaf_labels.size = 4, node_labels.size = 4, node_labels = T, node_labels.min = 1, rects.fill.aes = "color") +
scale_fill_gradient2(guide=F) + ylim(-8,NA)
I am trying to create a radar chart similar to the link here (
http://www.larsko.org/v/euc/).
I was able to create axes (my work so far), but I am having a problem to draw lines in it.
For instance, if I have a list of values something like below, how can I draw a line in the radar chart?
var tempData = [56784, 5.898, 3417, 0, 0, 0]
Edit: I have included code. I am having a problem finding XY coordinates and I think XY value has to be derived from "scales".
var width = 1000,
height = 960,
r = (960 / 2) - 160;
var svg = d3.select("#radar")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + ", " + height / 2 + ")");
d3.csv("data/results.csv", function(data) {
var headerNames = d3.keys(data[0]);
headerNames.splice(0, 1); //Remove 'scenario'
var minList = $.map(headerNames, function(h) {
return d3.min($.map(data, function(d) {
return d[h];
}));
}),
maxList = $.map(headerNames, function(h) {
return d3.max($.map(data, function(d) {
return d[h];
}));
}),
scales = $.map(headerNames, function(h, i) {
return d3.scale.linear()
.domain([minList[i], maxList[i]])
.range([50, r]);
}),
axes = $.map(headerNames, function(h, i) {
return d3.svg.axis()
.scale(scales[i])
.tickSize(4);
});
function angle(i) {
return i * (2 * Math.PI / headerNames.length) + Math.PI / headerNames.length;
}
var line = d3.svg.line()
.interpolate("cardinal-closed")
/* computing X and Y: I am having a problem here
.x(function(d){ return scales(d); })
.y(function(d){ return scales(d); }); */
$.each(axes, function(i, a) {
svg.append("g")
.attr("transform", "rotate(" + Math.round(angle(i) * (180 / Math.PI)) + ")")
.call(a)
.selectAll("text")
.attr("text-anchor", "middle")
.attr("transform", function(d) {
return "rotate(" + -angle(i) * (180 / Math.PI) + ")";
})
//Drawing line
svg.selectAll(".layer")
.data(data)
.enter()
.append("path")
.attr("class", "layer")
.attr("d", function(d) {
return line(d);
})
}) // End CSV
Example results.csv
scenario,n_dead_oaks,percent_dead_oaks,infected_area_ha,money_spent,area_treated_ha,price_per_oak
baseline,56784,5.898,3417,0,0,0
scen2,52725,5.477,3294,382036,35,94.12071939
RS_1,58037,6.028,3407,796705,59,-635.8379888
RS_2,33571,3.487,2555,1841047,104,79.31103261
RS_3,46111,4.79,2762,1176461,61,110.227771
As Squeegy suggested, you should share some code showing your current progress and how you have achieved to create the axes.
Anyways, this is how I would go about this:
For a given list of values that you want to represent as a line, find the [x,y] coordinates of every point of the line, i.e. place your data-points on each axis. If you have a scale system in place already to draw your axes, this shouldn't be too hard.
Use d3.svg.line to draw a line that goes through all these points.
The code would end up looking like this:
var tempData = [56784, 5.898, 3417, 0, 0, 0];
/** compute tempPoints from tempData **/
var tempPoints = [[123, 30], [12, 123], [123, 123], [0,0], [0,0], [0,0]];
var line = d3.svg.line();
d3.select('svg').append('path').attr('d', line(tempPoints) + 'Z'); // the trailing Z closes the path
I think I have a solution for now and I appreciate all of your response! Here is my current solution for my posting.
function getRowValues(data) {
return $.map(data, function(d, i) {
if (i != "scenario") {
return d;
}
});
}
function getCoor(data) {
console.log(data);
var row = getRowValues(data),
x,
y,
coor = [];
for (var i = 0; i < row.length; i++) {
x = Math.round(Math.cos(angle(i)) * scales[i](row[i]));
y = Math.round(Math.sin(angle(i)) * scales[i](row[i]));
coor.push([x, y]);
}
return coor;
}
var line = d3.svg.line()
.interpolate("cardinal-closed")
.tension(0.85);
svg.selectAll(".layer")
.data(data)
.enter()
.append("path")
.attr("class", "layer")
.attr("d", function(d) { return line(getCoor(d)) + "Z"; })
.style("stroke", function(d, i){ return colors[i]; })
.style("fill", "none");