I am working on an application that will be a hybrid pie chart and bubble chart -
here is a working animation for a simple doughnut pie chart.
http://jsfiddle.net/Qh9X5/8817/
var svg = d3.select("body")
.append("svg")
.append("g")
svg.append("g")
.attr("class", "slices");
svg.append("g")
.attr("class", "labels");
svg.append("g")
.attr("class", "lines");
var width = 560,
height = 450,
radius = Math.min(width, height) / 2;
var pie = d3.layout.pie()
.sort(null)
.value(function(d) {
return d.value;
});
var arc = d3.svg.arc()
.outerRadius(radius * 0.85)
.innerRadius(radius * 0.83);
var outerArc = d3.svg.arc()
.innerRadius(radius * 0.9)
.outerRadius(radius * 0.9);
svg.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var key = function(d){ return d.data.label; };
var color = d3.scale.ordinal()
.domain(["Lorem ipsum", "dolor sit", "amet", "consectetur", "adipisicing"])
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56"]);
function randomData (){
var labels = color.domain();
return labels.map(function(label){
return { label: label, value: Math.random() }
});
}
console.log("randomData()", randomData());
change(randomData());
d3.select(".randomize")
.on("click", function(){
change(randomData());
});
function change(data) {
/* ------- PIE SLICES -------*/
var slice = svg.select(".slices").selectAll("path.slice")
.data(pie(data), key);
slice.enter()
.insert("path")
.style("fill", function(d) { return color(d.data.label); })
.attr("class", "slice");
slice
.transition().duration(1000)
.attrTween("d", function(d) {
this._current = this._current || d;
var interpolate = d3.interpolate(this._current, d);
this._current = interpolate(0);
return function(t) {
return arc(interpolate(t));
};
})
slice.exit()
.remove();
};
http://jsfiddle.net/NYEaX/1487/
This is a static doughnut chart -- where I have calculated the mid-arcs -- but have lost the animation. So with the above sample of code -- where is it possible to obtain the arc.centroid(d)?
var width = 960,
height = 500,
radius = Math.min(width, height) / 2;
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var arc = d3.svg.arc()
.outerRadius(radius - 60)
.innerRadius(radius - 70);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.value; });
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var data = [
{
"group": "<5",
"value": 1000,
"children": [
{
"group": "<5",
"label": "Mel",
"value": 1000,
"totalGroupValue": 1000
}
]
},
{
"group": "5-13",
"value": 1000,
"children": [
{
"group": "5-13",
"label": "Erica",
"value": 1000,
"totalGroupValue": 1000
}
]
},
{
"group": "14-17",
"value": 2000,
"children": [
{
"group": "14-17",
"label": "Jessica",
"value": 1500,
"totalGroupValue": 2000
},
{
"group": "14-17",
"label": "Jill",
"value": 500,
"totalGroupValue": 2000
}
]
},
{
"group": "18-24",
"value": 1300,
"children": [
{
"group": "18-24",
"label": "Jerry",
"value": 500,
"totalGroupValue": 1300
},
{
"group": "18-24",
"label": "Ben",
"value": 500,
"totalGroupValue": 1300
},
{
"group": "18-24",
"label": "Billy",
"value": 300,
"totalGroupValue": 1300
}
]
},
{
"group": "25-44",
"value": 1000,
"children": [
{
"group": "25-44",
"label": "Kelly",
"value": 1000,
"totalGroupValue": 1000
}
]
}
];
var g = svg.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
.style("fill", function(d) {
return color(d.data.group);
});
arc
.outerRadius(radius - 10)
.innerRadius(0);
//create zone regions
var zones = [];
g.append("circle")
.attr("transform", function(d) {
zones[d.data.group] = arc.centroid(d);
return "translate(" + arc.centroid(d) + ")";
})
.attr("r", "1px")
.style("fill", function(d) {
return "black"//color(d.data.group);
});
g.append("g")
.attr("class", function(d,i) {
console.log("d", d)
return "bubble"+i;//color(d.data.group);
})
.attr("transform", function(d) {
zones[d.data.group] = arc.centroid(d);
return "translate(" + arc.centroid(d) + ")";
})
.attr("r", "1px")
.style("fill", function(d) {
return "black"//color(d.data.group);
});
//create zone regions
//custom bubble chart
function makeBubbles(transform, group, radius){
g.append("circle")
.attr("transform", function(d) {
return "translate("+transform+")";
})
.attr("r", radius)
.style("stroke", function(d) {
return "black";//color(group);
})
.style("fill", function(d) {
return color(group);
});
}
function bubbledata(data){
console.log("data", data)
//loop through data -- and MERGE children
var childs = [];
$.each(data, function( index, value ) {
childs.push(value.children);
});
var merged = data;//[].concat.apply([], childs);//flatterns multidimensional array
return $.extend(true, {}, {"children": merged});// return deep clone
}
function setBubbleChart(width, index, data){
//_create bubble
var diameter = width/2;//take half/width
var bubs = svg.select(".bubble"+index).append("g")
.attr("class", "bubs");
bubs.attr("transform", "translate("+-diameter/2+","+-diameter/2+")");
var bubble = d3.layout.pack()
.size([diameter, diameter])
.value(function(d) {
return d.value;
})
.padding(3);
//_create bubble
var data = bubbledata(data);
var nodes = bubble.nodes(data)
.filter(function(d) {
return !d.children;
}); // filter out the outer bubble
var bubbles = bubs.selectAll('circle')
.data(nodes);
bubbles.enter()
.insert("circle")
.attr('transform', function (d) {
return 'translate(' + d.x + ',' + d.y + ')';
})
.attr('r', function (d) {
return d.r;
})
.style("fill", function (d) {
return color(d.group);
});
bubbles = bubbles.transition()
.transition()
.duration(250)
.attr('transform', function(d) {
return 'translate(' + d.x + ',' + d.y + ')';
})
.attr('r', function (d) {
return d.r;
})
.ease('sine');
}
//loop through data and for EACH children array paint dots.
$.each(data, function( index, value ) {
setBubbleChart(100, index, value.children);
});
//custom bubble chart
function type(d) {
d.value = +d.value;
return d;
}
I've plotted the mid-arcs - but they are not being updated on a change of data?
http://jsfiddle.net/Qh9X5/10066/
function change(data) {
var slice = svg.select(".slices").selectAll("path.slice")
.data(pie(data), key);
slice.enter()
.insert("path")
.style("fill", function(d) {
return color(d.data.label);
})
.attr("class", "slice");
slice
.transition().duration(1000)
.attrTween("d", function(d) {
this._current = this._current || d;
var interpolate = d3.interpolate(this._current, d);
this._current = interpolate(0);
return function(t) {
return arc(interpolate(t));
};
})
slice.exit()
.remove();
var placeholders = svg.select(".placeholders").selectAll("circle.placeholder")
.data(pie(data), key);
placeholders.enter()
.insert("circle")
.style("fill", function(d) {
return "white";
//return color(d.data.label);
})
.attr("transform", function(d) {
return "translate(" + arc.centroid(d) + ")";
})
.attr("r", "5")
.attr("class", "placeholder");
placeholders
.transition().duration(1000)
placeholders.exit()
.remove();
};
I've managed to create an arc2 -- and animate the placeholders.
http://jsfiddle.net/Qh9X5/10068/
var svg = d3.select("#pies")
.append("svg")
.append("g")
svg.append("g")
.attr("class", "slices");
svg.append("g")
.attr("class", "placeholders");
svg.append("g")
.attr("class", "labels");
svg.append("g")
.attr("class", "lines");
var width = 560,
height = 450,
radius = Math.min(width, height) / 2;
var pie = d3.layout.pie()
.sort(null)
.value(function(d) {
return d.value;
});
var arc = d3.svg.arc()
.outerRadius(radius * 0.85)
.innerRadius(radius * 0.83);
var arc2 = d3.svg.arc()
.outerRadius(radius - 10)
.innerRadius(0);
var outerArc = d3.svg.arc()
.innerRadius(radius * 0.9)
.outerRadius(radius * 0.9);
svg.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var key = function(d) {
return d.data.label;
};
var color = d3.scale.ordinal()
.domain(["Lorem ipsum", "dolor sit", "amet", "consectetur", "adipisicing"])
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56"]);
function randomData() {
var labels = color.domain();
return labels.map(function(label) {
return {
label: label,
value: Math.random()
}
});
}
console.log("randomData()", randomData());
change(randomData());
d3.select(".randomize")
.on("click", function() {
change(randomData());
});
function change(data) {
var slice = svg.select(".slices").selectAll("path.slice")
.data(pie(data), key);
slice.enter()
.insert("path")
.style("fill", function(d) {
return color(d.data.label);
})
.attr("class", "slice");
slice
.transition().duration(1000)
.attrTween("d", function(d) {
this._current = this._current || d;
var interpolate = d3.interpolate(this._current, d);
this._current = interpolate(0);
return function(t) {
return arc(interpolate(t));
};
})
slice.exit()
.remove();
var placeholders = svg.select(".placeholders").selectAll("circle.placeholder")
.data(pie(data), key);
placeholders.enter()
.insert("circle")
.style("fill", function(d) {
return "white";
})
.attr("r", "5")
.attr("class", "placeholder");
placeholders
.transition().duration(1000)
.attr("transform", function(d) {
console.log("arc.centroid(d)", arc2.centroid(d))
return "translate(" + arc2.centroid(d) + ")";
})
placeholders.exit()
.remove();
};
http://jsfiddle.net/Qh9X5/10075/
I have managed to merge the pie chart with the bubble chart -- if the data sets change - the animations should be stable.
var svg = d3.select("#pies")
.append("svg")
.append("g")
svg.append("g")
.attr("class", "slices");
svg.append("g")
.attr("class", "placeholders");
var width = 560,
height = 450,
radius = Math.min(width, height) / 2;
var pie = d3.layout.pie()
.sort(null)
.value(function(d) {
return d.value;
});
var arc = d3.svg.arc()
.outerRadius(radius * 0.85)
.innerRadius(radius * 0.83);
var arc2 = d3.svg.arc()
.outerRadius(radius - 10)
.innerRadius(0);
var outerArc = d3.svg.arc()
.innerRadius(radius * 0.9)
.outerRadius(radius * 0.9);
svg.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
function colores_google(n) {
var colores_g = ["#f7b363", "#448875", "#c12f39", "#2b2d39", "#f8dd2f"];
return colores_g[n % colores_g.length];
}
var data = [{
"group": "<5",
"value": 1000,
"children": [{
"group": "<5",
"label": "Mel",
"value": 1000,
"totalGroupValue": 1000
}]
}, {
"group": "5-13",
"value": 1000,
"children": [{
"group": "5-13",
"label": "Erica",
"value": 1000,
"totalGroupValue": 1000
}]
}, {
"group": "14-17",
"value": 2000,
"children": [{
"group": "14-17",
"label": "Jessica",
"value": 1500,
"totalGroupValue": 2000
}, {
"group": "14-17",
"label": "Jill",
"value": 500,
"totalGroupValue": 2000
}]
}, {
"group": "18-24",
"value": 1300,
"children": [{
"group": "18-24",
"label": "Jerry",
"value": 500,
"totalGroupValue": 1300
}, {
"group": "18-24",
"label": "Ben",
"value": 500,
"totalGroupValue": 1300
}, {
"group": "18-24",
"label": "Billy",
"value": 300,
"totalGroupValue": 1300
}]
}, {
"group": "25-44",
"value": 1000,
"children": [{
"group": "25-44",
"label": "Kelly",
"value": 1000,
"totalGroupValue": 1000
}]
}];
$.each(data, function(index, value) {
value["groupid"] = index;
$.each(value.children, function(i, v) {
v["groupid"] = index;
});
});
//slices
var slice = svg.select(".slices").selectAll("path.slice")
.data(pie(data));
slice.enter()
.insert("path")
.style("fill", function(d) {
return colores_google(d.data.groupid);
})
.attr("class", "slice");
slice
.transition().duration(1000)
.attrTween("d", function(d) {
this._current = this._current || d;
var interpolate = d3.interpolate(this._current, d);
this._current = interpolate(0);
return function(t) {
return arc(interpolate(t));
};
})
slice.exit()
.remove();
//slices
//placeholder bubbles
var placeholders = svg.select(".placeholders").selectAll("g.placeholder")
.data(pie(data));
placeholders.enter()
.insert("g")
.attr("class", function(d, i) {
return "placeholder place" + i;
});
placeholders
.transition().duration(1000)
.attr("transform", function(d) {
return "translate(" + arc2.centroid(d) + ")";
})
placeholders.exit()
.remove();
//placeholder bubbles
//bubbles
function bubbledata(data) {
//loop through data -- and MERGE children
var childs = [];
$.each(data, function(index, value) {
childs.push(value.children);
});
return $.extend(true, {}, {
"children": data
}); // return deep clone
}
function setBubbleChart(width, index, data) {
//_create bubble
var diameter = width / 2; //take half/width
var bubs = svg.select(".place" + index).append("g")
.attr("class", "bubs");
bubs.attr("transform", "translate(" + -diameter / 2 + "," + -diameter / 2 + ")");
var bubble = d3.layout.pack()
.size([diameter, diameter])
.value(function(d) {
return d.value;
})
.padding(3);
//_create bubble
var data = bubbledata(data);
var nodes = bubble.nodes(data)
.filter(function(d) {
return !d.children;
}); // filter out the outer bubble
var bubbles = bubs.selectAll('circle')
.data(nodes);
bubbles.enter()
.insert("circle")
.attr('transform', function(d) {
return 'translate(' + d.x + ',' + d.y + ')';
})
.attr('r', function(d) {
return d.r;
})
.style("fill", function(d) {
return colores_google(d.groupid);
});
bubbles = bubbles.transition()
.transition()
.duration(250)
.attr('transform', function(d) {
return 'translate(' + d.x + ',' + d.y + ')';
})
.attr('r', function(d) {
return d.r;
})
.ease('sine');
}
//loop through data and for EACH children array paint dots.
$.each(data, function(index, value) {
setBubbleChart(100, index, value.children);
});
//custom bubble chart
Related
I have an old doughnut hybrid pie chart/bubble chart combo working on d3v3.
//doughnut bubble chart v3
https://jsfiddle.net/ajevh5wf/
var $this = $('body');
var w = 300,
h = 300;
var radius = Math.min(w, h) / 2;
var svg = d3.select($this[0])
.append("svg")
.attr("width", w)
.attr("height", h)
.append("g")
svg.append("g")
.attr("class", "slices");
svg.append("g")
.attr("class", "placeholders");
var pie = d3.layout.pie()
.sort(null)
.value(function(d) {
return d.value;
});
var arc = d3.svg.arc()
.outerRadius(radius * 0.85)
.innerRadius(radius * 0.83);
var arc2 = d3.svg.arc()
.outerRadius(radius - 10)
.innerRadius(0);
var outerArc = d3.svg.arc()
.innerRadius(radius * 0.9)
.outerRadius(radius * 0.9);
svg.attr("transform", "translate(" + w / 2 + "," + h / 2 + ")");
function colores_google(n) {
var colores_g = ["#f7b363", "#448875", "#c12f39", "#2b2d39", "#f8dd2f"];
return colores_g[n % colores_g.length];
}
var data = [{
"group": "<5",
"value": 1000,
"children": [{
"group": "<5",
"label": "Mel",
"value": 1000,
"totalGroupValue": 1000
}]
}, {
"group": "5-13",
"value": 1000,
"children": [{
"group": "5-13",
"label": "Erica",
"value": 1000,
"totalGroupValue": 1000
}]
}, {
"group": "14-17",
"value": 2000,
"children": [{
"group": "14-17",
"label": "Jessica",
"value": 1500,
"totalGroupValue": 2000
}, {
"group": "14-17",
"label": "Jill",
"value": 500,
"totalGroupValue": 2000
}]
}, {
"group": "18-24",
"value": 1300,
"children": [{
"group": "18-24",
"label": "Jerry",
"value": 500,
"totalGroupValue": 1300
}, {
"group": "18-24",
"label": "Ben",
"value": 500,
"totalGroupValue": 1300
}, {
"group": "18-24",
"label": "Billy",
"value": 150,
"totalGroupValue": 1300
}, {
"group": "18-24",
"label": "Billy2",
"value": 150,
"totalGroupValue": 1300
}]
}, {
"group": "25-44",
"value": 1000,
"children": [{
"group": "25-44",
"label": "Kelly",
"value": 1000,
"totalGroupValue": 1000
}]
}];
$.each(data, function(index, value) {
value["groupid"] = index;
$.each(value.children, function(i, v) {
v["groupid"] = index;
});
});
//slices
var slice = svg.select(".slices").selectAll("path.slice")
.data(pie(data));
slice.enter()
.insert("path")
.style("fill", function(d) {
return colores_google(d.data.groupid);
})
.attr("class", "slice");
slice
.transition().duration(1000)
.attrTween("d", function(d) {
this._current = this._current || d;
var interpolate = d3.interpolate(this._current, d);
this._current = interpolate(0);
return function(t) {
return arc(interpolate(t));
};
})
slice.exit()
.remove();
//slices
//placeholder bubbles
var placeholders = svg.select(".placeholders").selectAll("g.placeholder")
.data(pie(data));
placeholders.enter()
.insert("g")
.attr("class", function(d, i) {
return "placeholder place" + i;
});
placeholders
.transition().duration(1000)
.attr("transform", function(d) {
return "translate(" + arc2.centroid(d) + ")";
})
placeholders.exit()
.remove();
//placeholder bubbles
//bubbles
function bubbledata(data) {
//loop through data -- and MERGE children
var childs = [];
$.each(data, function(index, value) {
childs.push(value.children);
});
return $.extend(true, {}, {
"children": data
}); // return deep clone
}
function setBubbleChart(width, index, data) {
//_create bubble
var diameter = width / 2; //take half/width
var bubs = svg.select(".place" + index).append("g")
.attr("class", "bubs");
bubs.attr("transform", "translate(" + -diameter / 2 + "," + -diameter / 2 + ")");
var bubble = d3.layout.pack()
.size([diameter, diameter])
.value(function(d) {
return d.value;
})
.padding(3);
//_create bubble
var data = bubbledata(data);
var nodes = bubble.nodes(data)
.filter(function(d) {
return !d.children;
}); // filter out the outer bubble
var bubbles = bubs.selectAll('circle')
.data(nodes);
bubbles.enter()
.insert("circle")
.attr('transform', function(d) {
return 'translate(' + d.x + ',' + d.y + ')';
})
.attr('r', function(d) {
return d.r;
})
.style("fill", function(d) {
return colores_google(d.groupid);
});
bubbles = bubbles.transition()
.transition()
.duration(250)
.attr('transform', function(d) {
return 'translate(' + d.x + ',' + d.y + ')';
})
.attr('r', function(d) {
return d.r;
})
.ease('sine');
}
//loop through data and for EACH children array paint dots.
$.each(data, function(index, value) {
setBubbleChart(100, index, value.children);
});
//custom bubble chart
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
//doughnut bubble chart v4 - not working
https://jsfiddle.net/4rdj0fze/1/
var $this = $('body');
var data = [{
"group": "<5",
"value": 1000,
"children": [{
"group": "<5",
"label": "Mel",
"value": 1000,
"totalGroupValue": 1000
}]
}, {
"group": "5-13",
"value": 1000,
"children": [{
"group": "5-13",
"label": "Erica",
"value": 1000,
"totalGroupValue": 1000
}]
}, {
"group": "14-17",
"value": 2000,
"children": [{
"group": "14-17",
"label": "Jessica",
"value": 1500,
"totalGroupValue": 2000
}, {
"group": "14-17",
"label": "Jill",
"value": 500,
"totalGroupValue": 2000
}]
}, {
"group": "18-24",
"value": 1300,
"children": [{
"group": "18-24",
"label": "Jerry",
"value": 500,
"totalGroupValue": 1300
}, {
"group": "18-24",
"label": "Ben",
"value": 500,
"totalGroupValue": 1300
}, {
"group": "18-24",
"label": "Billy",
"value": 150,
"totalGroupValue": 1300
}, {
"group": "18-24",
"label": "Billy2",
"value": 150,
"totalGroupValue": 1300
}]
}, {
"group": "25-44",
"value": 1000,
"children": [{
"group": "25-44",
"label": "Kelly",
"value": 1000,
"totalGroupValue": 1000
}]
}];
var width = 300,
height = 300;
var color = d3.scaleOrdinal()
.range(["#eb6383", "#fa9191", "#ffe9c5", "#b4f2e1"]);
var margin = {
top: 20,
right: 5,
bottom: 30,
left: 20
},
width = width - margin.left - margin.right,
height = height - margin.top - margin.bottom;
var svg = d3.select($this[0])
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr('class', 'doughnutbubblechart')
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var doughnutbubble = svg.append('g').attr('class', 'doughnutbubble');
var radius = Math.min(width, height) / 2;
doughnutbubble.append("g")
.attr("class", "slices");
doughnutbubble.append("g")
.attr("class", "placeholders");
var pie = d3.pie()
.sort(null)
.value(function(d) {
return d.value;
});
var arc = d3.arc()
.outerRadius(radius * 0.85)
.innerRadius(radius * 0.83);
var arc2 = d3.arc()
.outerRadius(radius - 10)
.innerRadius(0);
var outerArc = d3.arc()
.innerRadius(radius * 0.9)
.outerRadius(radius * 0.9);
doughnutbubble.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
function colores_google(n) {
var colores_g = ["#f7b363", "#448875", "#c12f39", "#2b2d39", "#f8dd2f"];
return colores_g[n % colores_g.length];
}
$.each(data, function(index, value) {
value["groupid"] = index;
$.each(value.children, function(i, v) {
v["groupid"] = index;
});
});
//slices
var slice = doughnutbubble.select(".slices").selectAll("path.slice")
.data(pie(data));
slice.enter()
.insert("path")
.style("fill", function(d) {
return colores_google(d.data.groupid);
})
.attr("class", "slice")
.transition().duration(1000)
.attrTween("d", function(d) {
this._current = this._current || d;
var interpolate = d3.interpolate(this._current, d);
this._current = interpolate(0);
return function(t) {
return arc(interpolate(t));
};
})
slice.exit()
.remove();
//slices
//placeholder bubbles
var placeholders = doughnutbubble.select(".placeholders").selectAll("g.placeholder")
.data(pie(data));
placeholders.enter()
.insert("g")
.attr("class", function(d, i) {
return "placeholder place" + i;
})
.transition().duration(1000)
.attr("transform", function(d) {
return "translate(" + arc2.centroid(d) + ")";
})
placeholders.exit()
.remove();
//placeholder bubbles
//bubbles
function bubbledata(data) {
//loop through data -- and MERGE children
var childs = [];
$.each(data, function(index, value) {
childs.push(value.children);
});
return $.extend(true, {}, {
"children": data
}); // return deep clone
}
function setBubbleChart(width, index, data) {
//_create bubble
var diameter = width / 2; //take half/width
var bubs = doughnutbubble.select(".place" + index).append("g")
.attr("class", "bubs");
bubs.attr("transform", "translate(" + -diameter / 2 + "," + -diameter / 2 + ")");
//var data = bubbledata(data);
//console.log("data", data);
///
var force = d3.forceSimulation();
var foci = {
x: 150,
y: 150
}
var data = funnelData(data, width, height)
.map(function(d) {
d.foci = foci;
return d;
})
force.nodes(data);
var bubbles = bubs.append("g")
.attr("class", "bubbles")
var nodes = bubbles.selectAll("circle")
.data(data)
.enter()
.append('circle')
.attr('r', 1)
.attr('fill', function(d, i) {
return color(i);
});
// Update
nodes.transition()
.delay(300)
.duration(1000)
.attr("r", function(d) {
return d.value * (width / 6) / 100;
});
force.on("tick", function() {
data.forEach(cluster);
data.forEach(collide(0.1));
nodes.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
})
});
function funnelData(data, width, height) {
function getRandom(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
var max_amount = d3.max(data, function(d) {
return parseInt(d.value)
})
var radius_scale = d3.scalePow().exponent(0.5).domain([0, max_amount]).range([2, 85])
data.forEach(function(elem, index) {
elem.radius = radius_scale(elem.value) * .8;
elem.all = 'all';
elem.x = getRandom(0, width);
elem.y = getRandom(0, height);
});
return data;
}
function cluster(d, i) {
var f = d.foci;
var k = 0.55;
d.y += (f.y - d.y) * k * force.alpha()
d.x += (f.x - d.x) * k * force.alpha()
}
var maxRadius = d3.max(data, function(d) {
return d.radius;
})
var padding = 4;
function collide(alpha) {
var quadtree = d3.quadtree().x(function(d) {
return d.x;
}).y(function(d) {
return d.y;
}).extent([
[-1, -1],
[width + 1, height + 1]
]).addAll(data);
return function(d) {
var r = d.radius + maxRadius + padding,
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.length && (quad.data !== d)) {
var x = d.x - quad.data.x,
y = d.y - quad.data.y,
l = Math.sqrt(x * x + y * y),
r = d.radius + quad.data.radius + padding;
if (l < r) {
l = (l - r) / l * alpha;
d.x -= x *= l;
d.y -= y *= l;
quad.data.x += x;
quad.data.y += y;
}
}
return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
});
};
}
}
//loop through data and for EACH children array paint dots.
$.each(data, function(index, value) {
setBubbleChart(100, index, value.children);
});
//custom bubble chart
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
---bubblechart v4 working -
https://jsfiddle.net/of48xjar/
but when I try and add this bubblechartv4 code to the base v4 -- I can't seem to get the bubbles to render properly in the placeholders or the sizes -- and I think in the old version3 had to exclude the parent level to ensure it just renders children bubbles.
Many things have changed from v3 to v4, like d3.pack() instead of d3.layout.pack(), and having to import the "ease" module and reference d3.easeSin instead of 'sine'. Once you take care of all these little differences, your code works:
https://jsfiddle.net/dc6eugtn/1
Here's the relevant section of changes:
function setBubbleChart(width, index, data) {
var diameter = width / 2; //take half/width
var bubs = doughnutbubble.select(".place" + index).append("g")
.attr("class", "bubs");
bubs.attr("transform", "translate(" + -diameter / 2 + "," + -diameter / 2 + ")");
var data = bubbledata(data);
var bubble = d3.pack(data)
.size([diameter, diameter])
.padding(3);
var nodes = d3.hierarchy(data).sum(d => d.value);
var bubbles = bubs.selectAll('circle')
.data(bubble(nodes).descendants());
bubbles.enter()
.filter(d => !d.children)
.insert("circle")
.attr('transform', function(d) {
return 'translate(' + d.x + ',' + d.y + ')';
})
.attr('r', d => d.r)
.style("fill", function(d) {
return colores_google(d.data.groupid);
});
bubbles = bubbles.transition()
.transition()
.duration(250)
.attr('transform', function(d) {
return 'translate(' + d.x + ',' + d.y + ')';
})
.attr('r', d => d.r)
.ease(d3.easeSin);
}
My last question was answered so quickly and smoothly I thought I'd return with another issue I'm failing to figure out on my own.
I used one of the examples to create this graph:
data = [{ "label": "1", "value": 20 },
{ "label": "2", "value": 50 },
{ "label": "3", "value": 30 },
{ "label": "4", "value": 45 }];
var width = 400,
height = 450;
var outerRadius = 200,
innerRadius = outerRadius / 3,
color = d3.scale.category20c();
var pie = d3.layout.pie()
.value(function (d) { return d.value; });
var pieData = pie(data);
var arc = d3.svg.arc()
.innerRadius(innerRadius);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + outerRadius + "," + (outerRadius + 50) + ")");
svg.append("text")
.attr("x", 0)
.attr("y", -(outerRadius + 10))
.style("text-anchor", "middle")
.text("Title[enter image description here][1]");
svg.selectAll("path")
.data(pieData)
.enter().append("path")
.each(function (d) { d.outerRadius = outerRadius - 20; })
.attr("d", arc)
.attr("fill", function (d, i) { return color(i); })
.on("mouseover", arcTween(outerRadius, 0))
.on("mouseout", arcTween(outerRadius - 20, 150));
svg.selectAll("path")
.append("text")
.attr("transform", function (d) {
d.innerRadius = 0;
d.outerRadius = outerRadius;
return "translate(" + arc.centroid(d) + ")";
})
.attr("fill", "white")
.attr("text-anchor", "middle")
.text(function (d, i) { return data[i].label; });
function arcTween(outerRadius, delay) {
return function () {
d3.select(this).transition().delay(delay).attrTween("d", function (d) {
var i = d3.interpolate(d.outerRadius, outerRadius);
return function (t) { d.outerRadius = i(t); return arc(d); };
});
};
}
The idea being that when you hover over a section on the pie chart (donut chart?) it expands. However, this made my labels dissapear and I can't manage to make them come back. I either get an error, or they just don't show up on the screen (even though I see the tag in the inspector). Any obvious thing I'm missing?
Thanks!
You cannot append a <text> element to a <path> element. It simply doesn't work in an SVG. Even not working, the <text> element will be appended.
That being said, a solution is creating a new "enter" selection for the texts:
svg.selectAll(null)
.data(pieData)
.enter()
.append("text")
.attr("transform", function(d) {
d.innerRadius = 0;
d.outerRadius = outerRadius;
return "translate(" + arc.centroid(d) + ")";
})
.attr("fill", "white")
.attr("text-anchor", "middle")
.text(function(d, i) {
return data[i].label;
});
Here is your updated code:
data = [{
"label": "1",
"value": 20
}, {
"label": "2",
"value": 50
}, {
"label": "3",
"value": 30
}, {
"label": "4",
"value": 45
}];
var width = 400,
height = 450;
var outerRadius = 200,
innerRadius = outerRadius / 3,
color = d3.scale.category20c();
var pie = d3.layout.pie()
.value(function(d) {
return d.value;
});
var pieData = pie(data);
var arc = d3.svg.arc()
.innerRadius(innerRadius);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + outerRadius + "," + (outerRadius + 50) + ")");
svg.append("text")
.attr("x", 0)
.attr("y", -(outerRadius + 10))
.style("text-anchor", "middle")
.text("Title[enter image description here][1]");
svg.selectAll("path")
.data(pieData)
.enter().append("path")
.each(function(d) {
d.outerRadius = outerRadius - 20;
})
.attr("d", arc)
.attr("fill", function(d, i) {
return color(i);
})
.on("mouseover", arcTween(outerRadius, 0))
.on("mouseout", arcTween(outerRadius - 20, 150));
svg.selectAll(null)
.data(pieData)
.enter()
.append("text")
.attr("transform", function(d) {
d.innerRadius = 0;
d.outerRadius = outerRadius;
return "translate(" + arc.centroid(d) + ")";
})
.attr("fill", "white")
.attr("text-anchor", "middle")
.text(function(d, i) {
return data[i].label;
});
function arcTween(outerRadius, delay) {
return function() {
d3.select(this).transition().delay(delay).attrTween("d", function(d) {
var i = d3.interpolate(d.outerRadius, outerRadius);
return function(t) {
d.outerRadius = i(t);
return arc(d);
};
});
};
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
i'm trying to make this d3 treemap zoomable but seem to be struggling with it. I keep getting a " treemap.nodes is not a function" error. I tried to follow the Mike Bostock example here, https://bost.ocks.org/mike/treemap/
Here's my code:
var data =
{
"name" : "Max",
"value": 100,
"children":
[
{
"name": "A",
"value": 75,
"children": [
{ "name": "Alpha", "result": 20},
{ "name": "Aplha 2", "result": 40},
{ "name": "Aplha 3", "result": 35}
]
},
{
"name": "B",
"value": 75,
"children": [
{"name": "Bravo", "result": 80},
{"name": "Bravo 2", "result": 20},
{"name": "Bravo 3", "result": 33}
]
},
{
"name": "C",
"value": 75,
"children": [
{"name": "Charle", "result": 84},
{"name": "Charle 2", "result": 43},
{"name": "Charle 3", "result": 24}
]
}
]
}
var margin = {top: 20, right: 0, bottom: 0, left: 0},
width = 960,
height = 500 - margin.top - margin.bottom,
formatNumber = d3.format(",d"),
transitioning;
var x = d3.scale.linear()
.domain([0, width])
.range([0, width]);
var y = d3.scale.linear()
.domain([0, height])
.range([0, height]);
var color = d3.scale.category10();
var svg = d3.select("#chart").append("svg")
.attr("width", 1000)
.attr("height", 1000);
var treemap = d3.layout.treemap()
.size([1000, 1000])
.children(function(d, depth) { return depth ? null : d.children; })
.sort(function(a, b) {return a.value - b.value; })
.ratio(height / width * 0.5 * (1 + Math.sqrt(5)))
.round(false)
.nodes(data);
var cells = svg.selectAll(".cell")
.data(treemap)
.enter()
.append("g")
.attr("class", "cell")
cells.append("rect")
.attr("x", function(d){ return d.x})
.attr("y", function(d){ return d.y})
.attr("width", function(d){ return d.dx})
.attr("height", function(d){ return d.dy})
.attr("stroke", "#fff")
.attr("fill", function(d){ return d.children ? null : color(d.parent.name);})
cells.append("text")
.attr("x", function (d) { return d.x + d.dx /2})
.attr("y", function (d) { return d.y + d.dy /2})
.text(function(d) { return d.children ? null : d.name + d.value;})
var grandparent = svg.append("g")
.attr("class", "grandparent");
grandparent.append("rect")
.attr("y", -margin.top)
.attr("width", width)
.attr("height", margin.top);
grandparent.append("text")
.attr("x", 6)
.attr("y", 6 - margin.top)
.attr("dy", ".75em");
var root = sample_data;
initialize(root);
accumulate(root);
layout(root);
display(root);
function initialize(root) {
root.x = root.y = 0;
root.dx = width;
root.dy = height;
root.depth = 0;
}
function accumulate(d) {
return d.children
? d.value = d.children.reduce(function(p, v) { return p + accumulate(v); }, 0)
: d.value;
}
function layout(d) {
if (d.children) {
treemap.nodes({children: d.children});
d.children.forEach(function(c) {
c.x = d.x + c.x * d.dx;
c.y = d.y + c.y * d.dy;
c.dx *= d.dx;
c.dy *= d.dy;
c.parent = d;
layout(c);
});
}
}
function display(d) {
grandparent
.datum(d.parent)
.on("click", transition)
.select("text")
.text(name(d));
var g1 = svg.insert("g", ".grandparent")
.datum(d)
.attr("class", "depth");
var g = g1.selectAll("g")
.data(d.children)
.enter().append("g");
g.filter(function(d) { return d.children; })
.classed("children", true)
.on("click", transition);
g.selectAll(".child")
.data(function(d) { return d.children || [d]; })
.enter().append("rect")
.attr("class", "child")
.call(rect);
g.append("rect")
.attr("class", "parent")
.call(rect)
.append("title")
.text(function(d) { return formatNumber(d.value); });
g.append("text")
.attr("dy", ".75em")
.text(function(d) { return d.name; })
.call(text);
function transition(d) {
if (transitioning || !d) return;
transitioning = true;
var g2 = display(d),
t1 = g1.transition().duration(750),
t2 = g2.transition().duration(750);
x.domain([d.x, d.x + d.dx]);
y.domain([d.y, d.y + d.dy]);
svg.style("shape-rendering", null);
svg.selectAll(".depth").sort(function(a, b) { return a.depth - b.depth; });
g2.selectAll("text").style("fill-opacity", 0);
t1.selectAll("text").call(text).style("fill-opacity", 0);
t2.selectAll("text").call(text).style("fill-opacity", 1);
t1.selectAll("rect").call(rect);
t2.selectAll("rect").call(rect);
t1.remove().each("end", function() {
svg.style("shape-rendering", "crispEdges");
transitioning = false;
});
}
return g;
}
function text(text) {
text.attr("x", function(d) { return x(d.x) + 6; })
.attr("y", function(d) { return y(d.y) + 6; });
}
function rect(rect) {
rect.attr("x", function(d) { return x(d.x); })
.attr("y", function(d) { return y(d.y); })
.attr("width", function(d) { return x(d.x + d.dx) - x(d.x); })
.attr("height", function(d) { return y(d.y + d.dy) - y(d.y); });
}
function size(d) {
return d.value;
}
function name(d) {
return d.parent
? name(d.parent) + "." + d.name
: d.name;
}
Any help would be appreciated.
The .nodes() function does not return the treemap on which it is called (like many other functions), but the array of nodes instead, so you cannot have treemap= d3.layout.treemap(). [...] .nodes(data). Use the following instead:
var treemap = d3.layout.treemap()
.size([1000, 1000])
.children(function(d, depth) { return depth ? null : d.children; })
.sort(function(a, b) {return a.value - b.value; })
.ratio(height / width * 0.5 * (1 + Math.sqrt(5)))
.round(false);
var nodes = treemap.nodes(data);
I'm new to D3 and i'm trying to use a d3 chart for some visualizations, but i can't seem to figure out how to add data to this specific graphic:
http://bl.ocks.org/dbuezas/9306799
I tried to use:
d3.csv("data.csv", type, function(error, data) {
if (error) throw error; });
but i can't seem to find how to use it with the current setup.
I would appreciate if someone could point me in the right direction.
code:
<script>
var svg = d3.select("body")
.append("svg")
.append("g")
svg.append("g")
.attr("class", "slices");
svg.append("g")
.attr("class", "labels");
svg.append("g")
.attr("class", "lines");
var width = 960,
height = 450,
radius = Math.min(width, height) / 2;
var pie = d3.layout.pie()
.sort(null)
.value(function(d) {
return d.value;
});
var arc = d3.svg.arc()
.outerRadius(radius * 0.8)
.innerRadius(radius * 0.4);
var outerArc = d3.svg.arc()
.innerRadius(radius * 0.9)
.outerRadius(radius * 0.9);
svg.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var key = function(d){ return d.data.label; };
var color = d3.scale.ordinal()
.domain(["Lorem ipsum", "dolor sit", "amet", "consectetur", "adipisicing", "elit", "sed", "do", "eiusmod", "tempor", "incididunt"])
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
function randomData (){
var labels = color.domain();
return labels.map(function(label){
return { label: label, value: Math.random() }
});
}
change(randomData());
d3.select(".randomize")
.on("click", function(){
change(randomData());
});
function change(data) {
/* ------- PIE SLICES -------*/
var slice = svg.select(".slices").selectAll("path.slice")
.data(pie(data), key);
slice.enter()
.insert("path")
.style("fill", function(d) { return color(d.data.label); })
.attr("class", "slice");
slice
.transition().duration(1000)
.attrTween("d", function(d) {
this._current = this._current || d;
var interpolate = d3.interpolate(this._current, d);
this._current = interpolate(0);
return function(t) {
return arc(interpolate(t));
};
})
slice.exit()
.remove();
/* ------- TEXT LABELS -------*/
var text = svg.select(".labels").selectAll("text")
.data(pie(data), key);
text.enter()
.append("text")
.attr("dy", ".35em")
.text(function(d) {
return d.data.label;
});
function midAngle(d){
return d.startAngle + (d.endAngle - d.startAngle)/2;
}
text.transition().duration(1000)
.attrTween("transform", function(d) {
this._current = this._current || d;
var interpolate = d3.interpolate(this._current, d);
this._current = interpolate(0);
return function(t) {
var d2 = interpolate(t);
var pos = outerArc.centroid(d2);
pos[0] = radius * (midAngle(d2) < Math.PI ? 1 : -1);
return "translate("+ pos +")";
};
})
.styleTween("text-anchor", function(d){
this._current = this._current || d;
var interpolate = d3.interpolate(this._current, d);
this._current = interpolate(0);
return function(t) {
var d2 = interpolate(t);
return midAngle(d2) < Math.PI ? "start":"end";
};
});
text.exit()
.remove();
/* ------- SLICE TO TEXT POLYLINES -------*/
var polyline = svg.select(".lines").selectAll("polyline")
.data(pie(data), key);
polyline.enter()
.append("polyline");
polyline.transition().duration(1000)
.attrTween("points", function(d){
this._current = this._current || d;
var interpolate = d3.interpolate(this._current, d);
this._current = interpolate(0);
return function(t) {
var d2 = interpolate(t);
var pos = outerArc.centroid(d2);
pos[0] = radius * 0.95 * (midAngle(d2) < Math.PI ? 1 : -1);
return [arc.centroid(d2), outerArc.centroid(d2), pos];
};
});
polyline.exit()
.remove();
};
</script>
Working code here.
First you need to make a json file with some data example below:
[{
"label": "Lorem ipsum",
"value": 0.719082972034812
}, {
"label": "dolor sit",
"value": 0.03566315700300038
}, {
"label": "amet",
"value": 0.14385902439244092
}, {
"label": "consectetur",
"value": 0.5280405324883759
}, {
"label": "adipisicing",
"value": 0.42760335514321923
}, {
"label": "elit",
"value": 0.7200075597502291
}, {
"label": "sed",
"value": 0.7396465912461281
}, {
"label": "do",
"value": 0.8943409691564739
}, {
"label": "eiusmod",
"value": 0.02541762450709939
}, {
"label": "tempor",
"value": 0.31414360040798783
}, {
"label": "incididunt",
"value": 0.8193990120198578
}]
Refer my plunk data.json
Then in the script load the json like this
d3.json("data.json", function(json) {
change(json);//pass the loaded the json to change function for drawing.
});
I also had the same problem, but in my case, I am coming from a ruby application. Data is loaded off the database and I can't really have a json file to be loaded in JavaScript.
I slightly modified #dbuezas examples only to have a reusable function. I am setting up the svg object and calling change inside.
I'm in a ERB views, and I have my data in ruby instance variables.
<script>
var colors = d3.scale.ordinal()
.domain(<%= #pie_categories.inspect.html_safe %>)
.range(<%= #pie_colors.inspect.html_safe %>);
var chart_data = <%= #pie_chart_data.to_json.html_safe %>;
var width = 450, height = 350;
create_pie_chart_at('#categories_pie_chart',
colors, chart_data, width, height);
</script>
I need to create a legend for the bubble/circle pack chart. I'm displaying the values inside the circle. I need the names as the legend. For an instance, in the below provided data, if the value is 60, i need that name "Petrol" in the legend. How could i achieve it?
Snippet:
var diameter = 200,
format = d3.format(",d"),
color = ["#7b6888", "#ccc", "#aaa", "#6b486b"];
var bubble = d3.layout.pack().size([diameter, diameter]);
var svg = d3.select("#bubbleCharts").append("svg")
.attr("width", diameter + 10)
.attr("height", diameter)
.attr("class", "bubble");
d3.json("flare.json", function(error, root) {
var node = svg.selectAll(".node")
.data(bubble.nodes(classes(root))
.filter(function(d) { return !d.children; }))
.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + d.x + 20 + "," + d.y + ")"; });
node.append("circle").attr("r", function(d) { return d.r+ 7; })
.style("fill", function(d,i) { return color[i];} );
node.append("text").attr("dy", ".3em").style("text-anchor", "middle")
.text(function(d) { return d.value+"%"; });
});
function classes(root) {
var classes = [];
function recurse(name, node) {
if (node.children)
node.children.forEach(function(child){
recurse(node.name, child);
});
else
classes.push({packageName: name, value: node.value});
}
recurse(null, root);
return {children: classes};
}
var legend = d3.select("#bubbleChart").append("svg")
.selectAll("g").data(node.children).enter().append("g")
.attr("class","legend")
.attr("width", radius)
.attr("height", radius * 2)
.attr("transform", function(d, i) {return "translate(" + i *10 + "0" + ")"; });
legend.append("rect").attr("width", 18).attr("height", 10)
.style("fill", function(d, i) { return color[i];});
legend.append("text").attr("x", 24).attr("y", 5).attr("dy", ".35em")
.text(function(d) { return d; });
My data:
{
"name": "Spending Activity",
"children": [
{"name": "Petrol", "value": 10},
{"name": "Travel", "value": 60},
{"name": "Medical", "value": 25},
{"name": "Shopping", "value": 5}
]
}
How would i take the values from json and create a legend? Thanks.
You can simply iterate through your data set and add those values:
legend.selectAll("circle").data(data.children)
.enter()
.append("circle")
.attr("cy", function(d,i) { return (i+1) * 10; })
.attr("r", function(d) { return d.r+ 7; })
.style("fill", function(d,i) {
return color[i];
});
legend.selectAll("text").data(data.children)
.enter()
.append("text")
.attr("transform", function(d,i) {
return "translate(10," + ((i+1) * 10) + ")";
});