I'm drawing a sunburst chart with this example, http://www.jasondavies.com/coffee-wheel/. I find out most sunburst charts starting at 12 o'clock as 0 degree. How can I change the code to make it starts at 3 o'clock?
I tried to change the code:
x = d3.scale.linear().range([0, 2 * Math.PI]),
to
x = d3.scale.linear().range([90, 2 * Math.PI+90]),
but this doesn't work.
I would suggest rewritting parts of the example related to rendering.
Partition layout used in the example does not handle angles and rotations, they are derived by the code in the example. There are three rotations to deal with:
This is original cheese wheel adapted to be a code snippet:
var width = 840,
height = width,
radius = width / 2,
x = d3.scale.linear().range([0, 2 * Math.PI]),
y = d3.scale.pow().exponent(1.3).domain([0, 1]).range([0, radius]),
padding = 5,
duration = 1000;
var div = d3.select("body");
div.select("img").remove();
var vis = div.append("svg")
.attr("width", width + padding * 2)
.attr("height", height + padding * 2)
.append("g")
.attr("transform", "translate(" + [radius + padding, radius + padding] + ")");
div.append("p")
.attr("id", "intro")
.text("Click to zoom!");
var partition = d3.layout.partition()
.sort(null)
.value(function(d) {
return 5.8 - d.depth;
});
var arc = d3.svg.arc()
.startAngle(function(d) {
return Math.max(0, Math.min(2 * Math.PI, x(d.x)));
})
.endAngle(function(d) {
return Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx)));
})
.innerRadius(function(d) {
return Math.max(0, d.y ? y(d.y) : d.y);
})
.outerRadius(function(d) {
return Math.max(0, y(d.y + d.dy));
});
var json = getData();
var nodes = partition.nodes({ children: json });
var path = vis.selectAll("path").data(nodes);
path.enter().append("path")
.attr("id", function(d, i) {
return "path-" + i;
})
.attr("d", arc)
.attr("fill-rule", "evenodd")
.style("fill", colour)
.on("click", click);
var text = vis.selectAll("text").data(nodes);
var textEnter = text.enter().append("text")
.style("fill-opacity", 1)
.style("fill", function(d) {
return brightness(d3.rgb(colour(d))) < 125 ? "#eee" : "#000";
})
.attr("text-anchor", function(d) {
return x(d.x + d.dx / 2) > Math.PI ? "end" : "start";
})
.attr("dy", ".2em")
.attr("transform", function(d) {
var multiline = (d.name || "").split(" ").length > 1,
angle = x(d.x + d.dx / 2) * 180 / Math.PI - 90,
rotate = angle + (multiline ? -.5 : 0);
return "rotate(" + rotate + ")translate(" + (y(d.y) + padding) + ")rotate(" + (angle > 90 ? -180 : 0) + ")";
})
.on("click", click);
textEnter.append("tspan")
.attr("x", 0)
.text(function(d) {
return d.depth ? d.name.split(" ")[0] : "";
});
textEnter.append("tspan")
.attr("x", 0)
.attr("dy", "1em")
.text(function(d) {
return d.depth ? d.name.split(" ")[1] || "" : "";
});
function click(d) {
path.transition()
.duration(duration)
.attrTween("d", arcTween(d));
// Somewhat of a hack as we rely on arcTween updating the scales.
text.style("visibility", function(e) {
return isParentOf(d, e) ? null : d3.select(this).style("visibility");
})
.transition()
.duration(duration)
.attrTween("text-anchor", function(d) {
return function() {
return x(d.x + d.dx / 2) > Math.PI ? "end" : "start";
};
})
.attrTween("transform", function(d) {
var multiline = (d.name || "").split(" ").length > 1;
return function() {
var angle = x(d.x + d.dx / 2) * 180 / Math.PI - 90,
rotate = angle + (multiline ? -.5 : 0);
return "rotate(" + rotate + ")translate(" + (y(d.y) + padding) + ")rotate(" + (angle > 90 ? -180 : 0) + ")";
};
})
.style("fill-opacity", function(e) {
return isParentOf(d, e) ? 1 : 1e-6;
})
.each("end", function(e) {
d3.select(this).style("visibility", isParentOf(d, e) ? null : "hidden");
});
};
function isParentOf(p, c) {
if (p === c) return true;
if (p.children) {
return p.children.some(function(d) {
return isParentOf(d, c);
});
}
return false;
}
function colour(d) {
if (d.children) {
// There is a maximum of two children!
var colours = d.children.map(colour),
a = d3.hsl(colours[0]),
b = d3.hsl(colours[1]);
// L*a*b* might be better here...
return d3.hsl((a.h + b.h) / 2, a.s * 1.2, a.l / 1.2);
}
return d.colour || "#fff";
}
// Interpolate the scales!
function arcTween(d) {
var my = maxY(d),
xd = d3.interpolate(x.domain(), [d.x, d.x + d.dx]),
yd = d3.interpolate(y.domain(), [d.y, my]),
yr = d3.interpolate(y.range(), [d.y ? 20 : 0, radius]);
return function(d) {
return function(t) {
x.domain(xd(t));
y.domain(yd(t)).range(yr(t));
return arc(d);
};
};
}
function maxY(d) {
return d.children ? Math.max.apply(Math, d.children.map(maxY)) : d.y + d.dy;
}
// http://www.w3.org/WAI/ER/WD-AERT/#color-contrast
function brightness(rgb) {
return rgb.r * .299 + rgb.g * .587 + rgb.b * .114;
}
function getData(rgb) {
return [{
"name": "Aromas",
"children": [{
"name": "Enzymatic",
"children": [{
"name": "Flowery",
"children": [{
"name": "Floral",
"children": [{
"name": "Coffee Blossom",
"colour": "#f9f0ab"
}, {
"name": "Tea Rose",
"colour": "#e8e596"
}]
}, {
"name": "Fragrant",
"children": [{
"name": "Cardamon Caraway",
"colour": "#f0e2a3"
}, {
"name": "Coriander Seeds",
"colour": "#ede487"
}]
}]
}, {
"name": "Fruity",
"children": [{
"name": "Citrus",
"children": [{
"name": "Lemon",
"colour": "#efd580"
}, {
"name": "Apple",
"colour": "#f1cb82"
}]
}, {
"name": "Berry-like",
"children": [{
"name": "Apricot",
"colour": "#f1c298"
}, {
"name": "Blackberry",
"colour": "#e8b598"
}]
}]
}, {
"name": "Herby",
"children": [{
"name": "Alliaceous",
"children": [{
"name": "Onion",
"colour": "#d5dda1"
}, {
"name": "Garlic",
"colour": "#c9d2b5"
}]
}, {
"name": "Leguminous",
"children": [{
"name": "Cucumber",
"colour": "#aec1ad"
}, {
"name": "Garden Peas",
"colour": "#a7b8a8"
}]
}]
}]
}, {
"name": "Sugar Browning",
"children": [{
"name": "Nutty",
"children": [{
"name": "Nut-like",
"children": [{
"name": "Roasted Peanuts",
"colour": "#b49a3d"
}, {
"name": "Walnuts",
"colour": "#b28647"
}]
}, {
"name": "Malt-like",
"children": [{
"name": "Balsamic Rice",
"colour": "#a97d32"
}, {
"name": "Toast",
"colour": "#b68334"
}]
}]
}, {
"name": "Carmelly",
"children": [{
"name": "Candy-like",
"children": [{
"name": "Roasted Hazelnut",
"colour": "#d6a680"
}, {
"name": "Roasted Almond",
"colour": "#dfad70"
}]
}, {
"name": "Syrup-like",
"children": [{
"name": "Honey",
"colour": "#a2765d"
}, {
"name": "Maple Syrup",
"colour": "#9f6652"
}]
}]
}, {
"name": "Chocolatey",
"children": [{
"name": "Chocolate-like",
"children": [{
"name": "Bakers",
"colour": "#b9763f"
}, {
"name": "Dark Chocolate",
"colour": "#bf6e5d"
}]
}, {
"name": "Vanilla-like",
"children": [{
"name": "Swiss",
"colour": "#af643c"
}, {
"name": "Butter",
"colour": "#9b4c3f"
}]
}]
}]
}, {
"name": "Dry Distillation",
"children": [{
"name": "Resinous",
"children": [{
"name": "Turpeny",
"children": [{
"name": "Piney",
"colour": "#72659d"
}, {
"name": "Blackcurrant-like",
"colour": "#8a6e9e"
}]
}, {
"name": "Medicinal",
"children": [{
"name": "Camphoric",
"colour": "#8f5c85"
}, {
"name": "Cineolic",
"colour": "#934b8b"
}]
}]
}, {
"name": "Spicy",
"children": [{
"name": "Warming",
"children": [{
"name": "Cedar",
"colour": "#9d4e87"
}, {
"name": "Pepper",
"colour": "#92538c"
}]
}, {
"name": "Pungent",
"children": [{
"name": "Clove",
"colour": "#8b6397"
}, {
"name": "Thyme",
"colour": "#716084"
}]
}]
}, {
"name": "Carbony",
"children": [{
"name": "Smokey",
"children": [{
"name": "Tarry",
"colour": "#2e6093"
}, {
"name": "Pipe Tobacco",
"colour": "#3a5988"
}]
}, {
"name": "Ashy",
"children": [{
"name": "Burnt",
"colour": "#4a5072"
}, {
"name": "Charred",
"colour": "#393e64"
}]
}]
}]
}]
}, {
"name": "Tastes",
"children": [{
"name": "Bitter",
"children": [{
"name": "Pungent",
"children": [{
"name": "Creosol",
"colour": "#aaa1cc"
}, {
"name": "Phenolic",
"colour": "#e0b5c9"
}]
}, {
"name": "Harsh",
"children": [{
"name": "Caustic",
"colour": "#e098b0"
}, {
"name": "Alkaline",
"colour": "#ee82a2"
}]
}]
}, {
"name": "Salt",
"children": [{
"name": "Sharp",
"children": [{
"name": "Astringent",
"colour": "#ef91ac"
}, {
"name": "Rough",
"colour": "#eda994"
}]
}, {
"name": "Bland",
"children": [{
"name": "Neutral",
"colour": "#eeb798"
}, {
"name": "Soft",
"colour": "#ecc099"
}]
}]
}, {
"name": "Sweet",
"children": [{
"name": "Mellow",
"children": [{
"name": "Delicate",
"colour": "#f6d5aa"
}, {
"name": "Mild",
"colour": "#f0d48a"
}]
}, {
"name": "Acidy",
"children": [{
"name": "Nippy",
"colour": "#efd95f"
}, {
"name": "Piquant",
"colour": "#eee469"
}]
}]
}, {
"name": "Sour",
"children": [{
"name": "Winey",
"children": [{
"name": "Tangy",
"colour": "#dbdc7f"
}, {
"name": "Tart",
"colour": "#dfd961"
}]
}, {
"name": "Soury",
"children": [{
"name": "Hard",
"colour": "#ebe378"
}, {
"name": "Acrid",
"colour": "#f5e351"
}]
}]
}]
}];
};
body {
font-size: 1em;
font-weight: 400;
word-spacing: normal;
letter-spacing: normal;
text-transform: none;
font-family: Verdana, Myriad Web, Syntax, sans-serif;
font-size-adjust: .58;
color: #000;
background: #FFF;
line-height: 1.58em;
border-top: 0;
border-left: 0;
border-bottom: 0;
border-right: 0;
width: auto;
margin: 1.58em 5% 1.58em 8%;
padding: 0
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
Instead of this code:
.startAngle(function(d) {
return Math.max(0, Math.min(2 * Math.PI, x(d.x)));
})
.endAngle(function(d) {
return Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx)));
})
we need this:
.startAngle(function(d) {
return Math.PI/2 + Math.max(0, Math.min(2 * Math.PI, x(d.x)));
})
.endAngle(function(d) {
return Math.PI/2 + Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx)));
})
(Math.PI/2 is added to the both return values)
The result is:
Similar changes for rotating text, see the code in the snippet below..
var width = 840,
height = width,
radius = width / 2,
x = d3.scale.linear().range([0, 2 * Math.PI]),
y = d3.scale.pow().exponent(1.3).domain([0, 1]).range([0, radius]),
padding = 5,
duration = 1000;
var div = d3.select("body");
div.select("img").remove();
var vis = div.append("svg")
.attr("width", width + padding * 2)
.attr("height", height + padding * 2)
.append("g")
.attr("transform", "translate(" + [radius + padding, radius + padding] + ")");
div.append("p")
.attr("id", "intro")
.text("Click to zoom!");
var partition = d3.layout.partition()
.sort(null)
.value(function(d) {
return 5.8 - d.depth;
});
var arc = d3.svg.arc()
.startAngle(function(d) {
return Math.PI/2 + Math.max(0, Math.min(2 * Math.PI, x(d.x)));
})
.endAngle(function(d) {
return Math.PI/2 + Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx)));
})
.innerRadius(function(d) {
return Math.max(0, d.y ? y(d.y) : d.y);
})
.outerRadius(function(d) {
return Math.max(0, y(d.y + d.dy));
});
var json = getData();
var nodes = partition.nodes({ children: json });
var path = vis.selectAll("path").data(nodes);
path.enter().append("path")
.attr("id", function(d, i) {
return "path-" + i;
})
.attr("d", arc)
.attr("fill-rule", "evenodd")
.style("fill", colour)
.on("click", click);
var text = vis.selectAll("text").data(nodes);
var textEnter = text.enter().append("text")
.style("fill-opacity", 1)
.style("fill", function(d) {
return brightness(d3.rgb(colour(d))) < 125 ? "#eee" : "#000";
})
.attr("text-anchor", function(d) {
return ((x(d.x + d.dx / 2) > Math.PI/2)&&(x(d.x + d.dx / 2) < 3*Math.PI/2)) ? "end" : "start";
})
.attr("dy", ".2em")
.attr("transform", function(d) {
var multiline = (d.name || "").split(" ").length > 1,
angle = x(d.x + d.dx / 2) * 180 / Math.PI,
rotate = angle + (multiline ? -.5 : 0);
return "rotate(" + rotate + ")translate(" + (y(d.y) + padding) + ")rotate(" + ((angle > 90) && (angle < 270) ? -180 : 0) + ")";
})
.on("click", click);
textEnter.append("tspan")
.attr("x", 0)
.text(function(d) {
return d.depth ? d.name.split(" ")[0] : "";
});
textEnter.append("tspan")
.attr("x", 0)
.attr("dy", "1em")
.text(function(d) {
return d.depth ? d.name.split(" ")[1] || "" : "";
});
function click(d) {
path.transition()
.duration(duration)
.attrTween("d", arcTween(d));
// Somewhat of a hack as we rely on arcTween updating the scales.
text.style("visibility", function(e) {
return isParentOf(d, e) ? null : d3.select(this).style("visibility");
})
.transition()
.duration(duration)
.attrTween("text-anchor", function(d) {
return function() {
return ((x(d.x + d.dx / 2) > Math.PI/2)&&(x(d.x + d.dx / 2) < 3*Math.PI/2)) ? "end" : "start";
};
})
.attrTween("transform", function(d) {
var multiline = (d.name || "").split(" ").length > 1;
return function() {
var angle = x(d.x + d.dx / 2) * 180 / Math.PI,
rotate = angle + (multiline ? -.5 : 0);
// rotate = ((angle > 2*Math.PI) ? angle-2*Math.PI : angle) + (multiline ? -.5 : 0);
// if(d.level==1) rotate = 0;
// return "rotate(" + rotate + ")translate(" + (y(d.y) + padding) + ")rotate(" + (angle > 180 ? -180 : 0) + ")";
return "rotate(" + rotate + ")translate(" + (y(d.y) + padding) + ")rotate(" + ((angle > 90) && (angle < 270) ? -180 : 0) + ")";
};
})
.style("fill-opacity", function(e) {
return isParentOf(d, e) ? 1 : 1e-6;
})
.each("end", function(e) {
d3.select(this).style("visibility", isParentOf(d, e) ? null : "hidden");
});
};
function isParentOf(p, c) {
if (p === c) return true;
if (p.children) {
return p.children.some(function(d) {
return isParentOf(d, c);
});
}
return false;
}
function colour(d) {
if (d.children) {
// There is a maximum of two children!
var colours = d.children.map(colour),
a = d3.hsl(colours[0]),
b = d3.hsl(colours[1]);
// L*a*b* might be better here...
return d3.hsl((a.h + b.h) / 2, a.s * 1.2, a.l / 1.2);
}
return d.colour || "#fff";
}
// Interpolate the scales!
function arcTween(d) {
var my = maxY(d),
xd = d3.interpolate(x.domain(), [d.x, d.x + d.dx]),
yd = d3.interpolate(y.domain(), [d.y, my]),
yr = d3.interpolate(y.range(), [d.y ? 20 : 0, radius]);
return function(d) {
return function(t) {
x.domain(xd(t));
y.domain(yd(t)).range(yr(t));
return arc(d);
};
};
}
function maxY(d) {
return d.children ? Math.max.apply(Math, d.children.map(maxY)) : d.y + d.dy;
}
// http://www.w3.org/WAI/ER/WD-AERT/#color-contrast
function brightness(rgb) {
return rgb.r * .299 + rgb.g * .587 + rgb.b * .114;
}
function getData(rgb) {
return [{
"name": "Aromas",
"children": [{
"name": "Enzymatic",
"children": [{
"name": "Flowery",
"children": [{
"name": "Floral",
"children": [{
"name": "Coffee Blossom",
"colour": "#f9f0ab"
}, {
"name": "Tea Rose",
"colour": "#e8e596"
}]
}, {
"name": "Fragrant",
"children": [{
"name": "Cardamon Caraway",
"colour": "#f0e2a3"
}, {
"name": "Coriander Seeds",
"colour": "#ede487"
}]
}]
}, {
"name": "Fruity",
"children": [{
"name": "Citrus",
"children": [{
"name": "Lemon",
"colour": "#efd580"
}, {
"name": "Apple",
"colour": "#f1cb82"
}]
}, {
"name": "Berry-like",
"children": [{
"name": "Apricot",
"colour": "#f1c298"
}, {
"name": "Blackberry",
"colour": "#e8b598"
}]
}]
}, {
"name": "Herby",
"children": [{
"name": "Alliaceous",
"children": [{
"name": "Onion",
"colour": "#d5dda1"
}, {
"name": "Garlic",
"colour": "#c9d2b5"
}]
}, {
"name": "Leguminous",
"children": [{
"name": "Cucumber",
"colour": "#aec1ad"
}, {
"name": "Garden Peas",
"colour": "#a7b8a8"
}]
}]
}]
}, {
"name": "Sugar Browning",
"children": [{
"name": "Nutty",
"children": [{
"name": "Nut-like",
"children": [{
"name": "Roasted Peanuts",
"colour": "#b49a3d"
}, {
"name": "Walnuts",
"colour": "#b28647"
}]
}, {
"name": "Malt-like",
"children": [{
"name": "Balsamic Rice",
"colour": "#a97d32"
}, {
"name": "Toast",
"colour": "#b68334"
}]
}]
}, {
"name": "Carmelly",
"children": [{
"name": "Candy-like",
"children": [{
"name": "Roasted Hazelnut",
"colour": "#d6a680"
}, {
"name": "Roasted Almond",
"colour": "#dfad70"
}]
}, {
"name": "Syrup-like",
"children": [{
"name": "Honey",
"colour": "#a2765d"
}, {
"name": "Maple Syrup",
"colour": "#9f6652"
}]
}]
}, {
"name": "Chocolatey",
"children": [{
"name": "Chocolate-like",
"children": [{
"name": "Bakers",
"colour": "#b9763f"
}, {
"name": "Dark Chocolate",
"colour": "#bf6e5d"
}]
}, {
"name": "Vanilla-like",
"children": [{
"name": "Swiss",
"colour": "#af643c"
}, {
"name": "Butter",
"colour": "#9b4c3f"
}]
}]
}]
}, {
"name": "Dry Distillation",
"children": [{
"name": "Resinous",
"children": [{
"name": "Turpeny",
"children": [{
"name": "Piney",
"colour": "#72659d"
}, {
"name": "Blackcurrant-like",
"colour": "#8a6e9e"
}]
}, {
"name": "Medicinal",
"children": [{
"name": "Camphoric",
"colour": "#8f5c85"
}, {
"name": "Cineolic",
"colour": "#934b8b"
}]
}]
}, {
"name": "Spicy",
"children": [{
"name": "Warming",
"children": [{
"name": "Cedar",
"colour": "#9d4e87"
}, {
"name": "Pepper",
"colour": "#92538c"
}]
}, {
"name": "Pungent",
"children": [{
"name": "Clove",
"colour": "#8b6397"
}, {
"name": "Thyme",
"colour": "#716084"
}]
}]
}, {
"name": "Carbony",
"children": [{
"name": "Smokey",
"children": [{
"name": "Tarry",
"colour": "#2e6093"
}, {
"name": "Pipe Tobacco",
"colour": "#3a5988"
}]
}, {
"name": "Ashy",
"children": [{
"name": "Burnt",
"colour": "#4a5072"
}, {
"name": "Charred",
"colour": "#393e64"
}]
}]
}]
}]
}, {
"name": "Tastes",
"children": [{
"name": "Bitter",
"children": [{
"name": "Pungent",
"children": [{
"name": "Creosol",
"colour": "#aaa1cc"
}, {
"name": "Phenolic",
"colour": "#e0b5c9"
}]
}, {
"name": "Harsh",
"children": [{
"name": "Caustic",
"colour": "#e098b0"
}, {
"name": "Alkaline",
"colour": "#ee82a2"
}]
}]
}, {
"name": "Salt",
"children": [{
"name": "Sharp",
"children": [{
"name": "Astringent",
"colour": "#ef91ac"
}, {
"name": "Rough",
"colour": "#eda994"
}]
}, {
"name": "Bland",
"children": [{
"name": "Neutral",
"colour": "#eeb798"
}, {
"name": "Soft",
"colour": "#ecc099"
}]
}]
}, {
"name": "Sweet",
"children": [{
"name": "Mellow",
"children": [{
"name": "Delicate",
"colour": "#f6d5aa"
}, {
"name": "Mild",
"colour": "#f0d48a"
}]
}, {
"name": "Acidy",
"children": [{
"name": "Nippy",
"colour": "#efd95f"
}, {
"name": "Piquant",
"colour": "#eee469"
}]
}]
}, {
"name": "Sour",
"children": [{
"name": "Winey",
"children": [{
"name": "Tangy",
"colour": "#dbdc7f"
}, {
"name": "Tart",
"colour": "#dfd961"
}]
}, {
"name": "Soury",
"children": [{
"name": "Hard",
"colour": "#ebe378"
}, {
"name": "Acrid",
"colour": "#f5e351"
}]
}]
}]
}];
};
body {
font-size: 1em;
font-weight: 400;
word-spacing: normal;
letter-spacing: normal;
text-transform: none;
font-family: Verdana, Myriad Web, Syntax, sans-serif;
font-size-adjust: .58;
color: #000;
background: #FFF;
line-height: 1.58em;
border-top: 0;
border-left: 0;
border-bottom: 0;
border-right: 0;
width: auto;
margin: 1.58em 5% 1.58em 8%;
padding: 0
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>