Transform label in reverse order d3 radial chart - javascript

I have a d3 radial chart created using some community sample and stack-overflow posts.
Here the two bottom labels and numbers are in mirrored form (A13 and A14). Looking for some snippets to transform only this two in counter-clockwise with numbers top (next to the chart) then label so that it will be in better readable form.
JSFiddle
var data = [
{"name":"A11","value":217,"color":"#fad64b"},
{"name":"A12","value":86,"color":"#f15d5d"},
{"name":"A13","value":79,"color":"#f15d5d"},
{"name":"A14","value":82,"color":"#f15d5d"},
{"name":"A15","value":101,"color":"#fad64b"},
{"name":"A16","value":91,"color":"#fad64b"}
];
var width = 500;
var height = 300;
var barHeight = height / 2 - 15;
var formatNumber = d3.format('s');
var color = d3.scale.ordinal()
.range(['#F15D5D', '#FAD64B']);
var svg = d3.select('#chart').append('svg')
.attr('width', width)
.attr('height', height)
.attr('class', 'radial')
.append('g')
.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');
var extent = [0, d3.max(data, function(d) { return d.value; })];
var lastNum = extent[1];
var percentageOne = (lastNum*25)/100;
var percentageTwo = (lastNum*50)/100;
var percentageThree = (lastNum*75)/100;
var tickValues = [percentageOne, percentageTwo, percentageThree, lastNum];
var barScale = d3.scale.linear()
.domain(extent)
.range([0, barHeight]);
var keys = data.map(function(d, i) {
return d.name;
});
var numBars = keys.length;
// X scale
var x = d3.scale.linear()
.domain(extent)
.range([0, -barHeight]);
// X axis
var xAxis = d3.svg.axis()
.scale(x).orient('left')
.tickFormat(formatNumber)
.tickValues(tickValues);
// Inner circles
var circles = svg.selectAll('circle')
.data(tickValues)
.enter().append('circle')
.attr('r', function(d) {
return barScale(d);
})
.style('fill', 'none')
.style('stroke-width', '0.5px');
// Create arcs
var arc = d3.svg.arc()
.startAngle(function(d, i) {
var a = (i * 2 * Math.PI) / numBars;
var b = ((i + 1) * 2 * Math.PI) / numBars;
var d = (b - a) / 4;
var x = a + d;
var y = b - d;
return x; //(i * 2 * Math.PI) / numBars;
})
.endAngle(function(d, i) {
var a = (i * 2 * Math.PI) / numBars;
var b = ((i + 1) * 2 * Math.PI) / numBars;
var d = (b - a) / 4;
var x = a + d;
var y = b - d;
return y; //((i + 1) * 2 * Math.PI) / numBars;
})
.innerRadius(0);
// Render colored arcs
var segments = svg.selectAll('path')
.data(data)
.enter().append('path')
.each(function(d) {
d.outerRadius = 0;
})
.attr('class', 'bar')
.style('fill', function(d) {
return d.color;
})
.attr('d', arc);
// Animate segments
segments.transition().ease('elastic').duration(1000).delay(function(d, i) {
return (25 - i) * 10;
})
.attrTween('d', function(d, index) {
var i = d3.interpolate(d.outerRadius, barScale(+d.value));
return function(t) {
d.outerRadius = i(t);
return arc(d, index);
};
});
// Outer circle
svg.append('circle')
.attr('r', barHeight)
.classed('outer', true)
.style('fill', 'none')
.style('stroke-width', '.5px');
// Apply x axis
svg.append('g')
.attr('class', 'x axis')
.call(xAxis);
// Labels
var labelRadius = barHeight * 1.025;
var labels = svg.selectAll('foo')
.data(data)
.enter()
.append('g')
.classed('labels', true);
labels.append('def')
.append('path')
.attr('id', function(d, i) { return 'label-path' + i; })
.attr('d', function(d) {
return 'm0 ' + -(barScale(d.value) + 4) + ' a' + (barScale(d.value) + 4) + ' ' + (barScale(d.value) + 4) + ' 0 1,1 -0.01 0';
});
labels.append('def')
.append('path')
.attr('id', function(d, i) { return 'label-pathnum' + i; })
.attr('d', function(d){
return 'm0 ' + -(barScale(d.value) + 14) + ' a' + (barScale(d.value) + 14) + ' ' + (barScale(d.value) + 14) + ' 0 1,1 -0.01 0';
});
labels.append('text')
.style('text-anchor', 'middle')
.style('fill', function(d, i) {
return d.color;
})
.append('textPath')
.attr('xlink:href', function(d, i) { return '#label-path' + i; })
.attr('startOffset', function(d, i) {
return i * 100 / numBars + 50 / numBars + '%';
})
.text(function(d) {
return d.name.toUpperCase();
});
labels.append('text')
.style('text-anchor', 'middle')
.style('fill', function(d, i) {
return d.color;
})
.append('textPath')
.attr('xlink:href', function(d, i) { return '#label-pathnum' + i; })
.attr('startOffset', function(d, i) {
return i * 100 / numBars + 50 / numBars + '%';
})
.text(function(d) {
return d.value;
});

You need to modify the path for the specific elements that need to be flipped. To do this, I start by storing the angle in your data object:
.each(function(d,i) {
d.outerRadius = 0;
var angleStart = (i/numBars) * 360;
var angleEnd = ((i+1)/numBars) * 360;
d.angle = (angleStart + angleEnd) / 2;
})
Then I tested the angle when creating the paths for the text and I reverse the path for the flipped text case:
var len = barScale(d.value) + 4;
if(d.angle > 91 && d.angle < 269) {
len += 8; // the flipped text is on the inside of the path so we need to draw it further out
return 'M -0.01 ' + (-len) + ' A ' + len + ' ' + len + ' 0 1,0 0 ' + (-len);
}
else {
return 'M 0 ' + (-len) + ' A' + len + ' ' + len + ' 0 1,1 -0.01 ' + (-len);
}
Then, you need to flip your '% around the path' for the placement of the text along the reversed path:
.attr('startOffset', function(d, i) {
if(d.angle > 91 && d.angle < 269)
return (100 - (i * 100 / numBars + 50 / numBars)) + '%';
else
return i * 100 / numBars + 50 / numBars + '%';
})
Working fiddle can be found here: https://jsfiddle.net/FrancisMacDougall/mnrqokqL/
With this result:

Related

How create legend for bubble chart in d3? Legend not showing up

My aim is to add a legend to Clustered Bubble Chart based on the color of a cluster. The way that I did has no results.
In my CSV file, I created 5 clustered with different colors. In fact, I want to differentiate each cluster by name and color.
The code does not have any errors but nothing showing up. Can someone take look at it and tell what is wrong with it? Do you have any other suggestions to add a legend to the bubble chart?
<!DOCTYPE html>
<meta charset="utf-8">
<style type="text/css">
text {
font: 10px sans-serif;
}
circle {
stroke: #565352;
stroke-width: 1;
}
</style>
<body>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script>
var width = 1000,
height = 1000,
padding = 1.5, // separation between same-color nodes
clusterPadding = 6, // separation between different-color nodes
maxRadius = 65;
var color = d3.scale.ordinal()
.range(["#5499C7", "#8E44AD", "#138D75", "#F1C40F", "#D35400"]);
d3.text("word_groups.csv", function(error, text) {
var legendRectSize = 18;
var legendSpacing = 4;
if (error) throw error;
var colNames = "text,size,group\n" + text;
var data = d3.csv.parse(colNames);
data.forEach(function(d) {
d.size = +d.size;
});
//unique cluster/group id's
var cs = [];
data.forEach(function(d){
if(!cs.contains(d.group)) {
cs.push(d.group);
}
});
var n = data.length, // total number of nodes
m = cs.length; // number of distinct clusters
//create clusters and nodes
var clusters = new Array(m);
var nodes = [];
for (var i = 0; i<n; i++){
nodes.push(create_nodes(data,i));
}
var force = d3.layout.force()
.nodes(nodes)
.size([width, height])
.gravity(.02)
.charge(0)
.on("tick", tick)
.start();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var node = svg.selectAll("circle")
.data(nodes)
.enter().append("g").call(force.drag);
var legend = svg.selectAll('.legend')
.data(color.domain())
.enter()
.append('g')
.attr('class', 'legend')
.attr('transform', function(d, i) {
var height = legendRectSize + legendSpacing;
var offset = height * color.domain().length / 2;
var horz = -2 * legendRectSize;
var vert = i * height - offset;
return 'translate(' + horz + ',' + vert + ')';
});
legend.append('rect')
.attr('width', legendRectSize)
.attr('height', legendRectSize)
.style('fill', color)
.style('stroke', color);
legend.append('text')
.attr('x', legendRectSize + legendSpacing)
.attr('y', legendRectSize - legendSpacing)
.text(function(d) { return "Hans"; });
node.append("circle")
.style("fill", function (d) {
return color(d.cluster);
})
.attr("r", function(d){return d.radius})
node.append("text")
.attr("dy", ".3em")
.style("text-anchor", "middle")
.text(function(d) { return d.text.substring(0, d.radius / 3); });
function create_nodes(data,node_counter) {
var i = cs.indexOf(data[node_counter].group),
r = Math.sqrt((i + 1) / m * -Math.log(Math.random())) * maxRadius,
d = {
cluster: i,
radius: data[node_counter].size*1.5,
text: data[node_counter].text,
x: Math.cos(i / m * 2 * Math.PI) * 200 + width / 2 + Math.random(),
y: Math.sin(i / m * 2 * Math.PI) * 200 + height / 2 + Math.random()
};
if (!clusters[i] || (r > clusters[i].radius)) clusters[i] = d;
return d;
};
function tick(e) {
node.each(cluster(10 * e.alpha * e.alpha))
.each(collide(.5))
.attr("transform", function (d) {
var k = "translate(" + d.x + "," + d.y + ")";
return k;
})
}
// Move d to be adjacent to the cluster node.
function cluster(alpha) {
return function (d) {
var cluster = clusters[d.cluster];
if (cluster === d) return;
var x = d.x - cluster.x,
y = d.y - cluster.y,
l = Math.sqrt(x * x + y * y),
r = d.radius + cluster.radius;
if (l != r) {
l = (l - r) / l * alpha;
d.x -= x *= l;
d.y -= y *= l;
cluster.x += x;
cluster.y += y;
}
};
}
// Resolves collisions between d and all other circles.
function collide(alpha) {
var quadtree = d3.geom.quadtree(nodes);
return function (d) {
var r = d.radius + maxRadius + Math.max(padding, clusterPadding),
nx1 = d.x - r,
nx2 = d.x + r,
ny1 = d.y - r,
ny2 = d.y + r;
quadtree.visit(function (quad, x1, y1, x2, y2) {
if (quad.point && (quad.point !== d)) {
var x = d.x - quad.point.x,
y = d.y - quad.point.y,
l = Math.sqrt(x * x + y * y),
r = d.radius + quad.point.radius + (d.cluster === quad.point.cluster ? padding : clusterPadding);
if (l < r) {
l = (l - r) / l * alpha;
d.x -= x *= l;
d.y -= y *= l;
quad.point.x += x;
quad.point.y += y;
}
}
return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
});
};
}
});
Array.prototype.contains = function(v) {
for(var i = 0; i < this.length; i++) {
if(this[i] === v) return true;
}
return false;
};
</script>
The color.domain array is empty when you join it with the .legend selection, so no 'g' elements are appended.
The color.domain array is populated later in your code, when you append the circles to your nodes selection.
If you switch the order, then the legend items are created:
var node = svg
.selectAll('circle')
.data(nodes)
.enter()
.append('g')
.call(force.drag)
////MOVED BEFORE THE LEGEND CODE
node
.append('circle')
.style('fill', function (d) {
return color(d.cluster)
})
.attr('r', function (d) {
return d.radius
})
var legend = svg
.selectAll('.legend')
.data(color.domain())
.enter()
.append('g')
.attr('class', 'legend')
.attr('transform', function (d, i) {
var height = legendRectSize + legendSpacing
var offset = height * color.domain().length / 2
var horz = -2 * legendRectSize
var vert = i * height - offset
return 'translate(' + horz + ',' + vert + ')'
})
legend
.append('rect')
.attr('width', legendRectSize)
.attr('height', legendRectSize)
.style('fill', color)
.style('stroke', color)
legend
.append('text')
.attr('x', legendRectSize + legendSpacing)
.attr('y', legendRectSize - legendSpacing)
.text(function (d) {
return 'Hans'
})
PS: Some of the legend items are currently being translated off the SVG view, so your horz and vert variables need looking at.

Label placement in d3 radial bar chart

Here in my d3 radial chart, I am trying to get the label text just above the segment arcs instead of keeping outside the outer circle.
Fiddle
var width = 360,
height = 300,
barHeight = height / 2 - 40;
var formatNumber = d3.format("s");
var color = d3.scale.ordinal()
.range(["#F15D5D","#FAD64B"]);
var svg = d3.select('#chart').append("svg")
.attr("width", width)
.attr("height", height)
.attr('class','radial')
.append("g")
.attr("transform", "translate(" + width/2 + "," + height/2 + ")");
var data = [{
"name": "ABC",
"value":4
},
{
"name": "XYZ",
"value":5
},{
"name": "DEF",
"value":2
},
{
"name": "GHI",
"value":3
},{
"name": "JKL",
"value":1
}];
data.sort(function(a,b) { return b.value - a.value; });
var extent = [0, d3.max(data, d=>d.value)];
var barScale = d3.scale.linear()
.domain(extent)
.range([0, barHeight]);
var keys = data.map(function(d,i) { return d.name; });
var numBars = keys.length;
// X scale
var x = d3.scale.linear()
.domain(extent)
.range([0, -barHeight]);
// X axis
var xAxis = d3.svg.axis()
.scale(x).orient("left")
.ticks(3)
.tickFormat(formatNumber);
// Inner circles
var circles = svg.selectAll("circle")
.data(x.ticks(5))
.enter().append("circle")
.attr("r", function(d) {return barScale(d);})
.style("fill", "none")
//.style("stroke", "black")
//.style("stroke-dasharray", "2,2")
.style("stroke-width",".5px");
// Create arcs
var arc = d3.svg.arc()
.startAngle(function(d,i) {
var a = (i * 2 * Math.PI) / numBars;
var b = ((i + 1) * 2 * Math.PI) / numBars;
var d = (b-a) / 4;
var x = a+d;
var y = b-d;
return x;//(i * 2 * Math.PI) / numBars;
})
.endAngle(function(d,i) {
var a = (i * 2 * Math.PI) / numBars;
var b = ((i + 1) * 2 * Math.PI) / numBars;
var d = (b-a) / 4;
var x = a+d;
var y = b-d;
return y;//((i + 1) * 2 * Math.PI) / numBars;
})
.innerRadius(0);
// Render colored arcs
var segments = svg.selectAll("path")
.data(data)
.enter().append("path")
.each(function(d) { d.outerRadius = 0; })
.style("fill", function (d) { return color(d.name); })
.attr("d", arc);
// Animate segments
segments.transition().ease("elastic").duration(1000).delay(function(d,i) {return (25-i)*50;})
.attrTween("d", function(d,index) {
var i = d3.interpolate(d.outerRadius, barScale(+d.value));
return function(t) { d.outerRadius = i(t); return arc(d,index); };
});
// Outer circle
svg.append("circle")
.attr("r", barHeight)
.classed("outer", true)
.style("fill", "none")
//.style("stroke", "black")
.style("stroke-width",".5px");
// Apply x axis
svg.append("g")
.attr("class", "x axis")
.call(xAxis);
// Labels
var labelRadius = barHeight * 1.025;
var labels = svg.append("g")
.classed("labels", true);
labels.append("def")
.append("path")
.attr("id", "label-path")
.attr("d", "m0 " + -labelRadius + " a" + labelRadius + " " + labelRadius + " 0 1,1 -0.01 0");
labels.selectAll("text")
.data(keys)
.enter().append("text")
.style("text-anchor", "middle")
.style("font-weight","bold")
.style("fill", function(d, i) {return "#555";})
.append("textPath")
.attr("xlink:href", "#label-path")
.attr("startOffset", function(d, i) {return i * 100 / numBars + 50 / numBars + '%';})
.text(function(d) {return d.toUpperCase(); });
We can set the position of the <defs>'s paths using the same data you used to create the arcs.
First, let's create an enter selection:
var labels = svg.selectAll("foo")
.data(data)
.enter()
.append("g")
.classed("labels", true);
Then, we append the paths using the barScale for each value (hardcoded padding of 4px here):
labels.append("def")
.append("path")
.attr("id", (d, i) => "label-path" + i)
.attr("d", d => "m0 " + -(barScale(d.value) + 4) +
" a" + (barScale(d.value) + 4) + " " +
(barScale(d.value) + 4) + " 0 1,1 -0.01 0");
Please notice that we have to use unique IDs. Then, we change the IDs in the text-paths:
.attr("xlink:href", (d, i) => "#label-path" + i)
Here is your updated fiddle: https://jsfiddle.net/qt3e0rex/
And the same code in the Stack snippet:
var width = 360,
height = 300,
barHeight = height / 2 - 40;
var formatNumber = d3.format("s");
var color = d3.scale.ordinal()
.range(["#F15D5D", "#FAD64B"]);
var svg = d3.select('body').append("svg")
.attr("width", width)
.attr("height", height)
.attr('class', 'radial')
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var data = [{
"name": "ABC",
"value": 4
}, {
"name": "XYZ",
"value": 5
}, {
"name": "DEF",
"value": 2
}, {
"name": "GHI",
"value": 3
}, {
"name": "JKL",
"value": 1
}];
data.sort(function(a, b) {
return b.value - a.value;
});
var extent = [0, d3.max(data, d => d.value)];
var barScale = d3.scale.linear()
.domain(extent)
.range([0, barHeight]);
var keys = data.map(function(d, i) {
return d.name;
});
var numBars = keys.length;
// X scale
var x = d3.scale.linear()
.domain(extent)
.range([0, -barHeight]);
// X axis
var xAxis = d3.svg.axis()
.scale(x).orient("left")
.ticks(3)
.tickFormat(formatNumber);
// Inner circles
var circles = svg.selectAll("circle")
.data(x.ticks(5))
.enter().append("circle")
.attr("r", function(d) {
return barScale(d);
})
.style("fill", "none")
//.style("stroke", "black")
//.style("stroke-dasharray", "2,2")
.style("stroke-width", ".5px");
// Create arcs
var arc = d3.svg.arc()
.startAngle(function(d, i) {
var a = (i * 2 * Math.PI) / numBars;
var b = ((i + 1) * 2 * Math.PI) / numBars;
var d = (b - a) / 4;
var x = a + d;
var y = b - d;
return x; //(i * 2 * Math.PI) / numBars;
})
.endAngle(function(d, i) {
var a = (i * 2 * Math.PI) / numBars;
var b = ((i + 1) * 2 * Math.PI) / numBars;
var d = (b - a) / 4;
var x = a + d;
var y = b - d;
return y; //((i + 1) * 2 * Math.PI) / numBars;
})
.innerRadius(0);
// Render colored arcs
var segments = svg.selectAll("path")
.data(data)
.enter().append("path")
.each(function(d) {
d.outerRadius = 0;
})
.style("fill", function(d) {
return color(d.name);
})
.attr("d", arc);
// Animate segments
segments.transition().ease("elastic").duration(1000).delay(function(d, i) {
return (25 - i) * 50;
})
.attrTween("d", function(d, index) {
var i = d3.interpolate(d.outerRadius, barScale(+d.value));
return function(t) {
d.outerRadius = i(t);
return arc(d, index);
};
});
// Outer circle
svg.append("circle")
.attr("r", barHeight)
.classed("outer", true)
.style("fill", "none")
//.style("stroke", "black")
.style("stroke-width", ".5px");
// Apply x axis
svg.append("g")
.attr("class", "x axis")
.call(xAxis);
// Labels
var labelRadius = barHeight * 1.025;
var labels = svg.selectAll("foo")
.data(data)
.enter()
.append("g")
.classed("labels", true);
labels.append("def")
.append("path")
.attr("id", (d, i) => "label-path" + i)
.attr("d", d => "m0 " + -(barScale(d.value) + 4) + " a" + (barScale(d.value) + 4) + " " + (barScale(d.value) + 4) + " 0 1,1 -0.01 0");
labels.append("text")
.style("text-anchor", "middle")
.style("font-weight", "bold")
.style("fill", function(d, i) {
return "#555";
})
.append("textPath")
.attr("xlink:href", (d, i) => "#label-path" + i)
.attr("startOffset", function(d, i) {
return i * 100 / numBars + 50 / numBars + '%';
})
.text(function(d) {
return d.name.toUpperCase();
});
body {
background-color:#fff;
}
.axis path, .axis line {
fill: none;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
.tick text, line {
display:none;
}
circle {
stroke:#ccc;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

D3 merge colors to gradients

I have been trying to merge a set of paths in d3, So that one color blends into the other so it appears as though forms a gradient i tried to use to create a gradient but its always in a single direction which does not work out.
Fiddle here https://jsfiddle.net/roug3/jnpe5v3p/
var mapGroup = d3.select("svg");
function renderARC() {
var txData = {x: 200 , y : 200 , angle : 30};
var etxD = {etxSN : "TX500"};
if(d3.select(".arc"+etxD.etxSN).node()){
return;
}
var arcLevel = 5;
var arcSpan = 20;
var arcAngle = 2.0944;
var txAngle = txData.angle + 0;
var startAngle = txAngle - (arcAngle / 2);
var endAngle = txAngle + (arcAngle / 2);
var x = txData.x;
var y = txData.y;
var cwidth = 20;
var dataset = {};
for(var i = 1;i<= arcLevel;i++){
dataset[i] = [i];
}
var color = ["#ee4035","#f37736","#fdf498","#7bc043","#0392cf"]
// var color = ["#009933" , "#33cc33" ,"#ff3300" , "#ffcc66" ,"#ff6699" ,"#4dffff"];
var pie = d3.layout.pie()
.sort(null)
.startAngle(startAngle)
.endAngle(endAngle);
var arc = d3.svg.arc();
var gs = mapGroup.append("g").classed("arc"+etxD.etxSN , true).classed("arcSegment" , true);
console.log(gs);
var ggs = gs.selectAll("g").data(d3.values(dataset)).enter().append("g");
var arcP = ggs.selectAll("path").data(function (d) {
return pie(d);
})
.enter();
arcP.append("path").
attr("class" , function (d, i) {
return "arcID"+etxD.etxSN+i;
})
.attr("fill", function (d, i, j) {
// var cspan = Math.floor(Math.random() * arcLevel);
return color[ j ];
})
.attr("d", function (d, i, j) {
return arc.innerRadius(cwidth * j + arcSpan).outerRadius(cwidth * (j + 1) + arcSpan)(d);
}).
attr("transform" , "translate("+x+","+y+")");
}
renderARC();
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg width=500 height=500></svg>
Any Suggestions
Thanks
This is as close as I could get it : https://jsfiddle.net/thatoneguy/jnpe5v3p/2/
With the help of these :
http://jsfiddle.net/Qh9X5/1110/
http://www.w3schools.com/svg/svg_grad_radial.asp
Basically you have to create a radial blur using the dataset :
var grads = mapGroup.append("defs").selectAll("radialGradient").data(pie(d3.values(dataset)))
.enter().append("radialGradient")
.attr("gradientUnits", "userSpaceOnUse")
.attr("cx", 0)
.attr("cy", 0)
.attr("r", function(d, i) {
return cwidth * (i + 1) + arcSpan
})
.attr("id", function(d, i) {
return "grad" + i;
}).attr("transform", "translate(" + x + "," + y + ")");;
grads.append("stop")
.attr("offset", "80%")
.style("stop-color", function(d, i) {return color[i];});
grads.append("stop")
.attr("offset", "100%")
.style("stop-color", function(d, i) {
if (color[i + 1]) {
console.log(color[i + 1])
return color[i + 1];
} else {
return color[i];
}
})
Then select this to fill your paths :
arcP.append("path").
attr("class", function(d, i) {
return "arcID" + etxD.etxSN + i;
})
.attr("fill", function(d, i) {
console.log(count)
count++;
return "url(#grad" + count + ")";
})
.attr("d", function(d, i, j) {
return arc.innerRadius(cwidth * j + arcSpan).outerRadius(cwidth * (j + 1) + arcSpan)(d);
}).
attr("transform", "translate(" + x + "," + y + ")");
var mapGroup = d3.select("svg");
function renderARC() {
var txData = {
x: 200,
y: 200,
angle: 30
};
var etxD = {
etxSN: "TX500"
};
if (d3.select(".arc" + etxD.etxSN).node()) {
return;
}
var arcLevel = 5;
var arcSpan = 20;
var arcAngle = 2.0944;
var txAngle = txData.angle + 0;
var startAngle = txAngle - (arcAngle / 2);
var endAngle = txAngle + (arcAngle / 2);
var x = txData.x;
var y = txData.y;
var cwidth = 20;
var dataset = {};
for (var i = 1; i <= arcLevel; i++) {
dataset[i] = [i];
}
var color = ["#ee4035", "#f37736", "#fdf498", "#7bc043", "#0392cf"]
// var color = ["#009933" , "#33cc33" ,"#ff3300" , "#ffcc66" ,"#ff6699" ,"#4dffff"];
var pie = d3.layout.pie()
.sort(null)
.startAngle(startAngle)
.endAngle(endAngle);
var arc = d3.svg.arc();
var gs = mapGroup.append("g").classed("arc" + etxD.etxSN, true).classed("arcSegment", true);
console.log(gs);
var ggs = gs.selectAll("g").data(d3.values(dataset)).enter().append("g");
var arcP = ggs.selectAll("path").data(function(d) {
return pie(d);
})
.enter();
var grads = mapGroup.append("defs").selectAll("radialGradient").data(pie(d3.values(dataset)))
.enter().append("radialGradient")
.attr("gradientUnits", "userSpaceOnUse")
.attr("cx", 0)
.attr("cy", 0)
.attr("r", function(d, i) {
return cwidth * (i + 1) + arcSpan
})
.attr("id", function(d, i) {
return "grad" + i;
}).attr("transform", "translate(" + x + "," + y + ")");;
grads.append("stop")
.attr("offset", "80%")
.style("stop-color", function(d, i) {return color[i];});
grads.append("stop")
.attr("offset", "100%")
.style("stop-color", function(d, i) {
if (color[i + 1]) {
console.log(color[i + 1])
return color[i + 1];
} else {
return color[i];
}
})
var count = -1;
arcP.append("path").
attr("class", function(d, i) {
return "arcID" + etxD.etxSN + i;
})
.attr("fill", function(d, i) {
console.log(count)
count++;
return "url(#grad" + count + ")";
})
.attr("d", function(d, i, j) {
return arc.innerRadius(cwidth * j + arcSpan).outerRadius(cwidth * (j + 1) + arcSpan)(d);
}).
attr("transform", "translate(" + x + "," + y + ")");
}
renderARC();
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg width=500 height=500></svg>
It isn't perfect but will put you on the right track :) Hope this helps

Failed to generating two pie charts on the same page in d3.js

Failed Example
Woking Example
I'm switching for this pie chart for the animation effect. I wrap the code in a function and use $.each() to loop over the elements to generate two charts on the same page, but unlike the working example, which is my original code for generating pie charts , I can't get it to work. Can anyone figure out what the problem is?
var colors = ["#DFC267","#90C0E2","#DF5A6E","#FFA854","#749D79","#BFE5E2","#d3d3d3"];
function pie(dataset,el){
console.log(dataset);
var data = [];
var color = d3.scale.ordinal().range(colors);
r = 115,
labelr = r + 30,
pi = 2 * Math.PI,
svg = d3.select(el).append('svg').
attr('width', 350).
attr('height', 350),
group = svg.append('g').
attr('transform', 'translate(155, 170)')
,
arc = d3.svg.arc().
innerRadius(r - 50).
outerRadius(r)
,
pie = d3.layout.pie()
.value(function(d) { return d.result; }),
format = d3.format('.3r'),
arcs = group.selectAll('.arc').
data(pie(d3.values(dataset))).
enter().
append('g').
attr('class', 'arc')
;
arcs.append('path').
transition().
delay(function(d, i) { return i * 500; }).
duration(750).
attrTween('d', function(d) {
var i = d3.interpolate(d.startAngle + 0, d.endAngle);
return function(t) {
d.endAngle = i(t);
return arc(d);
};
}).
style('fill', function(d, i) { return color(i); }).
style('stroke', '#fff').
style('stroke-width', '2px')
;
arcs.append('text').
attr('transform', function(d) {
var c = arc.centroid(d),
x = c[0],
y = c[1],
h = Math.sqrt(x*x + y*y);
return "translate(" + (x/h * labelr) + ',' +
(y/h * labelr) + ")";
}).
attr('text-anchor', 'middle').
attr('font-size', '1em').
attr('fill', '#222').
text(function (d) {
var total = d3.sum(dataset.map(function(d) {
return d.result;
}));
var percent = Math.round(1000 * d.value / total) / 10;
return percent + ' %';
});
var tooltip = d3.select(el).append('div').attr('class', 'tooltip');
arcs.on('mousemove', function(d) { console.log(d3.event);
tooltip.style("top", d3.event.y - r+ "px").style("left", d3.event.x + "px")
});
arcs.on('mouseover', function(d) {
var total = d3.sum(dataset.map(function(d) {
return d.result;
}));
tooltip.style('display', 'block')
.style("opacity", 1)
.append('div')
.attr('class', 'label')
.append('div')
.attr('class', 'count')
tooltip.select('.label').html(d.data.item);
tooltip.select('.count').html(d.data.result);
});
arcs.on('mouseout', function() {
tooltip.style('display', 'none');
});
}
$('.j_chart').each(function(k,i){
var dataset = $(this).find('.j_data').text();
console.log(dataset);
pie(JSON.parse(dataset),'#j_'+k);
})
This is the working code:
var colors = ["#DFC267","#90C0E2","#DF5A6E","#FFA854","#749D79","#BFE5E2","#d3d3d3"];
function pie(dataset,el){ console.log(dataset)
'use strict';
var width = 280;
var height = 280;
var radius = Math.min(width, height) / 2;
var color = d3.scale.ordinal().range(colors);
var svg = d3.select(el)
.append('svg')
.attr('width', width)
.attr('height', height)
.append('g')
.attr('transform', 'translate(' + (width / 2) +
',' + (height / 2) + ')');
var arc = d3.svg.arc()
.outerRadius(radius);
var pie = d3.layout.pie()
.value(function(d) { return d.result; })
.sort(null);
var path = svg.selectAll('path')
.data(pie(dataset))
.enter()
.append('path')
.attr('d', arc)
.attr('fill', function(d, i) {
return color(d.data.item);
});
var tooltip = d3.select(el).append('div').attr('class', 'tooltip');
path.on('mousemove', function(d) { console.log(d3.event);
tooltip.style("top", d3.event.y - radius + "px").style("left", d3.event.x + "px")
});
path.on('mouseover', function(d) {
var total = d3.sum(dataset.map(function(d) {
return d.result;
}));
var percent = Math.round(1000 * d.data.result / total) / 10;
tooltip
.style('display', 'block')
.style("opacity", 1)
.append('div')
.attr('class', 'label')
.append('div')
.attr('class', 'count')
.append('div')
.attr('class', 'percent');
tooltip.select('.label').html(d.data.item);
tooltip.select('.count').html(d.data.result);
tooltip.select('.percent').html(percent + '%');
});
path.on('mouseout', function() {
tooltip.style('display', 'none');
});
}
the problem is that you have a function called "pie" and inside that function, you do
pie = d3.layout.pie()
thus, overwriting the function definition and the final result is that it is called only for the first item. try renaming the function to something else, like pie_func. check this fiddle with the correction: http://jsfiddle.net/cesarpachon/a4r2q4pw/

Transition not working

My goal is that given a value in seconds(resp_time), I want to create a counter in anticlock direction that would end once resp_time becomes 0.
I am following this tutorial: http://bl.ocks.org/mbostock/1096355 to create a polar clock. But I need the arc to decrease as in go anti-clockwise. I tried updating the endAngle value for the same, but it doesn't seem to work.
Here's my code:
var width = 960,
height = 800,
radius = Math.min(width, height) / 1.9,
spacing = .09;
var resp_time = 61;
var rh = parseInt(resp_time/3600), rm = parseInt((resp_time- rh*3600)/60), rs = parseInt((resp_time- rh*3600 - rm*60)%60);
var color = d3.scale.linear()
.range(["hsl(-180,50%,50%)", "hsl(180,50%,50%)"])
.interpolate(interpolateHsl);
var t;
var arc = d3.svg.arc()
.startAngle(0)
.endAngle(function(d) { return d.value * 2 * Math.PI; })
.innerRadius(function(d) { return d.index * radius; })
.outerRadius(function(d) { return (d.index + spacing) * radius; });
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var field = svg.selectAll("g")
.data(fields)
.enter().append("g");
field.append("path");
field.append("text");
d3.transition().duration(0).each(tick);
d3.select(self.frameElement).style("height", height + "px");
function tick() {
field = field
.each(function(d) { this._value = d.value; })
.data(fields)
.each(function(d) { d.previousValue = this._value; });
field.select("path")
.transition()
.ease("elastic")
.attrTween("d", arcTween)
.style("fill", function(d) { return color(d.value); });
field.select("text")
.attr("dy", function(d) { return d.value < .5 ? "-.5em" : "1em"; })
.text(function(d) { return d.text; })
.transition()
.ease("elastic")
.attr("transform", function(d) {
return "rotate(" + 360 * d.value + ")"
+ "translate(0," + -(d.index + spacing / 2) * radius + ")"
+ "rotate(" + (d.value < .5 ? -90 : 90) + ")"
});
if (resp_time > 0)
{
resp_time = resp_time - 1;
rh = parseInt(resp_time/3600), rm = parseInt((resp_time- rh*3600)/60), rs = parseInt((resp_time- rh*3600 - rm*60)%60);
t = setTimeout(tick, 1000);
}
}
function arcTween(d) {
console.log(d);
var i = d3.interpolateNumber(d.previousValue, d.value);
return function(t) { d.value = i(t); return arc(d); };
}
function fields() {
console.log(rs);
return [
{index: .3, text: rs+"s", value: rs},
{index: .2, text: rm+"m", value: rm},
{index: .1, text: rh+"h", value: rh}
];
}
function interpolateHsl(a, b) {
var i = d3.interpolateString(a, b);
return function(t) {
return d3.hsl(i(t));
};
}
This is just resulting in 3 static concentric circles(since I'm plotting only the minute, seconds and hours) with no transition.
I'm not sure how to proceed from here. What change should I make to get it working? Please help me out.
d.value in this code should be in the range [0, 1], so you need to divide by the maximum values in the fields generator:
function fields() {
console.log(rs);
return [
{index: .3, text: rs+"s", value: rs/60},
{index: .2, text: rm+"m", value: rm/60},
{index: .1, text: rh+"h", value: rh/24}
];
}
I noticed this because console.log(rs) was printing out values in the range of [0, 60] whereas arc.endAngle expects a radian value. If you substitute [0, 60] with the endAngle provider in the code, you get an over wound clock hand.
var arc = d3.svg.arc()
.startAngle(0)
.endAngle(function(d) { return d.value * 2 * Math.PI; })
var width = 960,
height = 800,
radius = Math.min(width, height) / 1.9,
spacing = .09;
var resp_time = 61;
var rh = parseInt(resp_time/3600), rm = parseInt((resp_time- rh*3600)/60), rs = parseInt((resp_time- rh*3600 - rm*60)%60);
var color = d3.scale.linear()
.range(["hsl(-180,50%,50%)", "hsl(180,50%,50%)"])
.interpolate(interpolateHsl);
var t;
var arc = d3.svg.arc()
.startAngle(0)
.endAngle(function(d) { return d.value * 2 * Math.PI; })
.innerRadius(function(d) { return d.index * radius; })
.outerRadius(function(d) { return (d.index + spacing) * radius; });
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var field = svg.selectAll("g")
.data(fields)
.enter().append("g");
field.append("path");
field.append("text");
d3.transition().duration(0).each(tick);
d3.select(self.frameElement).style("height", height + "px");
function tick() {
field = field
.each(function(d) { this._value = d.value; })
.data(fields)
.each(function(d) { d.previousValue = this._value; });
field.select("path")
.transition()
.ease("elastic")
.attrTween("d", arcTween)
.style("fill", function(d) { return color(d.value); });
field.select("text")
.attr("dy", function(d) { return d.value < .5 ? "-.5em" : "1em"; })
.text(function(d) { return d.text; })
.transition()
.ease("elastic")
.attr("transform", function(d) {
return "rotate(" + 360 * d.value + ")"
+ "translate(0," + -(d.index + spacing / 2) * radius + ")"
+ "rotate(" + (d.value < .5 ? -90 : 90) + ")"
});
if (resp_time > 0)
{
resp_time = resp_time - 1;
rh = parseInt(resp_time/3600), rm = parseInt((resp_time- rh*3600)/60), rs = parseInt((resp_time- rh*3600 - rm*60)%60);
t = setTimeout(tick, 1000);
}
}
function arcTween(d) {
console.log(d);
var i = d3.interpolateNumber(d.previousValue, d.value);
return function(t) { d.value = i(t); return arc(d); };
}
function fields() {
console.log(rs);
return [
{index: .3, text: rs+"s", value: rs/60},
{index: .2, text: rm+"m", value: rm/60},
{index: .1, text: rh+"h", value: rh/24}
];
}
function interpolateHsl(a, b) {
var i = d3.interpolateString(a, b);
return function(t) {
return d3.hsl(i(t));
};
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

Categories

Resources