Related
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 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
How can I add a legend on top of 3 pie charts (see this fiddle).
I want to specify that the light blue color corresponds to the legend "AAA", medium blue is "BBB" and blue is "CCC".
Also I would like to add numeric values inside each piece of pie. I tried this approach, but then the charts disappear:
var data = [
[{"piece_value",76.34}, {"piece_value",69.05}, {"piece_value",275.19}],
[{"piece_value",69.93}, {"piece_value",61.50}, {"piece_value",153.31}],
[{"piece_value",83.51}, {"piece_value",69.14}, {"piece_value",204.32}]
];
svg.selectAll("path")
.data(d3.layout.pie().value(function(d) { return d.piece_value; }))
.enter().append("path")
.attr("d", d3.svg.arc()
.innerRadius(r / 2)
.outerRadius(r))
.style("fill", function(d, i) { return z(i); });
UPDATE:
I also tried this, but the same result:
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.piece_value; });
svg.selectAll("path")
.data(pie(data))
.enter().append("path")
.attr("d", d3.svg.arc()
.innerRadius(r / 2)
.outerRadius(r))
.style("fill", function(d, i) { return z(i); });
You have a problem with your data array. This:
{"piece_value",76.34}
Is not a proper object. It should have a colon between the name and the value:
{"piece_value": 76.34}
So, let's change your data for this:
var data = [
[{"piece_value":76.34, name:"AAA"}, {"piece_value":69.05, name:"BBB"}, {"piece_value":275.19, name:"CCC"}],
[{"piece_value":69.93, name:"AAA"}, {"piece_value":61.50, name:"BBB"}, {"piece_value":153.31, name:"CCC"}],
[{"piece_value":83.51, name:"AAA"}, {"piece_value":69.14, name:"BBB"}, {"piece_value":204.32, name:"CCC"}]
];
And set the variables for the layout and the arc generator:
var pie = d3.layout.pie()
.sort(null)
.value(function(d) {
return d.piece_value;
});
var arc = d3.svg.arc()
.innerRadius(r / 2)
.outerRadius(r);
For adding the values in each pie, you can use arc.centroid:
svg.selectAll("foo")
.data(pie)
.enter()
.append("text")
.attr("text-anchor", "middle")
.attr("transform", d => "translate(" + arc.centroid(d) + ")")
.text(d => d.data.piece_value);
For creating the legend, add the rectangles and the texts using the inner arrays:
svg.selectAll("foo")
.data(d => d)
.enter()
.append("rect")
Here is the demo:
var data = [
[{
"piece_value": 76.34,
name: "AAA"
}, {
"piece_value": 69.05,
name: "BBB"
}, {
"piece_value": 275.19,
name: "CCC"
}],
[{
"piece_value": 69.93,
name: "AAA"
}, {
"piece_value": 61.50,
name: "BBB"
}, {
"piece_value": 153.31,
name: "CCC"
}],
[{
"piece_value": 83.51,
name: "AAA"
}, {
"piece_value": 69.14,
name: "BBB"
}, {
"piece_value": 204.32,
name: "CCC"
}]
];
var m = 10,
r = 100,
z = d3.scale.category20c();
var svg = d3.select("body").selectAll("svg")
.data(data)
.enter().append("svg")
.attr("width", (r + m) * 2)
.attr("height", (r + m) * 2)
.append("g")
.attr("transform", "translate(" + (r + m) + "," + (r + m) + ")");
var pie = d3.layout.pie()
.sort(null)
.value(function(d) {
return d.piece_value;
});
var arc = d3.svg.arc()
.innerRadius(r / 2.2)
.outerRadius(r/1.2)
svg.selectAll("path")
.data(pie)
.enter().append("path")
.attr("d", arc)
.style("fill", function(d, i) {
return z(i);
});
svg.selectAll("foo")
.data(pie)
.enter()
.append("text")
.attr("text-anchor", "middle")
.attr("transform", d => "translate(" + arc.centroid(d) + ")")
.text(d => d.data.piece_value);
svg.selectAll("foo")
.data(d=>d)
.enter()
.append("text")
.attr("transform", (d,i)=>"translate(" + (-r + 2.5*m + (i * 70)) + "," + (-r + m) + ")")
.text(d=>d.name);
svg.selectAll("foo")
.data(d=>d)
.enter()
.append("rect")
.attr("transform", (d,i)=>"translate(" + (-r + m + (i * 70)) + "," + (-r) + ")")
.attr("width", 10)
.attr("height", 10)
.style("fill", (d, i) => z(i));
<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 am experimenting with D3js Hive plots for the first time and am struggling to understand how to position labels alongside each node on the axes. Please see my fiddle, here:
http://jsfiddle.net/NovasTaylor/jgkqoasm/#base
Using this code I can place the labels for each node on its proper axis. The label (name) is not near its respective node. What code do I need to position the label adjacent to its node?
nodes.append("text")
.attr("class", "text")
.text(function(d) {return d.name;})
// Following is needed to place E,F on the vertical axis toward the top
// of the graph (group=3)
.attr("y", function(d) {
if (d.group === 3) {
return -radius(d.y);
} else {
return radius(d.y);
}
})
.attr("transform", function(d) {
if (d.group !== 3) {
return "rotate(" + (degrees(angle(d.group)) - 90) + ")";
}
})
Any help would be greatly appreciated!
Cheers,
Tim
You should be applying the same transform that's on your node circle elements: a rotation of degrees(angle(d.x)) and an x position of radius(d.y). Further, I would wrap the text in a g and then position that g using those attributes then append a text and rotate it counter to the g so the text is in a proper position:
nodes.append("g")
.attr("class", "text")
.attr("transform", function(d) {
var t = "rotate(" + degrees(angle(d.x)) + ")";
t += "translate(" + radius(d.y) + ",0)";
return t;
})
.append("text")
.attr("transform", function(d) {
var t = "rotate(" + -degrees(angle(d.x)) + ")";
return t;
})
.text(function(d) {return d.name;})
Full working code:
var width = 960,
height = 500,
innerRadius = 40,
outerRadius = 240;
var angle = d3.scale.ordinal().domain(d3.range(4)).rangePoints([0, 2 * Math.PI]),
radius = d3.scale.linear().range([innerRadius, outerRadius]),
color = d3.scale.category10().domain(d3.range(20));
var nodes = [
{name:"A", group:1, x: 0, y: .1},
{name:"B", group:1, x: 0, y: .9},
{name:"C", group:2, x: 1, y: .2},
{name:"D", group:2, x: 1, y: .3},
{name:"E", group:3, x: 2, y: .1},
{name:"F", group:3, x: 2, y: .8}
];
var links = [
{source: nodes[0], target: nodes[2]},
{source: nodes[1], target: nodes[3]},
{source: nodes[2], target: nodes[4]},
{source: nodes[2], target: nodes[5]},
{source: nodes[3], target: nodes[5]},
{source: nodes[4], target: nodes[0]},
{source: nodes[5], target: nodes[1]}
];
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
svg.selectAll(".axis")
.data(d3.range(3))
.enter().append("line")
.attr("class", "axis")
.attr("transform", function(d) { return "rotate(" + degrees(angle(d)) + ")"; })
.attr("x1", radius.range()[0])
.attr("x2", radius.range()[1]);
svg.selectAll(".link")
.data(links)
.enter().append("path")
.attr("class", "link")
.attr("d", d3.hive.link()
.angle(function(d) { return angle(d.x); })
.radius(function(d) { return radius(d.y); }))
.style("stroke", function(d) { return color(d.source.x); });
var nodes = svg.selectAll("g.node")
.data(nodes, function (d) {
return d.name;
});
nodes.enter()
.append("g");
nodes.append("circle")
.attr("class", "node")
.attr("transform", function(d) { return "rotate(" + degrees(angle(d.x)) + ")"; })
.attr("cx", function(d) { return radius(d.y); })
.attr("r", 5)
.style("fill", function(d) { return color(d.x); });
// Append name as label to each node
nodes.append("g")
.attr("class", "text")
.attr("transform", function(d) {
var t = "rotate(" + degrees(angle(d.x)) + ")";
t += "translate(" + radius(d.y) + ",0)";
return t;
})
.append("text")
.attr("transform", function(d) {
var t = "rotate(" + -degrees(angle(d.x)) + ")";
return t;
})
.text(function(d) {return d.name;})
function degrees(radians) {
return radians / Math.PI * 180 - 90;
}
.link {
fill: none;
stroke-width: 1.5px;
}
.axis, .node {
stroke: #000;
stroke-width: 1.5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="http://d3js.org/d3.hive.v0.min.js"></script>
I think it would be more easy to put the circles and texts in a group. So there wont need additional calculations for text positions.
var nodes = svg.selectAll("g.node")
.data(nodes, function(d) {
return d.name;
});
var group = nodes.enter()
.append("g")
.attr("transform", function(d) {
return "rotate(" + degrees(angle(d.x)) + ")";
});
group.append("circle")
.attr("class", "node")
.attr("cx", function(d) {
return radius(d.y);
})
.attr("r", 5)
.style("fill", function(d) {
return color(d.x);
});
// Append name as label to each node
group.append("text")
.attr("class", "text")
.attr("dy", "1.2em")
.text(function(d) {
return d.name;
})
.attr("x", function(d, i) {
return radius(d.y);
});
var width = 960,
height = 500,
innerRadius = 40,
outerRadius = 240;
var angle = d3.scale.ordinal().domain(d3.range(4)).rangePoints([0, 2 * Math.PI]),
radius = d3.scale.linear().range([innerRadius, outerRadius]),
color = d3.scale.category10().domain(d3.range(20));
var nodes = [{
name: "A",
group: 1,
x: 0,
y: .1
}, {
name: "B",
group: 1,
x: 0,
y: .9
}, {
name: "C",
group: 2,
x: 1,
y: .2
}, {
name: "D",
group: 2,
x: 1,
y: .3
}, {
name: "E",
group: 3,
x: 2,
y: .1
}, {
name: "F",
group: 3,
x: 2,
y: .8
}];
var links = [{
source: nodes[0],
target: nodes[2]
}, {
source: nodes[1],
target: nodes[3]
}, {
source: nodes[2],
target: nodes[4]
}, {
source: nodes[2],
target: nodes[5]
}, {
source: nodes[3],
target: nodes[5]
}, {
source: nodes[4],
target: nodes[0]
}, {
source: nodes[5],
target: nodes[1]
}];
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
svg.selectAll(".axis")
.data(d3.range(3))
.enter().append("line")
.attr("class", "axis")
.attr("transform", function(d) {
return "rotate(" + degrees(angle(d)) + ")";
})
.attr("x1", radius.range()[0])
.attr("x2", radius.range()[1]);
svg.selectAll(".link")
.data(links)
.enter().append("path")
.attr("class", "link")
.attr("d", d3.hive.link()
.angle(function(d) {
return angle(d.x);
})
.radius(function(d) {
return radius(d.y);
}))
.style("stroke", function(d) {
return color(d.source.x);
});
var nodes = svg.selectAll("g.node")
.data(nodes, function(d) {
return d.name;
});
var group = nodes.enter()
.append("g")
.attr("transform", function(d) {
return "rotate(" + degrees(angle(d.x)) + ")";
});
group.append("circle")
.attr("class", "node")
.attr("cx", function(d) {
return radius(d.y);
})
.attr("r", 5)
.style("fill", function(d) {
return color(d.x);
});
// Append name as label to each node
group.append("text")
.attr("class", "text")
.attr("dy", "1.2em")
.text(function(d) {
return d.name;
})
.attr("x", function(d, i) {
return radius(d.y);
});
function degrees(radians) {
return radians / Math.PI * 180 - 90;
}
.link {
fill: none;
stroke-width: 1.5px;
}
.axis,
.node {
stroke: #000;
stroke-width: 1.5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="http://d3js.org/d3.hive.v0.min.js"></script>