Related
I have been trying to create a stacked segment chart for the past few days and between all the trigonometry and key stacking it has truly been a nightmare for me to apply what seems to be the most basic of tweaks. Currently my segment chart has data that is sorted in descending order based on Active equity. I want to change this to have the data sorted by total.
const csvData = `State,Active equity,Passive equity,Fixed income,Mixed assets
BlackRock,1,17,0,0
Fidelity,13,2,0,0
SSgA,12,0,0,0
Hang Seng,11,0,0,0
UBS,9,0,0,1
Schroders,6,0,2,1
JP Morgan,5,2,0,1
Value Partners,1,0,6,0
First State,5,0,0,0
Invesco,4,1,0,0
HSBC,1,1,1,1
DBS,0,2,1,0
BOCI,1,1,1,0
CSOP,0,2,1,0
Principal,1,1,0,0
Allianz,2,1,0,0
Yuanta,0,2,1,0
Manulife,1,0,1,0
Aberdeen,2,0,0,0
Mirae,1,1,0,0
,0,0,0,0`;
//const data = d3.csvParse(csvData, d3.autoType);
var margins = {top:20, bottom:300, left:30, right:100};
var height = 600;
var width = 900;
var totalWidth = width+margins.left+margins.right;
var totalHeight = height+margins.top+margins.bottom;
var outerRadius = (400 / 2);
var innerRadius = 15;
var svg = d3.select('body')
.append('svg')
.attr('width', totalWidth)
.attr('height', totalHeight);
var graphGroup = svg.append('g')
.attr('transform', "translate(250,250)");
var x = d3.scaleBand()
.range([0, 2 * Math.PI])
.align(0);
var y = d3.scaleRadial()
.range([innerRadius, outerRadius]);
var z = d3.scaleOrdinal()
.range(["#003366", "#4f81b9", "#95b3d7", "#f6d18b"]);
d3.csv("csvData", function(d, i, columns) {
for (i = 1, t = 0; i < columns.length; ++i) t += d[columns[i]] = +d[columns[i]];
d.total = t;
return d;
}, function(error, data) {
if (error) throw error;
weave(data, function(a, b) { return b[data.columns[6]] - a[data.columns[6]]; });
x.domain(data.map(function(d) { return d.State; }));
y.domain([0, d3.max(data, function(d) { return d.total; })*1.3]);
z.domain(data.columns.slice(1));
graphGroup.append('g')
.selectAll('g')
.data(d3.stack().keys(data.columns.slice(1))(data))
.enter().append("g")
.selectAll(".bg-arc2")
.data(function(d) { return d; })
.enter().append("path")
.attr("d", d3.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius+2)
.startAngle(function(d) { return x(d.data.State); })
.endAngle(function(d) { return x(d.data.State) + x.bandwidth()*.90; })
.padAngle(0.1)
.padRadius(innerRadius))
.attr('class','bg-arc2')
.attr('fill','none')
.attr('stroke-width','4px')
.attr('stroke','#003366');
graphGroup.append('circle')
.attr('cx',0)
.attr('cy',0)
.attr('r',200)
.style('fill','#d9d9d9');
graphGroup.append('g')
.selectAll('g')
.data(d3.stack().keys(data.columns.slice(1))(data))
.enter().append("g")
.selectAll(".bg-arc")
.data(function(d) { return d; })
.enter().append("path")
.attr("d", d3.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius)
.startAngle(function(d) { return x(d.data.State); })
.endAngle(function(d) { return x(d.data.State) + x.bandwidth()*.95; })
.padAngle(0.1)
.padRadius(innerRadius))
.attr('class','bg-arc')
.attr('fill','#fff');
graphGroup.append('circle')
.attr('cx',0)
.attr('cy',0)
.attr('r',innerRadius)
.style('fill','#fff');
var stackedData = d3.stack().keys(data.columns.slice(1))(data);
var stackedData2 = stackedData.sort(function(a,b) { return d3.descending(a[0].data.total, b[0].data.total)});
console.log(stackedData[0][0].data.total)
console.log(stackedData2);
graphGroup.append("g")
.selectAll("g")
.data(stackedData)
.enter().append("g")
.attr("fill", function(d) { return z(d.key); })
.selectAll("path")
.data(function(d) { return d; })
.enter().append("path")
.attr("d", d3.arc()
.innerRadius(function(d) { return y(d[0]); })
.outerRadius(function(d) { return y(d[1]); })
.startAngle(function(d) { return x(d.data.State); })
.endAngle(function(d) { return x(d.data.State) + x.bandwidth()*.95; })
.padAngle(0.04)
.padRadius(innerRadius));
var label = graphGroup.append("g")
.selectAll("g")
.data(data)
.enter().append("g")
.attr("text-anchor", "middle")
.attr("transform", function(d) { return "rotate(" + ((x(d.State) + x.bandwidth() / 2) * 180 / Math.PI - 90) + ")translate(" + (outerRadius+25) + ",0)"; });
label.append("text")
.attr("transform", function(d) { return (x(d.State) + x.bandwidth() / 2 + Math.PI / 2) % (2 * Math.PI) < Math.PI ? "rotate(90)translate(0,16)" : "rotate(-90)translate(0,-9)"; })
.text(function(d) { return d.State; });
var yAxis = graphGroup.append("g")
.attr("text-anchor", "end");
var yTick = yAxis
.selectAll("g")
.data(y.ticks(10).slice(1))
.enter().append("g");
});
function weave(array, compare) {
var i = -1, j, n = array.sort(compare).length, weave = new Array(n);
while (++i < n) weave[i] = array[(j = i << 1) >= n ? (n - i << 1) - 1 : j];
while (--n >= 0) array[n] = weave[n];
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://gist.githubusercontent.com/mbostock/3686329aa6e1f5938df8eef12ec353fe/raw/1ab722df937c3ac86cac8292e34cfc7279b017f8/d3-scale-radial.js"></script>
Relevant code here:
var stackedData = d3.stack().keys(data.columns.slice(1))(data);
var stackedData2 = stackedData.sort(function(a,b) { return d3.descending(a[0].data.total, b[0].data.total)});
console.log(stackedData[0][0].data.total)
console.log(stackedData2);
I checked via the console log to make sure I was slicing at the correct place, which I was, as confirmed by console.log(stackedData[0][0].data.total) which returned the correct value of 18.
However I can't apply the sort as desired. The data is still sorted by Active equity and not total.
Question
The default sort for stacked radial charts seems to be whatever the first variable is. In my case, it's Active equity. With that in mind, based off my progress above, what is keeping me from applying the descending order sort by data.total as opposed to the default?
Your question is not clear: are you talking about sorting the segments within each slice, or are you talking about sorting the slices themselves?
For the first case, there is an method named order, that you can use with the stack generator (the link goes to v5 docs, which are a bit different from v4). However, because you said "I want to change [the order] to have the data sorted by total", it seems to me that you're talking about sorting the slices. If that is correct, two observations: it's not sorted by Active equity, right now it's just the order of the objects in the original data array.
For sorting by total you just need to change that array:
data.sort(function(a, b) {
return b.total - a.total;
});
Also, get rid of the weave function.
Here is the result:
(function(global, factory) {
typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("d3-scale")) :
typeof define === "function" && define.amd ? define(["exports", "d3-scale"], factory) :
(factory(global.d3 = global.d3 || {}, global.d3));
}(this, function(exports, d3Scale) {
'use strict';
function square(x) {
return x * x;
}
function radial() {
var linear = d3Scale.scaleLinear();
function scale(x) {
return Math.sqrt(linear(x));
}
scale.domain = function(_) {
return arguments.length ? (linear.domain(_), scale) : linear.domain();
};
scale.nice = function(count) {
return (linear.nice(count), scale);
};
scale.range = function(_) {
return arguments.length ? (linear.range(_.map(square)), scale) : linear.range().map(Math.sqrt);
};
scale.ticks = linear.ticks;
scale.tickFormat = linear.tickFormat;
return scale;
}
exports.scaleRadial = radial;
Object.defineProperty(exports, '__esModule', {
value: true
});
}));
const csvData = `State,Active equity,Passive equity,Fixed income,Mixed assets
BlackRock,1,17,0,0
Fidelity,13,2,0,0
SSgA,12,0,0,0
Hang Seng,11,0,0,0
UBS,9,0,0,1
Schroders,6,0,2,1
JP Morgan,5,2,0,1
Value Partners,1,0,6,0
First State,5,0,0,0
Invesco,4,1,0,0
HSBC,1,1,1,1
DBS,0,2,1,0
BOCI,1,1,1,0
CSOP,0,2,1,0
Principal,1,1,0,0
Allianz,2,1,0,0
Yuanta,0,2,1,0
Manulife,1,0,1,0
Aberdeen,2,0,0,0
Mirae,1,1,0,0
,0,0,0,0`;
const data = d3.csvParse(csvData, function(d, i, columns) {
for (i = 1, t = 0; i < columns.length; ++i) t += d[columns[i]] = +d[columns[i]];
d.total = t;
return d;
});
data.sort(function(a, b) {
return b.total - a.total;
});
var margins = {
top: 20,
bottom: 300,
left: 30,
right: 100
};
var height = 600;
var width = 900;
var totalWidth = width + margins.left + margins.right;
var totalHeight = height + margins.top + margins.bottom;
var outerRadius = (400 / 2);
var innerRadius = 15;
var svg = d3.select('body')
.append('svg')
.attr('width', totalWidth)
.attr('height', totalHeight);
var graphGroup = svg.append('g')
.attr('transform', "translate(250,250)");
var x = d3.scaleBand()
.range([0, 2 * Math.PI])
.align(0);
var y = d3.scaleRadial()
.range([innerRadius, outerRadius]);
var z = d3.scaleOrdinal()
.range(["#003366", "#4f81b9", "#95b3d7", "#f6d18b"]);
x.domain(data.map(function(d) {
return d.State;
}));
y.domain([0, d3.max(data, function(d) {
return d.total;
}) * 1.3]);
z.domain(data.columns.slice(1));
graphGroup.append('g')
.selectAll('g')
.data(d3.stack().keys(data.columns.slice(1))(data))
.enter().append("g")
.selectAll(".bg-arc2")
.data(function(d) {
return d;
})
.enter().append("path")
.attr("d", d3.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius + 2)
.startAngle(function(d) {
return x(d.data.State);
})
.endAngle(function(d) {
return x(d.data.State) + x.bandwidth() * .90;
})
.padAngle(0.1)
.padRadius(innerRadius))
.attr('class', 'bg-arc2')
.attr('fill', 'none')
.attr('stroke-width', '4px')
.attr('stroke', '#003366');
graphGroup.append('circle')
.attr('cx', 0)
.attr('cy', 0)
.attr('r', 200)
.style('fill', '#d9d9d9');
graphGroup.append('g')
.selectAll('g')
.data(d3.stack().keys(data.columns.slice(1))(data))
.enter().append("g")
.selectAll(".bg-arc")
.data(function(d) {
return d;
})
.enter().append("path")
.attr("d", d3.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius)
.startAngle(function(d) {
return x(d.data.State);
})
.endAngle(function(d) {
return x(d.data.State) + x.bandwidth() * .95;
})
.padAngle(0.1)
.padRadius(innerRadius))
.attr('class', 'bg-arc')
.attr('fill', '#fff');
graphGroup.append('circle')
.attr('cx', 0)
.attr('cy', 0)
.attr('r', innerRadius)
.style('fill', '#fff');
var stackedData = d3.stack()
.keys(data.columns.slice(1))
(data);
graphGroup.append("g")
.selectAll("g")
.data(stackedData)
.enter().append("g")
.attr("fill", function(d) {
return z(d.key);
})
.selectAll("path")
.data(function(d) {
return d;
})
.enter().append("path")
.attr("d", d3.arc()
.innerRadius(function(d) {
return y(d[0]);
})
.outerRadius(function(d) {
return y(d[1]);
})
.startAngle(function(d) {
return x(d.data.State);
})
.endAngle(function(d) {
return x(d.data.State) + x.bandwidth() * .95;
})
.padAngle(0.04)
.padRadius(innerRadius));
var label = graphGroup.append("g")
.selectAll("g")
.data(data)
.enter().append("g")
.attr("text-anchor", "middle")
.attr("transform", function(d) {
return "rotate(" + ((x(d.State) + x.bandwidth() / 2) * 180 / Math.PI - 90) + ")translate(" + (outerRadius + 25) + ",0)";
});
label.append("text")
.attr("transform", function(d) {
return (x(d.State) + x.bandwidth() / 2 + Math.PI / 2) % (2 * Math.PI) < Math.PI ? "rotate(90)translate(0,16)" : "rotate(-90)translate(0,-9)";
})
.text(function(d) {
return d.State;
});
var yAxis = graphGroup.append("g")
.attr("text-anchor", "end");
var yTick = yAxis
.selectAll("g")
.data(y.ticks(10).slice(1))
.enter().append("g");
function weave(array, compare) {
var i = -1,
j, n = array.sort(compare).length,
weave = new Array(n);
while (++i < n) weave[i] = array[(j = i << 1) >= n ? (n - i << 1) - 1 : j];
while (--n >= 0) array[n] = weave[n];
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
I've mixed and matched a lot of code from other sources.
Currently, the pie chart displays fine, but when I select a different server in the drop-down menu, it fails to update the picture. The console logs show that dataset is indeed being changed. I checked online and it seems like my elements are not being removed correctly? However, whenever I try to use path.exit().remove() it fails to display anything.
Here is my code and jsfiddle: http://jsfiddle.net/36q95k1c/2/
var ds1 = [1,1,1,1,1,1,1,3,0,0];
var ds2 = [0,0,0,1,1,1,1,1,1,1];
var ds3 = [0,0,1,1,0,0,0,0,0,0];
var ds4 = [0,0,2,0,5,3,0,0,0,0];
var ds5 = [0,0,0,0,0,0,0,0,0,0];
var ds6 = [0,0,0,0,0,0,0,0,0,0];
var ds7 = [0,0,0,0,0,0,0,0,0,0];
var ds8 = [0,0,0,0,0,0,0,0,0,0];
var ds9 = [0,0,0,0,0,0,0,0,0,0];
var ds10 = [0,0,0,0,0,0,0,0,0,0];
var ds11 = [0,0,0,0,0,0,0,0,0,0];
var ds12 = [0,0,0,0,0,0,0,0,0,0];
var ds13 = [0,0,0,0,0,0,0,0,0,0];
var ds14 = [0,0,0,0,0,0,0,0,0,0];
var dataset = {inner: [4,1,31,28,13,65,6,6,4,3],
middle: ds1,
outer: [1175,1802,8126,11926,37264,4267,2961,2909,850,12432]};
var victim_total = 4+1+31+28+13+65+6+6+4+3;
var killer_total = 22+4+37+72+2+20+2+11+3+3;
var general_total = 1175+1802+8126+11926+37264+4267+2961+2909+850+12432;
var legendRectSize = 25;
var legendSpacing = 6;
//Width and height
var w = 750;
var h = 750;
var r = 100;
var donutWidth = 225
var outerRadius = w / 2;
var innerRadius = donutWidth;
var arc = d3.svg.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
var pie = d3.layout.pie()
.sort(null);
// Easy colors accessible via a 10-step ordinal scale
var color = d3.scale.category20();
// Create SVG element
var svg = d3.select("body")
.append("svg")
.attr("width", 2*w)
.attr("height", h)
.append('g')
.attr('transform', 'translate(' + (w / 2) +
',' + (h / 2) + ')');
// Define the div for the tooltip
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
function updateLegend(newData) {
//https://dl.dropboxusercontent.com/s/hfho50s0xd2dcpn/craftinggeneralstats1.csv?dl=1"
//https://dl.dropboxusercontent.com/s/i152v8ccetr5gj0/craftinggeneralstats.csv?dl=1
//Import CSV file
d3.csv("https://dl.dropboxusercontent.com/s/i152v8ccetr5gj0/craftinggeneralstats.csv?dl=1", function(data) {
data.forEach(function(d) {
d.outer = +d.count;
d.middle = +d.countkiller;
d.inner = +d.countvictim;
d.label = d.label;
});
// function updateLegend(newData) {
/*var step;
for (step = 0; step < 10; step++) {
// Runs 5 times, with values of step 0 through 4.
data[step].countkiller = 1;
}*/
//data[i].countkiller = 0;
console.log(dataset.middle);
// Set up groups
var arcs = svg.selectAll("g.arc")
.data(d3.values(dataset));
arcs.enter()
.append("g")
.attr("class", "arc")
.attr("transform", "translate(" +0+ "," +0+ ")");
var path = arcs.selectAll("path")
.data(function(d) { return pie(d); });
path.enter().append("path")
.on("mouseover", function(d, i, j) {
// console.log(d);
//console.log(dataset.middle[3]);
div.transition()
.duration(200)
.style("opacity", .9)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
if (j ==0)
div.html("Victim" + "<br/>" + Math.round(1000 * d.value / victim_total) / 10 +"%")
if (j ==1)
div.html("Killer" + "<br/>" + Math.round(1000 * d.value / killer_total) / 10 +"%")
if (j ==2)
{
div.html("Overall" + "<br/>" + Math.round(1000 * d.value / general_total) / 10 +"%")
}
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
})
.attr("fill", function(d, i) { return color(i); })
.attr("d", function(d, i, j) { return arc.innerRadius(10+r*j).outerRadius(r*(j+1))(d); });
// .attr("text-anchor", "middle")
/* .text(function (d, i) {
return data[i].label;
});*/
// Setup legend
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 +(w/2)) + ',' + vert + ')';
});
legend.append('rect')
.attr('width', legendRectSize)
.attr('height', legendRectSize)
.style('fill', color)
.style('stroke', color)
.on('click', function(label) {
var rect = d3.select(this);
var enabled = true;
if (rect.attr('class') === 'disabled') {
rect.attr('class', '');
} else {
if (totalEnabled < 2) return;
rect.attr('class', 'disabled');
enabled = false;
}
pie.value(function(d) {
if (d.label === label) d.enabled = enabled;
return (d.enabled) ? d.count : 0;
});
arcs = arcs.data(d3.values(dataset))
});
legend.append('text')
.attr('x', legendRectSize + legendSpacing)
.attr('y', legendRectSize - legendSpacing)
.text(function (d, i) {
return data[i].label;
});
path.exit().remove();
d3.select('#opts')
.on('change', function(d) {
var server = eval(d3.select(this).property('value'));
dataset.middle = server;
updateLegend(dataset);
});
});
}
updateLegend(dataset);
In the current version, you just add declarations for the enter selection, which affects only arcs that are inserted. You need to guide D3 how to treat the update selection, which affects arcs that are updated and the exit selection, which affects arcs that are removed. You can add these lines of code before the path.enter() statement.
path.attr("d", function(d, i, j) {
return arc.innerRadius(10 + r * j).outerRadius(r * (j + 1))(d);
});
path.exit().remove();
The updated fiddle: http://jsfiddle.net/36q95k1c/5/
To add to Hieu Le's nice answer, for your query about animations, it can be a bit tricky to figure out the right point at which to add the transition() call. Try putting it on line 100 of Hieu Le's jsfiddle to see if it's what you're after:
path.transition().attr("d", function(d, i, j) {
return arc.innerRadius(10 + r * j).outerRadius(r * (j + 1))(d);
});
Once you've got it animating, check out the docs for information on different interpolations and tweens.
Cheers!
I am begginer to d3.js and getting errors, I customized the example for my needs but gettings these in the console. Please help. Thank you in Advance.
Error: Invalid value for <path> attribute d="M-1.8369701987210297e-14,-100A100,100 0 1,1 NaN,NaNLNaN,NaNA60,60 0 1,0 -1.1021821192326178e-14,-60Z"a # d3.v3.min.js:1
d3.v3.min.js:1 Error: Invalid value for <path> attribute d="MNaN,NaNA100,100 0 1,1 NaN,NaNLNaN,NaNA60,60 0 1,0 NaN,NaNZ"
var getData = function () {
var size = 3;
var data =[];
var text = "";
data=[
{
label:"Saved",
value:200,
},{
label:"Ordered",
value:1255,
},{
label:"Shipped",
value:760,
},{
label:"Backordered",
value:150,
},
{
label:"Cancelled",
value:250,
},
];
d3.select("#data").html(text);
return data;
};
console.log(getData());
var chart = donut(250,200,16,40)
.$el(d3.select("#chart"))
.data(getData())
.render();
d3.select("#refresh").on("click", function () {
chart.data(getData()).render();
});
function donut(width,height,label_font_size,value_font_size) {
// Default settings
var $el = d3.select("body")
var data = {};
// var showTitle = true;
var width = width,
height = height,
radius = Math.min(width, height) / 2;
//var arc = d3.svg.arc().outerRadius(radius);
var arcOver = d3.svg.arc()
.innerRadius(radius + 20);
var currentVal;
var color = d3.scale.category20();
var pie = d3.layout.pie()
.sort(null)
.value(function (d) {
console.log(d);
return d.value.value;
});
var svg, g, arc;
var object = {};
// Method for render/refresh graph
object.render = function () {
if (!svg) {
arc = d3.svg.arc()
.outerRadius(radius)
.innerRadius(radius - (radius / 2.5));
svg = $el.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
g = svg.selectAll(".arc")
.data(pie(d3.entries(data)))
.enter().append("g")
.attr("class", "arc");
g.append("path")
// Attach current value to g so that we can use it for animation
.each(function (d) {
//this._current = d;
})
.attr("d", arc)
.style("fill", function (d,i) {
return color(i);
});
/*g.append("text")
.attr("transform", function (d,i) {
return "translate(" + arc.centroid(d,i) + ")";
})
.attr("dy", ".35em")
.style("text-anchor", "middle");
g.select("text").text(function (d) {
//return d.data.key;
});
*/
svg.append("text")
.datum(data)
.attr("x", 0)
.attr("y", 0)
.attr("class", "text-tooltip")
.style("text-anchor", "middle")
.attr("font-weight", "bold")
.style("font-size", radius / 2.5 + "px");
g.on("mouseover", function (obj) {
var tooltipText=svg.select("text.text-tooltip");
tooltipText.attr("fill", function (d,i) {
return color(i);
});
tooltipText.append("tspan")
.attr("dy", -15)
.attr("x",0)
.attr("class", "text-tooltip-label")
.style("font-size", label_font_size + "px")
.text(function(d) {return d[obj.data.key].label;});
tooltipText.append("tspan")
.attr("dy", ".9em") // offest by 1.2 em
.attr("x",0)
.attr("class", "text-tooltip-value")
.style("font-size", value_font_size + "px")
.text(function(d) {return d[obj.data.key].value;});
d3.select(this)
.attr("stroke","white")
.transition()
.duration(1000)
.attr("d", arcOver)
.attr("stroke-width",6);
});
g.on("mouseout", function (obj) {
svg.select("text.text-tooltip").text("");
d3.select(this).transition()
.attr("d", arc)
.attr("stroke","none");
});
} else {
g.data(pie(d3.entries(data))).exit().remove();
g.select("path")
.transition().duration(200)
.attrTween("d", function (a) {
var i = d3.interpolate(this._current, a);
this._current = i(0);
return function (t) {
return arc(i(t));
};
});
g.select("text")
.attr("transform", function (d,i) {
return "translate(" + arc.centroid(d,i) + ")";
});
svg.select("text.text-tooltip").datum(data);
}
return object;
};
// Getter and setter methods
object.data = function (value) {
if (!arguments.length) return data;
data = value;
return object;
};
object.$el = function (value) {
if (!arguments.length) return $el;
$el = value;
return object;
};
object.width = function (value) {
if (!arguments.length) return width;
width = value;
radius = Math.min(width, height) / 2;
return object;
};
object.height = function (value) {
if (!arguments.length) return height;
height = value;
radius = Math.min(width, height) / 2;
return object;
};
return object;
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="chart"></div>
I have a code for multiple charts created on the same page using .each function with the first svg.
var svg = svg1.enter().append("svg:svg")
svg.attr("class", "r_chart")
.attr("width", left_width + width)
.attr("height", height)
.attr('viewBox', '0 0 350 310')
.attr('perserveAspectRatio', 'xMinYMid')
.each(function(d,no){
var base = d3.select(this);
base.selectAll("g").data(d.values);
....
})
Can anyone point out why the old charts aren't removed from the DOM before update on select using jQuery change function? I have svg1.exit().remove(); at the end of the function:
`svg1.exit().remove();`
Here's the JSON data to show up on page load
[{"name":"A","tid":"54","image":"img","measure":32,"group":"53"},
{"name":"B","tid":"55","image":"img","measure":45,"group":"53"},
{"name":"R","tid":"59","image":"img","measure":40,"group":"23"},
{"name":"T","tid":"29","image":"img","measure":20,"group":"23"}]
And on update
[{"name":"G","tid":"14","image":"img","measure":78,"group":"53"},
{"name":"S","tid":"85","image":"img","measure":25,"group":"55"},
{"name":"U","tid":"44","image":"img","measure":78,"group":"53"},
{"name":"K","tid":"29","image":"img","measure":32.6,"group":"55"},
{"name":"Y","tid":"53","image":"img","measure":30,"group":"24"},
{"name":"O","tid":"26","image":"img","measure":25,"group":"24"}]
Full Code:
var width = 350,
height = 310,
barHeight = 80,
left_width = 80,
fo_height = 70;
var x = d3.scale.linear().range([0, width / 1.5]);
y = d3.scale.ordinal().rangeBands([0, height]);
function chart(array){
array = JSON.parse(array);
console.log(array);
var colors = ['#222','red','blue'];
var nested_data = d3.nest().key(function(d) { return d.group; }).entries(array);
ymax = d3.max(nested_data,function(d){
return d.values.map(function(t){
return t.measure;
})
});
nested_data.forEach(function(nd){
nd.values.forEach(function(d){
d.measure = +d.measure;
})
})
var diff = 2;
var tickno = 5
var ymax = d3.max(nested_data, function(nd,k) {
return d3.max(nd.values,function(p,l){
return p.measure ;
});
});
var ymin = d3.min(nested_data, function(nd,k) {
return d3.min(nd.values,function(p,l){
return p.measure - 2;
});
});
x.domain([ymin ,ymax]);
console.log(nested_data);
var svg1 = d3.select('.chart').selectAll("svg").data(nested_data)
var svg = svg1.enter().append("svg:svg")
svg.attr("class", "r_chart")
.attr("width", left_width + width)
.attr("height", height)
.attr('viewBox', '0 0 350 310')
.attr('perserveAspectRatio', 'xMinYMid')
.each(function(d,no){
var count = 1;
var base = d3.select(this);
var bar1 = base.selectAll("g").data(d.values);
var bar = bar1.enter().append("g")
.attr("transform", function(t, i) {
return "translate("+left_width * count+"," + (i+0.5) * barHeight + ")";
count++;
});
var rule1 = base.selectAll(".rule")
.data(x.ticks(5));
var rule = rule1.enter().append("text");
rule.attr("class", "rule changeprice")
.attr("x", function(d) { return x(d) + left_width ; })
.attr("y", barHeight /2)
.attr("dy", -6)
.attr("text-anchor", "middle")
.attr("font-size", 10)
.text(String);
var rect = bar.append("rect")
.style("fill", function(v,l) {
return colors[l]
})
.attr("width", 0)
.attr("height", barHeight - 20)
.attr("rx", 2)
.attr("width", function(t){
return x(t.measure) + 'px';
})
bar.append("text")
.attr("dx", - left_width)
.attr("dy", fo_height )
.style("font-size",'10px')
.text(function(t) { return t.name; })
}) // each fucntion
svg1.exit().remove();
}
var array = $('#a2').text();
chart(array);
$('select').on('change',function(){
var get = $(this).val();
var data = $('#a'+get).text();
chart(data)
});
Here you go: jsfiddle
Please tell if this is what you need. I changed the way of creating the svg1 var. Removing items right after the change is mostly working way better. I also had issues with exit().remove() and found that out after a very long time. It's not the way d3 should be used tho, but it works well.
css:
.data{display:none}
html:
<select><option>Select</option><option value='1'>1</option><option value='2'>2</option></select>
<div class="chart"></div>
<div id='a2' class="data">[{"name":"A","tid":"54","image":"img","measure":32,"group":"53"},{"name":"B","tid":"55","image":"img","measure":45,"group":"53"},{"name":"R","tid":"59","image":"img","measure":40,"group":"23"},{"name":"T","tid":"29","image":"img","measure":20,"group":"23"}]</div>
<div id='a1' class='data'>[{"name":"G","tid":"14","image":"img","measure":78,"group":"53"},{"name":"S","tid":"85","image":"img","measure":25,"group":"55"},
{"name":"U","tid":"44","image":"img","measure":78,"group":"53"}, {"name":"K","tid":"29","image":"img","measure":32.6,"group":"55"}, {"name":"Y","tid":"53","image":"img","measure":30,"group":"24"},{"name":"O","tid":"26","image":"img","measure":25,"group":"24"}]</div>
js:
var width = 350,
height = 310,
barHeight = 80,
left_width = 80,
fo_height = 70;
var x = d3.scale.linear().range([0, width / 1.5]);
y = d3.scale.ordinal().rangeBands([0, height]);
function chart(array){
array = JSON.parse(array);
console.log(array);
var colors = ['#222','red','blue'];
var nested_data = d3.nest().key(function(d) { return d.group; }).entries(array);
ymax = d3.max(nested_data,function(d){
return d.values.map(function(t){
return t.measure;
})
});
nested_data.forEach(function(nd){
nd.values.forEach(function(d){
d.measure = +d.measure;
})
})
var diff = 2;
var tickno = 5
var ymax = d3.max(nested_data, function(nd,k) {
return d3.max(nd.values,function(p,l){
return p.measure ;
});
});
var ymin = d3.min(nested_data, function(nd,k) {
return d3.min(nd.values,function(p,l){
return p.measure - 2;
});
});
x.domain([ymin ,ymax]);
console.log(nested_data);
svg1 = d3.select('.chart').selectAll("svg").data(nested_data)
var svg = svg1.enter().append("svg:svg")
svg.attr("class", "r_chart")
.attr("width", left_width + width)
.attr("height", height)
.attr('viewBox', '0 0 350 310')
.attr('perserveAspectRatio', 'xMinYMid')
.each(function(d,no){
var count = 1;
var base = d3.select(this);
var bar1 = base.selectAll("g").data(d.values);
var bar = bar1.enter().append("g")
.attr("transform", function(t, i) {
return "translate("+left_width * count+"," + (i+0.5) * barHeight + ")";
count++;
});
var rule1 = base.selectAll(".rule")
.data(x.ticks(5));
var rule = rule1.enter().append("text");
rule.attr("class", "rule changeprice")
.attr("x", function(d) { return x(d) + left_width ; })
.attr("y", barHeight /2)
.attr("dy", -6)
.attr("text-anchor", "middle")
.attr("font-size", 10)
.text(String);
var rect = bar.append("rect")
.style("fill", function(v,l) {
return colors[l]
})
.attr("width", 0)
.attr("height", barHeight - 20)
.attr("rx", 2)
.attr("width", function(t){
return x(t.measure) + 'px';
})
bar.append("text")
.attr("dx", - left_width)
.attr("dy", fo_height )
.style("font-size",'10px')
.text(function(t) { return t.name; })
}) // each fucntion
}
var array = $('#a2').text();
var svg1;
chart(array);
$('select').on('change',function(){
var get = $(this).val();
var data = $('#a'+get).text();
svg1.remove();
chart(data)
});
var margin = {top: 30, right: 20, bottom: 30, left: 70},
h= 500;
w = 960;
ruleColor = "#CCC";
minVal = 0;
maxVal = 100;
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var viz = d3.select("#radar")
.append('svg:svg')
.attr('width', w)
.attr('height', h)
.attr('class', 'vizSvg');
viz.append("svg:rect")
.attr('id', 'axis-separator')
.attr('x', 0)
.attr('y', 0)
.attr('height', 0)
.attr('width', 0)
.attr('height', 0);
vizBody = viz.append("svg:g")
.attr('id', 'body');
var heightCircleConstraint = 500 - margin.top - margin.bottom;
var widthCircleConstraint = width = 960 - margin.left - margin.right,
circleConstraint = d3.min([heightCircleConstraint, widthCircleConstraint]);
var radius = d3.scale.linear().domain([minVal, maxVal]).range([0, (circleConstraint / 2)]);
var radiusLength = radius(maxVal);
var centerXPos = widthCircleConstraint / 2 + margin.left;
var centerYPos = heightCircleConstraint / 2 + margin.top;
vizBody.attr("transform",
"translate(" + centerXPos + ", " + centerYPos + ")");
d3.json("data/radar.json", function(error, data) {
var hours = [];
var series = [[]];
data.QualitySummaryObject.forEach(function(d,i) {
series[0][i] = d.extractPercentage;
hours[i] = d.extractorName;
});
for (i = 0; i < series.length; i += 1) {
series[i].push(series[i][0]);
}
//console.log(series.length);
var radialTicks = radius.ticks(5);
vizBody.selectAll('.circle-ticks').remove();
vizBody.selectAll('.line-ticks').remove();
var circleAxes = vizBody.selectAll('.circle-ticks')
.data(radialTicks)
.enter().append('svg:g')
.attr("class", "circle-ticks");
circleAxes.append("svg:circle")
.attr("r", function (d, i) {
return radius(d);
})
.attr("class", "circle")
.style("stroke", ruleColor)
.style("fill", "none");
circleAxes.append("svg:text")
.attr("text-anchor", "middle")
.attr("dy", function (d) {
return -1 * radius(d);
})
.text(String);
var lineAxes = vizBody.selectAll('.line-ticks')
.data(hours)
.enter().append('svg:g')
.attr("transform", function (d, i) {
return "rotate(" + ((i / hours.length * 360) - 90) +
")translate(" + radius(maxVal) + ")";
})
.attr("class", "line-ticks");
lineAxes.append('svg:line')
.attr("x2", -1 * radius(maxVal))
.style("stroke", ruleColor)
.style("fill", "none");
lineAxes.append('svg:text')
.text(String)
.attr("text-anchor", "middle")
.attr("transform","translate(15) rotate(90)");
//var highlightedDotSize = 4;
var groups = vizBody.selectAll('.series').data(series);
//console.log(hours.length);
groups.enter().append("svg:g")
.attr('class', 'series')
.style('fill', "green")
.style('stroke',"#ccc");
//groups.exit().remove();
//console.log(Math.PI);
var lines = groups.append('svg:path')
.attr("class", "line")
/*.attr("d", d3.svg.line.radial()
.radius(function (d) {
return 10;
})
.angle(function (d, i) {
if (i == hours.length) {
i = 0;
} //close the line
return (i / hours.length) * 2 * Math.PI;
}))*/
.style("stroke-width", 1)
.style("fill", "rgba(124,240,10,0.1)");
/*groups.selectAll(".curr-point")
.data(function (d) {
return [d[0]];
})
.enter().append("svg:circle")
.attr("class", "curr-point")
.attr("r", 0);
groups.selectAll(".clicked-point")
.data(function (d) {
return [d[0]];
})
.enter().append("svg:circle")
.attr('r', 0)
.attr("class", "clicked-point");*/
lines.attr("d", d3.svg.line.radial()
.radius(function (d) {
return radius(d);
})
.angle(function (d, i) {
if (i === hours.length) {
i = 0;
} //close the line
return (i / hours.length) * 2 * Math.PI;
}));
});
i implemented this code to create radar chart with a json data here is the json data format
{
"QualitySummaryObject": [
{
"extractPercentage": 68.81964,
"extractorName": "Classification"
},
{
"extractPercentage": 74.09091,
"extractorName": "Keyword Match"
},
{
"extractPercentage": 54.62963,
"extractorName": "LocationBroadcast"
},
{
"extractPercentage": 98.91892,
"extractorName": "Qualification"
},
{
"extractPercentage": 98.76923,
"extractorName": "User Profile Location"
},
{
"extractPercentage": 80.15909,
"extractorName": "Valid Person Name"
},
]
}
Now i want to put tooltip on each node point .. but i am not able to get any idea how to do that any body can help?
Here is an example of Twitter Bootstrap tooltips running on SVGs with D3 http://markhansen.co.nz/stolen-vehicles-pt2/
To get it working on newer versions see Why doesn't Twitter Bootstrap popover work with SVG elements? You'll need to use a 2.3.0+ version of bootstrap or the fix I posted in that thread.