How to add day numbers to D3 calendar heatmap? - d3.js
I'm working with a modified version of Mike Bostock's Calendar Heatmap (https://bl.ocks.org/mbostock/4063318). In this version, I rotated the calendar and I'm just focusing on a few months (this was helpful: Tweak D3 calendar view example to be vertical with days be ordered left to right, then top to bottom?). See the attached image.
Does anyone have ideas on how to add small font day numbers to each of the day rectangles? They would be font size 8, and in the corners of each day's rectangle. I've been fiddling with it for hours and haven't gotten anywhere.
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var width = 550,
height = 750,
cellSize = 45;
var formatPercent = d3.format(".1%");
var color = d3.scaleQuantize()
.domain([0, 100])
.range(["#ffffff","#e6f7ff","#b3e6ff","#99ddff","#66ccff","#4dc3ff","#1ab2ff","#0077b3","#004466"]);
var month_strings = ["January", "February", "March"]
var svg = d3.select("body")
.selectAll("svg")
.data([2018])
.enter().append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + ((width - cellSize * 5) / 2) + "," + (height - cellSize * 16 - 1) + ")");
svg.append("text")
.attr("transform", "translate(-10," + cellSize * 3 + ")rotate(-90)")
.attr("font-family", "sans-serif")
.attr("font-size", 20)
.attr("text-anchor", "middle")
.text(month_strings[0]);
svg.append("text")
.attr("transform", "translate(-10," + cellSize * 7 + ")rotate(-90)")
.attr("font-family", "sans-serif")
.attr("font-size", 20)
.attr("text-anchor", "middle")
.text(month_strings[1]);
svg.append("text")
.attr("transform", "translate(-10," + cellSize * 11 + ")rotate(-90)")
.attr("font-family", "sans-serif")
.attr("font-size", 20)
.attr("text-anchor", "middle")
.text(month_strings[2]);
var rect = svg.append("g")
.attr("fill", "none")
.attr("stroke", "#d2d4d8")
.selectAll("rect")
.data(function(d) { return d3.timeDays(new Date(2018, 0, 1), new Date(2018, 3, 1)); })
.enter().append("rect")
.attr("width", cellSize)
.attr("height", cellSize)
.attr("class", "hour bordered")
.attr("rx", 4)
.attr("ry", 4)
//.attr("x", function(d) { return d3.timeWeek.count(d3.timeYear(d), d) * cellSize; })
//.attr("y", function(d) { return d.getDay() * cellSize; })
.attr("x", function(d) { return d.getDay() * cellSize;})
.attr("y", function(d) { return d3.timeWeek.count(d3.timeYear(d), d) * cellSize; })
.datum(d3.timeFormat("%Y-%m-%d"));
svg.append("g")
.attr("fill", "none")
.attr("stroke", "#000")
.selectAll("path")
.data(function(d) { return d3.timeMonths(new Date(2018, 0, 1), new Date(2018, 3, 1)); })
.enter().append("path")
.attr("d", pathMonth);
d3.csv("static/test.csv", function(error, csv) {
if (error) throw error;
var data = d3.nest()
.key(function(d) { return d.Date; })
.rollup(function(d) { return (d[0].Close - d[0].Open) / d[0].Open; })
.object(csv);
rect.filter(function(d) { return d in data; })
.attr("fill", function(d) { return color(data[d]); })
.append("title")
.text(function(d) { return d + ": " + formatPercent(data[d]); });
});
function pathMonth(t0) {
var t1 = new Date(t0.getFullYear(), t0.getMonth() + 1, 0),
d0 = t0.getDay(), w0 = d3.timeWeek.count(d3.timeYear(t0), t0),
d1 = t1.getDay(), w1 = d3.timeWeek.count(d3.timeYear(t1), t1);
return "M" + d0 * cellSize + "," + (w0) * cellSize
+ "H" + 7 * cellSize + "V" + (w1) * cellSize
+ "H" + (d1 + 1) * cellSize + "V" + (w1 + 1) * cellSize
+ "H" + 0 + "V" + (w0 + 1) * cellSize + "H"
+ d0 * cellSize + "Z";
}
var start_box = svg.append("rect")
.attr("x", 225)
.attr("y", 45)
.attr("width", cellSize)
.attr("height", cellSize)
.attr("rx", 4)
.attr("ry", 4)
.attr("class", "hour bordered")
.style("fill", "#FFD700");
</script>
Here is the CSV:
Date,Weekday,Open,Close
2018-01-01,0,1,0
2018-01-02,1,1,1
2018-01-03,2,1,2
2018-01-04,3,1,3
2018-01-05,4,1,4
2018-01-06,5,1,1
2018-01-07,6,1,1
2018-01-08,0,1,7
2018-01-09,1,1,8
2018-01-10,2,1,9
2018-01-11,3,1,10
2018-01-12,4,1,11
2018-01-13,5,1,1
2018-01-14,6,1,1
2018-01-15,0,1,14
2018-01-16,1,1,15
2018-01-17,2,1,16
2018-01-18,3,1,17
2018-01-19,4,1,18
2018-01-20,5,1,1
2018-01-21,6,1,1
2018-01-22,0,1,21
2018-01-23,1,1,22
2018-01-24,2,1,23
2018-01-25,3,1,24
2018-01-26,4,1,25
2018-01-27,5,1,1
2018-01-28,6,1,1
2018-01-29,0,1,28
2018-01-30,1,1,29
2018-01-31,2,1,30
2018-02-01,3,1,31
2018-02-02,4,1,32
2018-02-03,5,1,1
2018-02-04,6,1,1
2018-02-05,0,1,35
2018-02-06,1,1,36
2018-02-07,2,1,37
2018-02-08,3,1,38
2018-02-09,4,1,39
2018-02-10,5,1,1
2018-02-11,6,1,1
2018-02-12,0,1,42
2018-02-13,1,1,43
2018-02-14,2,1,44
2018-02-15,3,1,45
2018-02-16,4,1,46
2018-02-17,5,1,1
2018-02-18,6,1,1
2018-02-19,0,1,49
2018-02-20,1,1,50
2018-02-21,2,1,51
2018-02-22,3,1,52
2018-02-23,4,1,53
2018-02-24,5,1,1
2018-02-25,6,1,1
2018-02-26,0,1,56
2018-02-27,1,1,57
2018-02-28,2,1,58
2018-03-01,3,1,59
2018-03-02,4,1,60
2018-03-03,5,1,1
2018-03-04,6,1,1
2018-03-05,0,1,63
2018-03-06,1,1,64
2018-03-07,2,1,65
2018-03-08,3,1,66
2018-03-09,4,1,67
2018-03-10,5,1,1
2018-03-11,6,1,1
2018-03-12,0,1,70
2018-03-13,1,1,71
2018-03-14,2,1,72
2018-03-15,3,1,73
2018-03-16,4,1,74
2018-03-17,5,1,1
2018-03-18,6,1,1
2018-03-19,0,1,77
2018-03-20,1,1,78
2018-03-21,2,1,79
2018-03-22,3,1,80
2018-03-23,4,1,81
2018-03-24,5,1,1
2018-03-25,6,1,1
2018-03-26,0,1,84
2018-03-27,1,1,85
2018-03-28,2,1,86
2018-03-29,3,1,87
2018-03-30,4,1,88
2018-03-31,5,1,1
I'd do it like this:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
<script src="script.js"></script>
</head>
<body>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var width = 550,
height = 750,
cellSize = 45;
var formatPercent = d3.format(".1%");
var color = d3.scaleQuantize()
.domain([0, 100])
.range(["#ffffff", "#e6f7ff", "#b3e6ff", "#99ddff", "#66ccff", "#4dc3ff", "#1ab2ff", "#0077b3", "#004466"]);
var month_strings = ["January", "February", "March"]
var svg = d3.select("body")
.selectAll("svg")
.data([2018])
.enter().append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + ((width - cellSize * 5) / 2) + "," + (height - cellSize * 16 - 1) + ")");
svg.append("text")
.attr("transform", "translate(-10," + cellSize * 3 + ")rotate(-90)")
.attr("font-family", "sans-serif")
.attr("font-size", 20)
.attr("text-anchor", "middle")
.text(month_strings[0]);
svg.append("text")
.attr("transform", "translate(-10," + cellSize * 7 + ")rotate(-90)")
.attr("font-family", "sans-serif")
.attr("font-size", 20)
.attr("text-anchor", "middle")
.text(month_strings[1]);
svg.append("text")
.attr("transform", "translate(-10," + cellSize * 11 + ")rotate(-90)")
.attr("font-family", "sans-serif")
.attr("font-size", 20)
.attr("text-anchor", "middle")
.text(month_strings[2]);
var g = svg.append("g")
.attr("fill", "none")
.attr("stroke", "#d2d4d8")
.selectAll("g")
.data(function(d) {
return d3.timeDays(new Date(2018, 0, 1), new Date(2018, 3, 1));
})
.enter()
.append("g")
.attr("transform", function(d){
var x = d.getDay() * cellSize,
y = d3.timeWeek.count(d3.timeYear(d), d) * cellSize;
return "translate(" + x + "," + y + ")";
})
g.append("rect")
.attr("width", cellSize)
.attr("height", cellSize)
.attr("class", "hour bordered")
.attr("rx", 4)
.attr("ry", 4)
.datum(d3.timeFormat("%Y-%m-%d"));
g.append("text")
.text(function(d){
return d.getDate();
})
.attr("y", cellSize)
.style("font-family", "arial")
.style("font-size", "8pt")
svg.append("g")
.attr("fill", "none")
.attr("stroke", "#000")
.selectAll("path")
.data(function(d) {
return d3.timeMonths(new Date(2018, 0, 1), new Date(2018, 3, 1));
})
.enter().append("path")
.attr("d", pathMonth);
d3.csv("static/test.csv", function(error, csv) {
if (error) throw error;
var data = d3.nest()
.key(function(d) {
return d.Date;
})
.rollup(function(d) {
return (d[0].Close - d[0].Open) / d[0].Open;
})
.object(csv);
rect.filter(function(d) {
return d in data;
})
.attr("fill", function(d) {
return color(data[d]);
})
.append("title")
.text(function(d) {
return d + ": " + formatPercent(data[d]);
});
});
function pathMonth(t0) {
var t1 = new Date(t0.getFullYear(), t0.getMonth() + 1, 0),
d0 = t0.getDay(),
w0 = d3.timeWeek.count(d3.timeYear(t0), t0),
d1 = t1.getDay(),
w1 = d3.timeWeek.count(d3.timeYear(t1), t1);
return "M" + d0 * cellSize + "," + (w0) * cellSize + "H" + 7 * cellSize + "V" + (w1) * cellSize + "H" + (d1 + 1) * cellSize + "V" + (w1 + 1) * cellSize + "H" + 0 + "V" + (w0 + 1) * cellSize + "H" + d0 * cellSize + "Z";
}
var start_box = svg.append("rect")
.attr("x", 225)
.attr("y", 45)
.attr("width", cellSize)
.attr("height", cellSize)
.attr("rx", 4)
.attr("ry", 4)
.attr("class", "hour bordered")
.style("fill", "#FFD700");
</script>
</body>
</html>
Related
D3 updated data not propagated to mouseover event
I have a multi series line chart where I can choose to display data based on selected range of years. My line charts is working perfectly fine except for the tooltips, it just doesn't get updated! As you can see from above the tooltips display 1994~2013 instead of 2002~2008. var crimeRate = multiLineChart.selectAll(".crimeRate") .data(rateCol); //console.log("new_data", rateCol); crimeRate.exit().remove(); var crimeRateEnter = crimeRate.enter().append("g") .attr("class", "crimeRate"); crimeRateEnter.append("path") .attr("class", "line") .attr("d", function(d) { return line(d.values); }) .style("stroke", function(d) { return z(d.id); }) .on("mouseover", function(d) { var xPosition = d3.mouse(this)[0]; var yPosition = d3.mouse(this)[1] - 30; //console.log("Mouse X =", d3.mouse(this)[0], "\nMouse Y =", d3.mouse(this)[1]); //Prevent tooltips from going out of screen if (d3.mouse(this)[0] > (0.9 * svgLine.attr("width"))) { xPosition = xPosition - 0.05 * svgLine.attr("width"); } if (d3.mouse(this)[1] < (0.1 * svgLine.attr("height"))) { yPosition = yPosition + (0.1 * svgLine.attr("height")) + 50; } //console.log("is it correct?", rateCol); var currentCrimeType = d.id, currentMinYear = getCrimeMinYear(currentCrimeType), currentMaxYear = getCrimeMaxYear(currentCrimeType), diffMinMax = getCrimeRateDiff(currentCrimeType, currentMinYear, currentMaxYear), percDiff = diffMinMax[0], rateMin = diffMinMax[1], rateMax = diffMinMax[2]; var boxXPos = xPosition - 80; var boxYPos = yPosition - 60; multiLineChart.append("rect") .attr("class", "tooltipBox") .attr("rx", 10) .attr("ry", 10) .attr("id", "textBox") .attr("x", boxXPos) .attr("y", boxYPos) .attr("width", 180) .attr("height", 75) .attr("stroke", "black") .style("fill", "white") .style("opacity", 0.7); multiLineChart.append("text") .attr("class", "tooltipText") .attr("id", "tooltip") .attr("x", boxXPos + 10) .attr("y", boxYPos + 10) .attr("text-anchor", "middle") .attr("font-family", "Open Sans") .attr("font-size", "16px") .attr("fill", "black") .attr("text-anchor", "start") .append("tspan") .attr("font-weight", "bold") .attr("font-style", "italic") .attr("font-size", "18px") .attr("float", "left") .attr("x", boxXPos + 10) .attr("y", boxYPos + 20) .text(getFullName(d)) .append("tspan") .attr("float", "left") .attr("font-weight", "normal") .attr("font-style", "normal") .attr("font-size", "16px") .attr("x", boxXPos + 10) .attr("y", boxYPos + 35) .text("Dropped by " + percDiff + "%") .append("tspan") .attr("x", boxXPos + 10) .attr("y", boxYPos + 50) .text("From " + rateMin + " to " + rateMax) .append("tspan") .attr("x", boxXPos + 10) .attr("y", boxYPos + 65) .text("In year " + currentMinYear + " to " + currentMaxYear); d3.select(this) .style("stroke", "#FFFF00") .style("stroke-width", "5px"); }) .on("mouseout", function(d) { d3.select("#tooltip").remove(); d3.select("#textBox").remove(); d3.select(this) .style("stroke", function(d) { return z(d.id); }) .style("stroke-width", "2px") }); crimeRate.select("path") .transition() .duration(1000) .attr("d", function(d) { return line(d.values); }); Codes above are responsible for the line and mouseover events. Any help is appreciated, Thanks in advance!
After hours of researching, I found a solution. For anyone having the same problem and stumble upon this, I solved it by separating the mouseover function and I select the "path" element and specify the mouseover event here. Code Snippet: var crimeRate = multiLineChart.selectAll(".crimeRate") .data(rateCol); //console.log("new_data", rateCol); crimeRate.exit().remove(); var crimeRateEnter = crimeRate.enter().append("g") .attr("class", "crimeRate"); crimeRateEnter.append("path") .attr("class", "line") .attr("d", function(d) {return line(d.values); }) .style("stroke", function(d) { return z(d.id); }); crimeRate.select("path") .transition() .duration(1000) .attr("d", function(d) { return line(d.values); }); crimeRate.select("path") .on("mouseover", function(d) { var xPosition = d3.mouse(this)[0]; var yPosition = d3.mouse(this)[1] - 30; //console.log("Mouse X =", d3.mouse(this)[0], "\nMouse Y =", d3.mouse(this)[1]); //Prevent tooltips from going out of screen if (d3.mouse(this)[0] > (0.9*svgLine.attr("width"))){ xPosition = xPosition - 0.05 * svgLine.attr("width"); } if (d3.mouse(this)[1] < (0.1*svgLine.attr("height"))){ yPosition = yPosition + (0.1*svgLine.attr("height")) + 50; } //console.log("is it correct?", rateCol); var currentCrimeType = d.id, currentMinYear = getCrimeMinYear(currentCrimeType), currentMaxYear = getCrimeMaxYear(currentCrimeType), diffMinMax = getCrimeRateDiff(currentCrimeType,currentMinYear,currentMaxYear), percDiff = diffMinMax[0], rateMin = diffMinMax[1], rateMax = diffMinMax[2]; var boxXPos = xPosition - 80; var boxYPos = yPosition - 60; multiLineChart.append("rect") .attr("class", "tooltipBox") .attr("rx", 10) .attr("ry", 10) .attr("id","textBox") .attr("x", boxXPos) .attr("y", boxYPos) .attr("width", 180) .attr("height",75) .attr("stroke","black") .style("fill","white") .style("opacity", 0.7); multiLineChart.append("text") .attr("class", "tooltipText") .attr("id", "tooltip") .attr("x", boxXPos + 10) .attr("y", boxYPos + 10) .attr("text-anchor", "middle") .attr("font-family", "Open Sans") .attr("font-size", "16px") .attr("fill", "black") .attr("text-anchor","start") .append("tspan") .attr("font-weight", "bold") .attr("font-style", "italic") .attr("font-size", "18px") .attr("float","left") .attr("x", boxXPos + 10) .attr("y", boxYPos + 20) .text(getFullName(d)) .append("tspan") .attr("float","left") .attr("font-weight", "normal") .attr("font-style", "normal") .attr("font-size", "16px") .attr("x", boxXPos + 10) .attr("y", boxYPos + 35) .text("Dropped by " + percDiff + "%") .append("tspan") .attr("x", boxXPos + 10) .attr("y", boxYPos + 50) .text("From " + rateMin + " to " + rateMax) .append("tspan") .attr("x", boxXPos + 10) .attr("y", boxYPos + 65) .text("In year " + currentMinYear + " to " + currentMaxYear); d3.select(this) .style("stroke", "#FFFF00") .style("stroke-width", "5px"); }) .on("mouseout", function(d) { d3.select("#tooltip").remove(); d3.select("#textBox").remove(); d3.select(this) .style("stroke", function(d) { return z(d.id); }) .style("stroke-width", "2px") });
d3 heatmap chart draw data based on a filter
I am referring this to draw a chart. I want to draw a month vs time of the day chart.From what I infer from this link is that it draws data like in a matrix format (1,1),(1,2) ... What I want is to show Jan Feb ... Dec on x axis and 0:00,1:00 .... 24:00 on y axis. I have a month filter so an user can select from Mar to Jun if he wants to. How can I modify this code to suit my needs? const margin = { top: 50, right: 0, bottom: 100, left: 30 }, width = 960 - margin.left - margin.right, height = 430 - margin.top - margin.bottom, gridSize = Math.floor(width / 24), legendElementWidth = gridSize*2, buckets = 9, colors = ["#ffffd9","#edf8b1","#c7e9b4","#7fcdbb","#41b6c4","#1d91c0","#225ea8","#253494","#081d58"], // alternatively colorbrewer.YlGnBu[9] days = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul","Aug","Sep","Oct","Nov","Dec"], times = ["1a", "2a", "3a", "4a", "5a", "6a", "7a", "8a", "9a", "10a", "11a", "12a", "1p", "2p", "3p", "4p", "5p", "6p", "7p", "8p", "9p", "10p", "11p", "12p"]; datasets = ["data.tsv", "data2.tsv"]; const svg = d3.select("#chart").append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); const dayLabels = svg.selectAll(".dayLabel") .data(days) .enter().append("text") .text(function (d) { return d; }) .attr("x", 0) .attr("y", (d, i) => i * gridSize) .style("text-anchor", "end") .attr("transform", "translate(-6," + gridSize / 1.5 + ")") .attr("class", (d, i) => ((i >= 0 && i <= 4) ? "dayLabel mono axis axis-workweek" : "dayLabel mono axis")); const timeLabels = svg.selectAll(".timeLabel") .data(times) .enter().append("text") .text((d) => d) .attr("x", (d, i) => i * gridSize) .attr("y", 0) .style("text-anchor", "middle") .attr("transform", "translate(" + gridSize / 2 + ", -6)") .attr("class", (d, i) => ((i >= 7 && i <= 16) ? "timeLabel mono axis axis-worktime" : "timeLabel mono axis")); const type = (d) => { return { day: +d.day, hour: +d.hour, value: +d.value }; }; const heatmapChart = function(tsvFile) { d3.tsv(tsvFile, type, (error, data) => { const colorScale = d3.scaleQuantile() .domain([0, buckets - 1, d3.max(data, (d) => d.value)]) .range(colors); const cards = svg.selectAll(".hour") .data(data, (d) => d.day+':'+d.hour); cards.append("title"); cards.enter().append("rect") .attr("x", (d) => (d.hour - 1) * gridSize) .attr("y", (d) => (d.day - 1) * gridSize) .attr("rx", 4) .attr("ry", 4) .attr("class", "hour bordered") .attr("width", gridSize) .attr("height", gridSize) .style("fill", colors[0]) .merge(cards) .transition() .duration(1000) .style("fill", (d) => colorScale(d.value)); cards.select("title").text((d) => d.value); cards.exit().remove(); const legend = svg.selectAll(".legend") .data([0].concat(colorScale.quantiles()), (d) => d); const legend_g = legend.enter().append("g") .attr("class", "legend"); legend_g.append("rect") .attr("x", (d, i) => legendElementWidth * i) .attr("y", height) .attr("width", legendElementWidth) .attr("height", gridSize / 2) .style("fill", (d, i) => colors[i]); legend_g.append("text") .attr("class", "mono") .text((d) => "≥ " + Math.round(d)) .attr("x", (d, i) => legendElementWidth * i) .attr("y", height + gridSize); legend.exit().remove(); }); }; heatmapChart(datasets[0]); const datasetpicker = d3.select("#dataset-picker") .selectAll(".dataset-button") .data(datasets); datasetpicker.enter() .append("input") .attr("value", (d) => "Dataset " + d) .attr("type", "button") .attr("class", "dataset-button") .on("click", (d) => heatmapChart(d));
How use text variable for label d3 graphic
I am learning d3, i use the "dataset" variable for the values of the graphic, i wish use dataset.nombre values for label the d3 graphic, but i can only use dataset.numbers, this is a simplified code version: <body> <div id="container2"></div> <script> var dataset = { numbers: [15, 3, 10, 2, 14,17,1], nombre:["a","b","c","d","e","f","g"] }; var color = d3.scale.category20(); var pie = d3.layout.pie() .sort(null); var piedata = pie(dataset.apples); var arc = d3.svg.arc() .innerRadius(radius - 100) .outerRadius(radius - 50); var svg = d3.select("#container2").append("svg") .attr("width", width) .attr("height", height) .append("g") .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")"); var path = svg.selectAll("path") .data(piedata) .enter().append("path") .attr("fill", function(d, i) { return color(i); }) .attr("d", arc); svg.selectAll("text").data(piedata) .enter() .append("text") .attr("text-anchor", "middle") .attr("x", function(d) { var a = d.startAngle + (d.endAngle - d.startAngle)/2 - Math.PI/2; d.cx = Math.cos(a) * (radius - 75); return d.x = Math.cos(a) * (radius - 20); }) .attr("y", function(d) { var a = d.startAngle + (d.endAngle - d.startAngle)/2 - Math.PI/2; d.cy = Math.sin(a) * (radius - 75); return d.y = Math.sin(a) * (radius - 20); }) .text(function(d) { return d.value; }) .each(function(d) { var bbox = this.getBBox(); d.sx = d.x - bbox.width/2 - 2; d.ox = d.x + bbox.width/2 + 2; d.sy = d.oy = d.y + 5; }); svg.append("defs").append("marker") .attr("id", "circ") .attr("markerWidth", 6) .attr("markerHeight", 6) .attr("refX", 3) .attr("refY", 3) .append("circle") .attr("cx", 3) .attr("cy", 3) .attr("r", 3); svg.selectAll("path.pointer").data(piedata).enter() .append("path") .attr("class", "pointer") .style("fill", "none") .style("stroke", "black") .attr("marker-end", "url(#circ)") .attr("d", function(d) { if(d.cx > d.ox) { return "M" + d.sx + "," + d.sy + "L" + d.ox + "," + d.oy + " " + d.cx + "," + d.cy; } else { return "M" + d.ox + "," + d.oy + "L" + d.sx + "," + d.sy + " " + d.cx + "," + d.cy; } }); </script> </body>
Ideally you'd have objects instead of numbers as the data you pass to the pie layout; then you'd be able to use the data directly. In your case, you can still do so by using the index: .text(function(d, i) { return dataset.nombre[i]; }) Complete demo here.
Label of donut chart is too long
My problem is labels of donut chart too long. It'll be cut lost when it get over width or height of svg. i don't know how i must cut it to 2 or more line. I try to add tag div outside tag text but it's wrong. Who can give me a solution. This is my code: var tooltip = d3.select('#chart') .append('div') .attr('class', 'tooltips'); tooltip.append('div') .attr('class', 'label'); var data = [ {country: "UNITED KINGDOMhhhhhhhhhhhhhhhhhhhhhhhhh hhhhhhhhhhhhhhhhh", val: 86.68}, {country: "HONG KONGggggggggggggggggggggg g g g gg g g g g gg gg g g ", val: 9.23}, {country: "OTHERS", val: 4.09} ]; var w = 600, h = 600, r = Math.min(w, h) / 2 - 100, labelr = r + 30, // radius for label anchor color = d3.scale.category20(), donut = d3.layout.pie(), arc = d3.svg.arc().innerRadius(r * .6).outerRadius(r); var vis = d3.select("#chart") .append("svg:svg") .data([data]) .attr("width", w + 150) .attr("height", h); var arcs = vis.selectAll("g.arc") .data(donut.value(function(d) { return d.val })) .enter().append("svg:g") .attr("class", "arc") .attr("transform", "translate(" + (r + 200) + "," + (r+100) + ")"); var arcOver = d3.svg.arc() .innerRadius(r * .57) .outerRadius(r + 5); arcs.append("path") .attr("fill", function(d, i) { return color(i); }) .attr("d", arc) .on("mouseover",function(d){ d3.select(this).transition() .duration(50) .attr("d", arcOver); tooltip.select('.label').html(d.value + "%"); tooltip.style('display', 'block'); }) .on('mouseout', function() { d3.select(this).transition() .duration(50) .attr("d", arc); tooltip.style('display', 'none'); }) .on('mousemove', function(d) { tooltip.style('top', (d3.event.pageY - 80) + 'px') .style('left', (d3.event.pageX + 10) + 'px'); }); arcs.append("text") .attr("transform", function(d) { var c = arc.centroid(d), x = c[0], y = c[1], // pythagorean theorem for hypotenuse h = Math.sqrt(x*x + y*y); return "translate(" + (x/h * labelr) + ',' + (y/h * labelr) + ")"; }) .attr("dy", ".35em") .attr("text-anchor", function(d) { // are we past the center? return (d.endAngle + d.startAngle)/2 > Math.PI ? "end" : "start"; }) .text(function(d) { return d.data.country; }); Thanks!!!
var tooltip = d3.select('#chart') .append('div') .attr('class', 'tooltips'); tooltip.append('div') .attr('class', 'label'); var data = [ {country: "UNITED KINGDOMhhhhhhhhhhhhhhhhhhhhhhhhh hhhhhhhhhhhhhhhhh", val: 86.68}, {country: "HONG KONGggggggggggggggggggggg g g g gg g g g g gg gg g g ", val: 9.23}, {country: "OTHERS", val: 4.09} ]; var w = 600, h = 600, r = Math.min(w, h) / 2 - 100, labelr = r + 30, // radius for label anchor color = d3.scale.category20(), donut = d3.layout.pie(), arc = d3.svg.arc().innerRadius(r * .6).outerRadius(r); var vis = d3.select("#chart") .append("svg:svg") .data([data]) .attr("width", w + 150) .attr("height", h); var arcs = vis.selectAll("g.arc") .data(donut.value(function(d) { return d.val })) .enter().append("svg:g") .attr("class", "arc") .attr("transform", "translate(" + (r + 200) + "," + (r+100) + ")"); var arcOver = d3.svg.arc() .innerRadius(r * .57) .outerRadius(r + 5); arcs.append("path") .attr("fill", function(d, i) { return color(i); }) .attr("d", arc) .on("mouseover",function(d){ d3.select(this).transition() .duration(50) .attr("d", arcOver); tooltip.select('.label').html(d.value + "%"); tooltip.style('display', 'block'); }) .on('mouseout', function() { d3.select(this).transition() .duration(50) .attr("d", arc); tooltip.style('display', 'none'); }) .on('mousemove', function(d) { tooltip.style('top', (d3.event.pageY - 80) + 'px') .style('left', (d3.event.pageX + 10) + 'px'); }); var text = arcs.append("text") .attr("transform", function(d) { var c = arc.centroid(d), x = c[0], y = c[1], // pythagorean theorem for hypotenuse h = Math.sqrt(x*x + y*y); return "translate(" + (x/h * labelr) + ',' + (y/h * labelr) + ")"; }) .attr("dy", ".35em") .attr("text-anchor", function(d) { // are we past the center? return (d.endAngle + d.startAngle)/2 > Math.PI ? "end" : "start"; });/* .text(function(d) { return d.data.country; });*/ text.each(function(d){ var text = d3.select(this), words = d.data.country.split(/\s+/).reverse(), word, line = [], lineNumber = 0, lineHeight = 0.22, // ems y = text.attr("y"), dy = parseFloat(text.attr("dy")), tspan = text.text(null) .append("tspan") .attr("x", 0) .attr("y", y) .attr("dy", dy + "em"); while (word = words.pop()) { line.push(word); tspan.text(line.join(" ")); if (tspan.node().getComputedTextLength() > 10) { line.pop(); tspan.text(line.join(" ")); line = [word]; tspan = text.append("tspan") .attr("x", 0) .attr("y", y) .attr("dy", ++lineNumber * lineHeight + dy + "em") .text(word); } } }); <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script> <div id='chart'></div> After doing a great lot of work out. I got this. Hope this will fulfill your need/requirement. All I did is, Adding tspan elements to text element.Observe below code. text is a var see above code. which holds all text elements which we want to add to the every g text.each(function(d){ var text = d3.select(this),//selecting current text element words = d.data.country.split(/\s+/).reverse(),//splitting the country name by using space, if you want you can change. word,//to store one one word line = [], lineNumber = 0, lineHeight = 0.22, // ems, you can increase for more gaps vise versa y = text.attr("y"), dy = parseFloat(text.attr("dy")), tspan = text.text(null) .append("tspan") .attr("x", 0) .attr("y", y) .attr("dy", dy + "em"); while (word = words.pop()) { line.push(word); tspan.text(line.join(" ")); if (tspan.node().getComputedTextLength() > 10) {//here I'm checking the length of the text line.pop(); tspan.text(line.join(" ")); line = [word]; tspan = text.append("tspan") .attr("x", 0) .attr("y", y) .attr("dy", ++lineNumber * lineHeight + dy + "em")//setting the gap between the label line gaps. .text(word); } } })
meteor d3 import data
I'm having difficulties in importing data from MongoDB into the D3 heatmap function. An example can be found here: http://bl.ocks.org/tjdecke/5558084 The data looks like this: [Object { _id="DoaB3wnW7CCzxqzug", weekday=1, hour=1, value=12}, Object { _id="RnwxHbRPGdWHBaAYu", weekday=1, hour=2, value=4}] My code looks like this: Template.weeklyUsageHeatmap.rendered = function() { var margin = { top: 20, right: 0, bottom: 100, left: 20 }, width = 720 - margin.left - margin.right, height = 340 - margin.top - margin.bottom, gridSize = Math.floor(width / 24), legendElementWidth = gridSize*2, buckets = 9, colors = ["#ffffe5","#fff7bc","#fee391","#fec44f","#fe9929","#ec7014","#cc4c02","#993404","#662506"] days = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"], times = ["1a", "2a", "3a", "4a", "5a", "6a", "7a", "8a", "9a", "10a", "11a", "12a", "1p", "2p", "3p", "4p", "5p", "6p", "7p", "8p", "9p", "10p", "11p", "12p"]; var data = WeeklyUsage.find().fetch(); d3.json(data, function(d) { return { day: +d.weekday, hour: +d.hour, value: +d.value }; }, function(error, data) { var colorScale = d3.scale.quantile() .domain([0, buckets - 1, d3.max(data, function (d) { return d.value; })]) .range(colors); var svg = d3.select("#chart").append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); var dayLabels = svg.selectAll(".dayLabel") .data(days) .enter().append("text") .text(function (d) { return d; }) .attr("x", 0) .attr("y", function (d, i) { return i * gridSize; }) .style("text-anchor", "end") .attr("transform", "translate(-6," + gridSize / 1.5 + ")") .attr("class", function (d, i) { return ((i >= 0 && i <= 4) ? "dayLabel mono axis axis-workweek" : "dayLabel mono axis"); }); var timeLabels = svg.selectAll(".timeLabel") .data(times) .enter().append("text") .text(function(d) { return d; }) .attr("x", function(d, i) { return i * gridSize; }) .attr("y", 0) .style("text-anchor", "middle") .attr("transform", "translate(" + gridSize / 2 + ", -6)") .attr("class", function(d, i) { return ((i >= 7 && i <= 16) ? "timeLabel mono axis axis-worktime" : "timeLabel mono axis"); }); var heatMap = svg.selectAll(".hour") .data(data) .enter().append("rect") .attr("x", function(d) { return (d.hour - 1) * gridSize; }) .attr("y", function(d) { return (d.weekday - 1) * gridSize; }) .attr("rx", 4) .attr("ry", 4) .attr("class", "hour bordered") .attr("width", gridSize) .attr("height", gridSize) .style("fill", colors[0]); heatMap.transition().duration(1000) .style("fill", function(d) { return colorScale(d.value); }); heatMap.append("title").text(function(d) { return d.value; }); var legend = svg.selectAll(".legend") .data([0].concat(colorScale.quantiles()), function(d) { return d; }) .enter().append("g") .attr("class", "legend"); legend.append("rect") .attr("x", function(d, i) { return legendElementWidth * i; }) .attr("y", height) .attr("width", legendElementWidth) .attr("height", gridSize / 2) .style("fill", function(d, i) { return colors[i]; }); legend.append("text") .attr("class", "mono") .text(function(d) { return "≥ " + Math.round(d); }) .attr("x", function(d, i) { return legendElementWidth * i; }) .attr("y", height + gridSize); }); }; The error I got was: d is NULL.