Positioning of canvas after the conversion from D3 - javascript

I just managed to convert the respective heatmap from SVG to canvas. (due to large dataset vs performance issue) However the position of the generated heatmap has goes to a new region. I not sure how am I going to do about this. By changing the transform does not change anything as well.
My code:
var units = [];
for(var unit_i = 0; unit_i<=101;){
if(unit_i==0){
units.push(1);
unit_i = unit_i + 5;
}
else{
units.push(unit_i);
unit_i = unit_i + 4;
}
}
var times = [];
for(var times_i = 0; times_i<=1440;){
if(times_i==0){
times.push(1);
times_i = times_i + 10;
}
else{
times.push(times_i);
times_i = times_i + 9;
}
}
var newSample = [{unit:null, timestamp: null, level: null}];
//by using below method we can observe the delay is not due to the data during insertion
for(var unit=1; unit<=99; unit++){
for(var timestamp = 1; timestamp<=100; timestamp++){
var i = Math.random() * 1400;
newSample.push({unit:unit, timestamp: timestamp, level:i});
}
}
var hours = 0;
var hoursIndicator = 0;
var margin = {
top: 170,
right: 100,
bottom: 70,
left: 100
};
var width = 2500,//optimized width
//gridSize = Math.floor(width / times.length),//optimized gridsize
gridSize = 10;//if 20 each interval will have 5
height = 50 * (units.length); //optimized, the greater the length, the greater the height
console.log("this is gridSize:" + gridSize +", this is height: " + height + ", and this is width: " + width);
//SVG container
var svg = d3.select('.trafficCongestions')
.append("svg")
//.style("position", "absolute")
.attr("width", width + margin.left + margin.right)//optimization
.attr("height", height + margin.top + margin.bottom)//optimization
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var canvas = d3.select('.trafficCongestions').append("canvas")
.attr("id", "canvas")
.attr("width", width + margin.left + margin.right)//optimization
.attr("height", height + margin.top + margin.bottom);//optimization
var context = canvas.node().getContext("2d");
context.clearRect(0, 0, width, height);
var detachedContainer = document.createElement("custom");
var dataContainer = d3.select(detachedContainer);
//Reset the overall font size
var newFontSize = width * 62.5 / 900;
//heatmap drawing starts from here
var colorScale = d3.scaleLinear()
.domain([0, d3.max(newSample, function(d, i) {return d.level; })/2, d3.max(newSample, function(d, i) {return d.level; })])
.range(["#009933", "#FFCC00", "#990000"])//obtain the max data value of count
//y-axis (solely based on data of days)
var dayLabels = svg.selectAll(".dayLabel")
.data(units)
.enter().append("text")
.text(function (d) { return d; })
.attr("x", 0)
.attr("y", function (d, i) {
return (i) * (gridSize * 4)/*adjusts the interval distance with (n - 1) concept*/; })
.style("text-anchor", "end")
.attr("transform", "translate(-6," + gridSize + ")");
//x-axis (solely based on data of times)
var timeLabels = svg.selectAll(".timeLabel")
.data(times)
.enter().append("text")
.text(function(d, i) {
var hrs = Math.floor(d/60);
var mins = d%60;
if(hrs<10){
if(mins<10){
return "0"+hrs + ":0" + mins;
}
return "0"+ hrs + ":" + mins;
}
return hrs +":"+ mins;
})
.attr("x", function(d, i) { return i * (gridSize * 9)/*adjusts the interval distance with (n - 1) concept*/; })
.attr("y", 0)
.style("text-anchor", "middle")
.attr("transform", "translate(" + 1 + ", -6)")
var heatMap = dataContainer.selectAll("custom.rect")
.data(newSample)
.enter().append("custom")
.attr("x", function(d) {
return (d.timestamp - 1) * (gridSize);
})
.attr("y", function(d) {
console.log(d.unit);
return (d.unit - 1) * (gridSize);
})
.classed("rect", true)
.attr("class", " rect bordered")
.attr("width", gridSize)
.attr("height", gridSize)
.attr("strokeStyle", "rgba(255,255,255, 0.6)")//to have the middle line or not
.attr("fillStyle", function(d,i){
return colorScale(d.level);
});
drawCanvas();
//Append title to the top
svg.append("text")
.attr("class", "title")
.attr("x", width/2)
.attr("y", -90)
.style("text-anchor", "middle")
.text("Sample Result");
svg.append("text")
.attr("class", "subtitle")
.attr("x", width/2)
.attr("y", -60)
.style("text-anchor", "middle")
.text("HEATMAP");
//Append credit at bottom
svg.append("text")
.attr("class", "credit")
.attr("x", width/2)
.attr("y", gridSize * (units.length+1) + 80)
.style("text-anchor", "middle");
//Extra scale since the color scale is interpolated
var countScale = d3.scaleLinear()
.domain([0, d3.max(newSample, function(d) {return d.level; })])
.range([0, width])
//Calculate the variables for the temp gradient
var numStops = 10;
countRange = countScale.domain();
countRange[2] = countRange[1] - countRange[0];
countPoint = [];
for(var i = 0; i < numStops; i++) {
countPoint.push(i * countRange[2]/(numStops-1) + countRange[0]);
}//for i
//Create the gradient
svg.append("defs")
.append("linearGradient")
.attr("id", "legendLevel")
.attr("x1", "0%").attr("y1", "0%")
.attr("x2", "100%").attr("y2", "0%")
.selectAll("stop")
.data(d3.range(numStops))
.enter().append("stop")
.attr("offset", function(d,i) {
return countScale( countPoint[i] )/width;
})
.attr("stop-color", function(d,i) {
return colorScale( countPoint[i] );
});
var legendWidth = Math.min(width, 400);//the width of the legend
console.log(width);
//Color Legend container
var legendsvg = svg.append("g")
.attr("class", "legendWrapper")
.attr("transform", "translate(" + (width/2) + "," + (gridSize * 100 + 40) + ")");
//Draw the Rectangle
legendsvg.append("rect")
.attr("class", "legendRect")
.attr("x", -legendWidth/2)
.attr("y", 0)
.attr("width", legendWidth)
.attr("height", 10)
.style("fill", "url(#legendLevel)");
//Append title
legendsvg.append("text")
.attr("class", "legendTitle")
.attr("x", 0)
.attr("y", -10)
.style("text-anchor", "middle")
.text("Level");
//Set scale for x-axis
var xScale = d3.scaleLinear()
.range([-legendWidth/2, legendWidth/2])
.domain([ 0, d3.max(newSample, function(d) { return d.level; })] );
//Define x-axis
var xAxis = d3.axisBottom()
.ticks(5)
.scale(xScale);
//Set up X axis
legendsvg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + (10) + ")")
.call(xAxis);
function drawCanvas(){
var elements = dataContainer.selectAll("custom.rect");
elements.each(function(d){
var node = d3.select(this);
context.beginPath();
context.fillStyle = node.attr("fillStyle");
context.rect(node.attr("x"), node.attr("y"), node.attr("width"), node.attr("height"));
context.fill();
context.closePath();
});
}
html { font-size: 100%; }
.timeLabel, .dayLabel {
font-size: 1rem;
fill: #AAAAAA;
font-weight: 300;
}
.title {
font-size: 1.8rem;
fill: #4F4F4F;
font-weight: 300;
}
.subtitle {
font-size: 1.0rem;
fill: #AAAAAA;
font-weight: 300;
}
.credit {
font-size: 1.2rem;
fill: #AAAAAA;
font-weight: 400;
}
.axis path, .axis tick, .axis line {
fill: none;
stroke: none;
}
.legendTitle {
font-size: 1.3rem;
fill: #4F4F4F;
font-weight: 300;
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id="trafficCongestions" class="trafficCongestions"></div>

As you're using svg to just show up the text and legend, I'd say you can absolute position the canvas on top of the SVG - by CSS.
Here are the CSS changes:
div#trafficCongestions {
position: relative;
}
canvas {
position: absolute;
top: 170px;
left: 100px;
}
You can do the above using d3 style as well `cause the margins are defined in the script. I just wanted to show that this is an option to use.
var units = [];
for(var unit_i = 0; unit_i<=101;){
if(unit_i==0){
units.push(1);
unit_i = unit_i + 5;
}
else{
units.push(unit_i);
unit_i = unit_i + 4;
}
}
var times = [];
for(var times_i = 0; times_i<=1440;){
if(times_i==0){
times.push(1);
times_i = times_i + 10;
}
else{
times.push(times_i);
times_i = times_i + 9;
}
}
var newSample = [{unit:null, timestamp: null, level: null}];
//by using below method we can observe the delay is not due to the data during insertion
for(var unit=1; unit<=99; unit++){
for(var timestamp = 1; timestamp<=100; timestamp++){
var i = Math.random() * 1400;
newSample.push({unit:unit, timestamp: timestamp, level:i});
}
}
var hours = 0;
var hoursIndicator = 0;
var margin = {
top: 170,
right: 100,
bottom: 70,
left: 100
};
var width = 2500,//optimized width
//gridSize = Math.floor(width / times.length),//optimized gridsize
gridSize = 10;//if 20 each interval will have 5
height = 50 * (units.length); //optimized, the greater the length, the greater the height
//console.log("this is gridSize:" + gridSize +", this is height: " + height + ", and this is width: " + width);
//SVG container
var svg = d3.select('.trafficCongestions')
.append("svg")
//.style("position", "absolute")
.attr("width", width + margin.left + margin.right)//optimization
.attr("height", height + margin.top + margin.bottom)//optimization
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var canvas = d3.select('.trafficCongestions').append("canvas")
.attr("id", "canvas")
.attr("width", width + margin.left + margin.right)//optimization
.attr("height", height + margin.top + margin.bottom);//optimization
var context = canvas.node().getContext("2d");
context.clearRect(0, 0, width, height);
var detachedContainer = document.createElement("custom");
var dataContainer = d3.select(detachedContainer);
//Reset the overall font size
var newFontSize = width * 62.5 / 900;
//heatmap drawing starts from here
var colorScale = d3.scaleLinear()
.domain([0, d3.max(newSample, function(d, i) {return d.level; })/2, d3.max(newSample, function(d, i) {return d.level; })])
.range(["#009933", "#FFCC00", "#990000"])//obtain the max data value of count
//y-axis (solely based on data of days)
var dayLabels = svg.selectAll(".dayLabel")
.data(units)
.enter().append("text")
.text(function (d) { return d; })
.attr("x", 0)
.attr("y", function (d, i) {
return (i) * (gridSize * 4)/*adjusts the interval distance with (n - 1) concept*/; })
.style("text-anchor", "end")
.attr("transform", "translate(-6," + gridSize + ")");
//x-axis (solely based on data of times)
var timeLabels = svg.selectAll(".timeLabel")
.data(times)
.enter().append("text")
.text(function(d, i) {
var hrs = Math.floor(d/60);
var mins = d%60;
if(hrs<10){
if(mins<10){
return "0"+hrs + ":0" + mins;
}
return "0"+ hrs + ":" + mins;
}
return hrs +":"+ mins;
})
.attr("x", function(d, i) { return i * (gridSize * 9)/*adjusts the interval distance with (n - 1) concept*/; })
.attr("y", 0)
.style("text-anchor", "middle")
.attr("transform", "translate(" + 1 + ", -6)")
var heatMap = dataContainer.selectAll("custom.rect")
.data(newSample)
.enter().append("custom")
.attr("x", function(d) {
return (d.timestamp - 1) * (gridSize);
})
.attr("y", function(d) {
//console.log(d.unit);
return (d.unit - 1) * (gridSize);
})
.classed("rect", true)
.attr("class", " rect bordered")
.attr("width", gridSize)
.attr("height", gridSize)
.attr("strokeStyle", "rgba(255,255,255, 0.6)")//to have the middle line or not
.attr("fillStyle", function(d,i){
return colorScale(d.level);
});
drawCanvas();
//Append title to the top
svg.append("text")
.attr("class", "title")
.attr("x", width/2)
.attr("y", -90)
.style("text-anchor", "middle")
.text("Sample Result");
svg.append("text")
.attr("class", "subtitle")
.attr("x", width/2)
.attr("y", -60)
.style("text-anchor", "middle")
.text("HEATMAP");
//Append credit at bottom
svg.append("text")
.attr("class", "credit")
.attr("x", width/2)
.attr("y", gridSize * (units.length+1) + 80)
.style("text-anchor", "middle");
//Extra scale since the color scale is interpolated
var countScale = d3.scaleLinear()
.domain([0, d3.max(newSample, function(d) {return d.level; })])
.range([0, width])
//Calculate the variables for the temp gradient
var numStops = 10;
countRange = countScale.domain();
countRange[2] = countRange[1] - countRange[0];
countPoint = [];
for(var i = 0; i < numStops; i++) {
countPoint.push(i * countRange[2]/(numStops-1) + countRange[0]);
}//for i
//Create the gradient
svg.append("defs")
.append("linearGradient")
.attr("id", "legendLevel")
.attr("x1", "0%").attr("y1", "0%")
.attr("x2", "100%").attr("y2", "0%")
.selectAll("stop")
.data(d3.range(numStops))
.enter().append("stop")
.attr("offset", function(d,i) {
return countScale( countPoint[i] )/width;
})
.attr("stop-color", function(d,i) {
return colorScale( countPoint[i] );
});
var legendWidth = Math.min(width, 400);//the width of the legend
//console.log(width);
//Color Legend container
var legendsvg = svg.append("g")
.attr("class", "legendWrapper")
.attr("transform", "translate(" + (width/2) + "," + (gridSize * 100 + 40) + ")");
//Draw the Rectangle
legendsvg.append("rect")
.attr("class", "legendRect")
.attr("x", -legendWidth/2)
.attr("y", 0)
.attr("width", legendWidth)
.attr("height", 10)
.style("fill", "url(#legendLevel)");
//Append title
legendsvg.append("text")
.attr("class", "legendTitle")
.attr("x", 0)
.attr("y", -10)
.style("text-anchor", "middle")
.text("Level");
//Set scale for x-axis
var xScale = d3.scaleLinear()
.range([-legendWidth/2, legendWidth/2])
.domain([ 0, d3.max(newSample, function(d) { return d.level; })] );
//Define x-axis
var xAxis = d3.axisBottom()
.ticks(5)
.scale(xScale);
//Set up X axis
legendsvg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + (10) + ")")
.call(xAxis);
function drawCanvas(){
var elements = dataContainer.selectAll("custom.rect");
elements.each(function(d){
var node = d3.select(this);
context.beginPath();
context.fillStyle = node.attr("fillStyle");
context.rect(node.attr("x"), node.attr("y"), node.attr("width"), node.attr("height"));
context.fill();
context.closePath();
});
}
html { font-size: 100%; }
div#trafficCongestions {
position: relative;
}
canvas {
position: absolute;
top: 170px;
left: 100px;
}
.timeLabel, .dayLabel {
font-size: 1rem;
fill: #AAAAAA;
font-weight: 300;
}
.title {
font-size: 1.8rem;
fill: #4F4F4F;
font-weight: 300;
}
.subtitle {
font-size: 1.0rem;
fill: #AAAAAA;
font-weight: 300;
}
.credit {
font-size: 1.2rem;
fill: #AAAAAA;
font-weight: 400;
}
.axis path, .axis tick, .axis line {
fill: none;
stroke: none;
}
.legendTitle {
font-size: 1.3rem;
fill: #4F4F4F;
font-weight: 300;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id="trafficCongestions" class="trafficCongestions"></div>
Let me know if this makes sense. If not, let's look for other approaches.
Edit as per comments: (visual studio didn't support the CSS styling added by above approach)
Added the style using d3:
canvas.style('position', 'absolute').style('top', margin.top+'px').style('left', margin.left+'px')
And this worked.

Related

D3 text not centered on nodes?

I have a jsfiddle with a scatter plot, on which the corresponding text will not align with the dots.
I believe that this chunk should be displaying the names at the same coordinates as the circles:
.attr("x", function(d){return timeScale(d[1]);})
.attr("y", function(d){return rankScale(d[0]);})
This is the same code I used to place the circles.
Do I misunderstand something?
Since you are (for whatever reason) translating the circles, you should apply the same translate to the texts:
textSelection.attr("transform", "translate("+transRight+","+transDown+")")
Alternatively, don't translate the circles.
Here is your updated fiddle: https://jsfiddle.net/yv3ts1fw/
And here a Stack snippet with the same code:
function call() {
$.ajax({
url: "https://raw.githubusercontent.com/FreeCodeCamp/ProjectReferenceData/master/cyclist-data.json",
data: {
format: 'json'
},
complete: function(xhr, textStatus) {
//console.log(xhr.status);
//console.log(textStatus);
},
error: function() {
$('#container').html('<p>An error has occurred</p>');
},
success: (data) => {
pushArray(data);
},
type: 'GET'
});
}
call();
var timeArray = [];
function pushArray(data) {
data = JSON.parse(data);
for (var i = 0; i < data.length; i++) {
var tempArray = [];
tempArray.push(i);
var hms = data[i].Time;
var a = hms.split(':');
var seconds = ((+a[0]) * 60 + (+a[1]));
tempArray.push(seconds);
timeArray.push(tempArray);
}
var w = $(window).width();
var h = $(window).height();
var margin = {
top: h / 5,
left: w / 10
};
h = h * .8;
w = w * .9;
var svgW = w * .8;
var svgH = h * .8;
w = w * .6;
h = h * .6;
var rankScale = d3.scaleLinear()
.domain([1, 35])
.range([0, h]);
var axisRankScale = d3.scaleLinear()
.domain([35, 1])
.range([h, 0]);
var timeScale = d3.scaleLinear()
.domain([2200, 2400])
.range([0, w]);
var xAxis = d3.axisBottom()
.scale(timeScale);
var yAxis = d3.axisLeft()
.scale(axisRankScale);
var toolTip = d3.select("body")
.append("div")
.attr("class", "tooltip")
.style("opacity", 0);
var transDown = 20;
var transRight = 100;
//h = 700;
// h=h*1.2;
//w=w*1.2;
//w= 300;
var theBody = d3.select("#container")
.append("svg")
.attr("height", svgH * 1.4)
.attr("width", w * 1.6)
.attr("transform", "translate(50, 50)")
.append("g")
.attr("transform", "translate(20, 20)");
theBody.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 50)
.attr("x", -(h / 2))
//.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Rank");
theBody.append("text")
.attr("y", h + margin.top)
.attr("x", w / 1.5)
.style("text-anchor", "middle")
.text("Seconds");
theBody.selectAll("foo")
.data(timeArray)
.enter()
.append("text")
.text(function(d) {
var index = d[0];
var _this = data[index];
return _this.Name;
})
.attr("x", function(d) {
return timeScale((d[1]));
})
.attr("y", function(d) {
return rankScale((d[0]));
})
.attr("font-size", "10px")
.attr("transform", "translate(" + (transRight + 16) + "," + transDown + ")");
theBody.selectAll("circle")
.data(timeArray)
.enter()
.append("circle")
.attr("cx", function(d) {
return timeScale(d[1]);
})
.attr("cy", function(d) {
return rankScale((d[0]));
})
.attr("r", 10)
.attr("fill", "green")
.attr("stroke", "black")
.attr("transform", "translate(" + transRight + "," + transDown + ")")
.on("mouseout", function() {
toolTip.style("opacity", 0.0);
})
.on("mouseover", function(d) {
var index = d[0];
var _this = data[index];
var time = _this.Time,
name = _this.Name,
year = _this.Year,
dope = _this.Doping;
if (dope === "") {
dope = "No doping allegations!";
}
toolTip.html(name + "<br>" + year + "<br>" + time + " <br>---<br>" + dope)
.style("opacity", 1)
.style("left", ((d3.event.pageX) + 40) + "px")
.style("top", ((d3.event.pageY) + 0) + "px");
});
}; // end pushArray main
svg {
border: solid;
}
#container {
border: solid green;
width: 100vw;
height: 100vh;
}
div.tooltip {
display: flex;
position: absolute;
justify-content: center;
align-items: center;
text-align: center;
width: 12vw;
height: 25vh;
padding: 2px;
font: 12px sans-serif;
font-weight: bold;
background: rgb(255, 82, 80);
border: 0px;
border-radius: 8px;
pointer-events: none;
opacity: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id="container">
<div>tour de france
<div></div>
PS: in your text selection, don't do theBody.selectAll("text"), because you already have texts in that SVG. Instead of that, select something that doesn't exist, like theBody.selectAll("foo").

Using tooltip in D3 chord diagram to get value - Error: "property of target undefined"

I am trying to create a chord diagram and have the value of each ribbons source and target value as seen be accessible when they are hovered over. I also want to be able to access the index of that value to refer to use the colour.
At the moment I am getting an error of d not being defined whenever I hover over - I'm not sure how to access the values I need.. I don't want to add any other .js libraries if possible.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.group-tick line {
stroke: #000;
}
.ribbons {
fill-opacity: 0.67;
}
.toolTip {
position: absolute;
display: none;
min-width: 80px;
height: auto;
background: none repeat scroll 0 0 #ffffff;
border: 1px solid #6F257F;
padding: 14px;
text-align: center;
}
</style>
<svg width="960" height="960"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var matrix = [
[11975, 5871, 8916, 2868],
[ 1951, 10048, 2060, 6171],
[ 8010, 16145, 8090, 8045],
[ 1013, 990, 940, 6907]
];
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
outerRadius = Math.min(width, height) * 0.5 - 40,
innerRadius = outerRadius - 30;
var formatValue = d3.formatPrefix(",.0", 1e3);
var chord = d3.chord()
.padAngle(0.05)
.sortSubgroups(d3.descending);
var arc = d3.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
var ribbon = d3.ribbon()
.radius(innerRadius);
var color = d3.scaleOrdinal()
.domain(d3.range(4))
.range(["#000000", "#FFDD89", "#957244", "#F26223"]);
var g = svg.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")
.datum(chord(matrix));
var group = g.append("g")
.attr("class", "groups")
.selectAll("g")
.data(function(chords) { return chords.groups; })
.enter().append("g");
group.append("path")
.style("fill", function(d) { return color(d.index); })
.style("stroke", function(d) { return d3.rgb(color(d.index)).darker(); })
.attr("d", arc);
var groupTick = group.selectAll(".group-tick")
.data(function(d) { return groupTicks(d, 1e3); })
.enter().append("g")
.attr("class", "group-tick")
.attr("transform", function(d) { return "rotate(" + (d.angle * 180 / Math.PI - 90) + ") translate(" + outerRadius + ",0)"; });
groupTick.append("line")
.attr("x2", 6);
groupTick
.filter(function(d) { return d.value % 5e3 === 0; })
.append("text")
.attr("x", 8)
.attr("dy", ".35em")
.attr("transform", function(d) { return d.angle > Math.PI ? "rotate(180) translate(-16)" : null; })
.style("text-anchor", function(d) { return d.angle > Math.PI ? "end" : null; })
.text(function(d) { return formatValue(d.value); });
var tooltip = d3.select("body").append("div").attr("class", "toolTip");
g.append("g")
.attr("class", "ribbons")
.selectAll("path")
.data(function(chords) { return chords; })
.enter().append("path")
.attr("d", ribbon)
.style("fill", function(d) { return color(d.target.index); })
.style("stroke", function(d) { return d3.rgb(color(d.target.index)).darker(); })
.on("mouseover", function(d) {
tooltip
.style("left", d3.event.pageX - 50 + "px")
.style("top", d3.event.pageY - 70 + "px")
.style("display", "inline-block")
.html(function(d) {
return (d.target.index) + "Hello";
});
})
.on("mouseout", function(d){
tooltip
.style("display", "none")
})
;
// Returns an array of tick angles and values for a given group and step.
function groupTicks(d, step) {
var k = (d.endAngle - d.startAngle) / d.value;
return d3.range(0, d.value, step).map(function(value) {
return {value: value, angle: value * k + d.startAngle};
});
}
</script>
I'm using the tutorial here: https://bl.ocks.org/mbostock/4062006
The issue is that there is no datum associated with your tooltip:
var tooltip = d3.select("body").append("div").attr("class", "toolTip");
So when you say tooltip.html(function(d) { ... there is no datum associated with that element to use.
Instead, try to use the datum associated with the selected chord:
.on("mouseover", function(d) { // the datum you want
tooltip
.style("left", d3.event.pageX - 50 + "px")
.style("top", d3.event.pageY - 70 + "px")
.style("display", "inline-block")
.html(d.target.index + "Hello");
})
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.group-tick line {
stroke: #000;
}
.ribbons {
fill-opacity: 0.67;
}
.toolTip {
position: absolute;
display: none;
min-width: 80px;
height: auto;
background: none repeat scroll 0 0 #ffffff;
border: 1px solid #6F257F;
padding: 14px;
text-align: center;
}
</style>
<svg width="960" height="960"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var matrix = [
[11975, 5871, 8916, 2868],
[ 1951, 10048, 2060, 6171],
[ 8010, 16145, 8090, 8045],
[ 1013, 990, 940, 6907]
];
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
outerRadius = Math.min(width, height) * 0.5 - 40,
innerRadius = outerRadius - 30;
var formatValue = d3.formatPrefix(",.0", 1e3);
var chord = d3.chord()
.padAngle(0.05)
.sortSubgroups(d3.descending);
var arc = d3.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
var ribbon = d3.ribbon()
.radius(innerRadius);
var color = d3.scaleOrdinal()
.domain(d3.range(4))
.range(["#000000", "#FFDD89", "#957244", "#F26223"]);
var g = svg.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")
.datum(chord(matrix));
var group = g.append("g")
.attr("class", "groups")
.selectAll("g")
.data(function(chords) { return chords.groups; })
.enter().append("g");
group.append("path")
.style("fill", function(d) { return color(d.index); })
.style("stroke", function(d) { return d3.rgb(color(d.index)).darker(); })
.attr("d", arc);
var groupTick = group.selectAll(".group-tick")
.data(function(d) { return groupTicks(d, 1e3); })
.enter().append("g")
.attr("class", "group-tick")
.attr("transform", function(d) { return "rotate(" + (d.angle * 180 / Math.PI - 90) + ") translate(" + outerRadius + ",0)"; });
groupTick.append("line")
.attr("x2", 6);
groupTick
.filter(function(d) { return d.value % 5e3 === 0; })
.append("text")
.attr("x", 8)
.attr("dy", ".35em")
.attr("transform", function(d) { return d.angle > Math.PI ? "rotate(180) translate(-16)" : null; })
.style("text-anchor", function(d) { return d.angle > Math.PI ? "end" : null; })
.text(function(d) { return formatValue(d.value); });
var tooltip = d3.select("body").append("div").attr("class", "toolTip");
g.append("g")
.attr("class", "ribbons")
.selectAll("path")
.data(function(chords) { return chords; })
.enter().append("path")
.attr("d", ribbon)
.style("fill", function(d) { return color(d.target.index); })
.style("stroke", function(d) { return d3.rgb(color(d.target.index)).darker(); })
.on("mouseover", function(d) {
tooltip
.style("left", d3.event.pageX - 50 + "px")
.style("top", d3.event.pageY - 70 + "px")
.style("display", "inline-block")
.html(d.target.index + "Hello");
})
.on("mouseout", function(d){
tooltip
.style("display", "none")
})
;
// Returns an array of tick angles and values for a given group and step.
function groupTicks(d, step) {
var k = (d.endAngle - d.startAngle) / d.value;
return d3.range(0, d.value, step).map(function(value) {
return {value: value, angle: value * k + d.startAngle};
});
}
</script>

data not bind from json for d3 chart

Hi I am using D3 chart.
i'm using This d3 chart :http://bl.ocks.org/diethardsteiner/3287802
In this chart all the data are read from variable. I want to read the data from json file.
I have complete the half of the work. I did same to complete the another half of the work but it is not working.
Pie chart is read data from json. but the bar chart is not to read data from json.
Here I have Created Plunker check this and give some solution.
https://plnkr.co/edit/TaXMsUWuIXe5kv3yzazk?p=preview
here what i tried to read data is not read from json.
I want to run this chart as same as this example:http://bl.ocks.org/diethardsteiner/3287802
i tried like this
d3.json("data1.json", function(datasetBarChart){
debugger;
// set initial group value
var group = "All";
function datasetBarChosen(group) {
var ds = [];
for (x in datasetBarChart) {
if(datasetBarChart[x].group==group){
ds.push(datasetBarChart[x]);
}
}
return ds;
}
function dsBarChartBasics() {
debugger;
var margin = {top: 30, right: 5, bottom: 20, left: 50},
width = 500 - margin.left - margin.right,
height = 250 - margin.top - margin.bottom,
colorBar = d3.scale.category20(),
barPadding = 1
;
return {
margin : margin,
width : width,
height : height,
colorBar : colorBar,
barPadding : barPadding
}
;
}
function dsBarChart() {
debugger;
var firstDatasetBarChart = datasetBarChosen(group);
var basics = dsBarChartBasics();
var margin = basics.margin,
width = basics.width,
height = basics.height,
colorBar = basics.colorBar,
barPadding = basics.barPadding
;
var xScale = d3.scale.linear()
.domain([0, firstDatasetBarChart.length])
.range([0, width])
;
var yScale = d3.scale.linear()
.domain([0, d3.max(firstDatasetBarChart, function(d) { return d.measure; })])
.range([height, 0])
;
//Create SVG element
var svg = d3.select("#barChart")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.attr("id","barChartPlot")
;
var plot = svg
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
;
plot.selectAll("rect")
.data(firstDatasetBarChart)
.enter()
.append("rect")
.attr("x", function(d, i) {
return xScale(i);
})
.attr("width", width / firstDatasetBarChart.length - barPadding)
.attr("y", function(d) {
return yScale(d.measure);
})
.attr("height", function(d) {
return height-yScale(d.measure);
})
.attr("fill", "lightgrey")
;
// Add y labels to plot
plot.selectAll("text")
.data(firstDatasetBarChart)
.enter()
.append("text")
.text(function(d) {
return formatAsInteger(d3.round(d.measure));
})
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return (i * (width / firstDatasetBarChart.length)) + ((width / firstDatasetBarChart.length - barPadding) / 2);
})
.attr("y", function(d) {
return yScale(d.measure) + 14;
})
.attr("class", "yAxis")
var xLabels = svg
.append("g")
.attr("transform", "translate(" + margin.left + "," + (margin.top + height) + ")")
;
debugger;
xLabels.selectAll("text.xAxis")
.data(firstDatasetBarChart)
.enter()
.append("text")
.text(function(d) { return d.category;})
.attr("text-anchor", "middle")
// Set x position to the left edge of each bar plus half the bar width
.attr("x", function(d, i) {
return (i * (width / firstDatasetBarChart.length)) + ((width / firstDatasetBarChart.length - barPadding) / 2);
})
.attr("y", 15)
.attr("class", "xAxis")
//.attr("style", "font-size: 12; font-family: Helvetica, sans-serif")
;
// Title
svg.append("text")
.attr("x", (width + margin.left + margin.right)/2)
.attr("y", 15)
.attr("class","title")
.attr("text-anchor", "middle")
.text("Elevator Trips by Material Stream and Destination")
;
}
dsBarChart();
/* ** UPDATE CHART ** */
/* updates bar chart on request */
function updateBarChart(group, colorChosen) {
debugger;
var currentDatasetBarChart = datasetBarChosen(group);
var basics = dsBarChartBasics();
var margin = basics.margin,
width = basics.width,
height = basics.height,
colorBar = basics.colorBar,
barPadding = basics.barPadding
;
var xScale = d3.scale.linear()
.domain([0, currentDatasetBarChart.length])
.range([0, width])
;
var yScale = d3.scale.linear()
.domain([0, d3.max(currentDatasetBarChart, function(d) { return d.measure; })])
.range([height,0])
;
var svg = d3.select("#barChart svg");
var plot = d3.select("#barChartPlot")
.datum(currentDatasetBarChart)
;
/* Note that here we only have to select the elements - no more appending! */
plot.selectAll("rect")
.data(currentDatasetBarChart)
.transition()
.duration(750)
.attr("x", function(d, i) {
return xScale(i);
})
.attr("width", width / currentDatasetBarChart.length - barPadding)
.attr("y", function(d) {
return yScale(d.measure);
})
.attr("height", function(d) {
return height-yScale(d.measure);
})
.attr("fill", colorChosen)
;
plot.selectAll("text.yAxis") // target the text element(s) which has a yAxis class defined
.data(currentDatasetBarChart)
.transition()
.duration(750)
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return (i * (width / currentDatasetBarChart.length)) + ((width / currentDatasetBarChart.length - barPadding) / 2);
})
.attr("y", function(d) {
return yScale(d.measure) + 14;
})
.text(function(d) {
return formatAsInteger(d3.round(d.measure));
})
.attr("class", "yAxis")
;
svg.selectAll("text.title") // target the text element(s) which has a title class defined
.attr("x", (width + margin.left + margin.right)/2)
.attr("y", 15)
.attr("class","title")
.attr("text-anchor", "middle")
.text(group + "'s Elevator Trips by Material Stream and Destination")
;
}
});
Thanks,
You are using wrong d3.json callback signature. The first argument of the callback is an error that occurred, or null if no error occurred, the second argument is the returned data. So, you should use following:
d3.json("data1.json", function(error, data){
if(error) {
// handle error
} else {
// work with the data array
}
}
The following code snippet demonstrates the loading of dataset from an external JSON storage. When data has been loaded it is passed to the dsPieChart function that draws the above mentioned pie chart.
d3.select(window).on('load', function() {
// Loading data from external JSON source
d3.json('https://api.myjson.com/bins/1hewit', function(error, json) {
if (error) {
throw new Error('An error occurs');
} else {
dsPieChart(json);
}
});
});
// Format options
var formatAsPercentage = d3.format("%"),
formatAsPercentage1Dec = d3.format(".1%"),
formatAsInteger = d3.format(","),
fsec = d3.timeFormat("%S s"),
fmin = d3.timeFormat("%M m"),
fhou = d3.timeFormat("%H h"),
fwee = d3.timeFormat("%a"),
fdat = d3.timeFormat("%d d"),
fmon = d3.timeFormat("%b");
// PIE CHART drawing
function dsPieChart(dataset) {
var width = 400,
height = 400,
outerRadius = Math.min(width, height) / 2,
innerRadius = outerRadius * .999,
innerRadiusFinal = outerRadius * .5,
innerRadiusFinal3 = outerRadius * .45,
color = d3.scaleOrdinal(d3.schemeCategory20);
var vis = d3.select("#pieChart")
.append("svg:svg")
.data([dataset])
.attr("width", width)
.attr("height", height)
.append("svg:g")
.attr("transform", "translate(" + outerRadius + "," + outerRadius + ")");
var arc = d3.arc()
.outerRadius(outerRadius).innerRadius(innerRadius);
var arcFinal = d3.arc().innerRadius(innerRadiusFinal).outerRadius(outerRadius);
var arcFinal3 = d3.arc().innerRadius(innerRadiusFinal3).outerRadius(outerRadius);
var pie = d3.pie()
.value(function(d) {
return d.measure;
});
var arcs = vis.selectAll("g.slice")
.data(pie)
.enter()
.append("svg:g")
.attr("class", "slice")
.on("mouseover", mouseover)
.on("mouseout", mouseout)
.on("click", up);
arcs.append("svg:path")
.attr("fill", function(d, i) {
return color(i);
})
.attr("d", arc)
.append("svg:title")
.text(function(d) {
return d.data.category + ": " + formatAsPercentage(d.data.measure);
});
d3.selectAll("g.slice").selectAll("path").transition()
.duration(750)
.delay(10)
.attr("d", arcFinal);
arcs.filter(function(d) {
return d.endAngle - d.startAngle > .2;
})
.append("svg:text")
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.attr("transform", function(d) {
return "translate(" + arcFinal.centroid(d) + ")rotate(" + angle(d) + ")";
})
.text(function(d) {
return d.data.category;
});
function angle(d) {
var a = (d.startAngle + d.endAngle) * 90 / Math.PI - 90;
return a > 90 ? a - 180 : a;
}
vis.append("svg:text")
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text("Revenue Share 2012")
.attr("class", "title");
function mouseover() {
d3.select(this).select("path").transition()
.duration(750)
.attr("d", arcFinal3);
}
function mouseout() {
d3.select(this).select("path").transition()
.duration(750)
.attr("d", arcFinal);
}
function up(d, i) {
updateBarChart(d.data.category, color(i));
updateLineChart(d.data.category, color(i));
}
}
#pieChart {
position: absolute;
top: 10px;
left: 10px;
width: 400px;
height: 400px;
}
.slice {
font-size: 12pt;
font-family: Verdana;
fill: white;
font-weight: bold;
}
.title {
font-family: Verdana;
font-size: 15px;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id="pieChart"></div>
See also Bar chart from external JSON file example.

In D3 bar graph, y-axis ordinal scale is not aligning properly with bars

I was following Mike's tutorial Let's make a bar graph part II, part III for the bar graph in d3.
I end up with a horizontal bar graph.
The Problem is I'm not able to set the y-axis labels aligned with its respective bar, maybe I'm somewhere wrong with ordinal scale or setting up the y position of the bars.
Also, the graph bars are starting from the top instead of baseline.
var options = {
margin: {
top: 0,
right: 30,
bottom: 0,
left: 50
},
width: 560,
height: 400,
element: 'body',
colors: ["#f44336", "#e91e63", "#673ab7", "#3f51b5", "#2196f3", "#03a9f4", "#00bcd4", "#009688", "#4caf50", "#8bc34a", "#cddc39", "#ffeb3b", "#ffc107", "#ff9800", "#ff5722", "#795548", "#607d8b"]
};
options.mWidth = options.width - options.margin.left - options.margin.right;
options.mHeight = options.height - options.margin.top - options.margin.bottom;
var _colorScale = d3.scale.ordinal()
// .domain([minValue,maxValue])
.range(options.colors);
var items = Math.round((Math.random() * 10) + 3);
var data = [];
for (var i = 0; i < items; i++) {
data.push({
label: 'label' + i,
value: Math.random() * 100
});
}
var _barThickness = 20;
var width = options.mWidth,
height = options.mHeight;
var svg = d3.select(options.element)
.append("svg")
.attr("width", width).attr("height", height)
.attr("viewBox", "0 0 " + options.width + " " + options.height)
//.attr("preserveAspectRatio", "xMinYMin meet")
.append("g");
svg.attr("transform", "translate(" + options.margin.left * 2 + "," + options.margin.top + ")");
var maxValue = 0,
minValue = 0;
for (var i = 0; i < data.length; i++) {
maxValue = Math.max(maxValue, data[i].value);
minValue = Math.min(minValue, data[i].value);
}
var scales = {
x: d3.scale.linear().domain([0, maxValue]).range([0, width / 2]),
y: d3.scale.ordinal().rangeRoundBands([0, height], .1)
}
scales.y.domain(data.map(function(d) {
return d.label;
}));
var bars = svg.append("g")
.attr("class", "bar");
var xAxis = d3.svg.axis()
.scale(scales.x)
.orient("bottom")
.ticks(5);
var yAxis = d3.svg.axis()
.scale(scales.y)
.orient("left");
var xaxis = svg.append("g")
.attr("class", "x axis")
.attr('transform', 'translate(' + 0 + ',' + height + ')')
.call(xAxis)
.append("text")
.attr("x", width / 2)
.attr("dy", "3em")
.style("text-anchor", "middle")
.text("Durations in hours");
var yaxis = svg.append("g")
.attr("class", "y axis")
.attr('transform', 'translate(' + 0 + ',' + 0 + ')')
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", -options.margin.left)
.attr("x", -height / 2)
.attr("dy", ".71em")
.style("text-anchor", "middle")
.text("Labels");
var bar = bars.selectAll('rect.bar').data(data);
var rect = bar.enter()
.append('g')
.attr('transform', function(d, i) {
return 'translate(0,' + parseInt(i * _barThickness + 2) + ')';
});
rect.append('rect')
.attr('class', 'bar')
.attr('fill', function(d) {
return _colorScale(d.value);
})
.attr('width', function(d) {
return scales.x(d.value);
})
.attr('height', _barThickness - 1)
.attr('y', function(d, i) {
return (i * _barThickness);
})
rect.append('text')
.attr('x', function(d) {
return scales.x(d.value) + 2;
})
.attr('y', function(d, i) {
return (i * _barThickness);
})
.attr('dy', '0.35em')
.text(function(d) {
return Math.round(d.value);
});
svg {
width: 100%;
height: 100%;
}
text {
font-size: 0.8em;
line-height: 1em;
}
path.slice {
stroke-width: 2px;
}
polyline {
opacity: .3;
stroke: black;
stroke-width: 2px;
fill: none;
}
.axis {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
Problem 1:
Instead of this:
var _barThickness = 20;
Do this:
var _barThickness = scales.y.rangeBand();
Read here
If you want to regulate the thickness
Instead of this:
y: d3.scale.ordinal().rangeRoundBands([0, height], .1)
Do this:
y: d3.scale.ordinal().rangeRoundBands([0, height], .7)
Problem 2:
Incorrect transform.
Instead of this:
var rect = bar.enter()
.append('g')
.attr('transform', function(d, i) {
return 'translate(0,' + parseInt(i * _barThickness + 2) + ')';
});
Do this:
var rect = bar.enter()
.append('g');
Problem 3:
Incorrect calculation of y for the bars.
.attr('y', function(d, i) {
return (i * _barThickness);
})
Do this:
.attr('y', function(d, i) {
return scales.y(d.label);
})
working code here

Tooltip for svg heatmap/colorscale in d3js

I am adapting this gradient example in AngularJS. Here is a bit of the dataset I'm using:
var stays=[
{
day:2,
hour:1,
time_spent:127
},
{
day:4,
hour:1,
time_spent:141
},
{
day:1,
hour:1,
time_spent:134
},
{
day:5,
hour:1,
time_spent:174
},
{
day:3,
hour:1,
time_spent:131
},
{
day:6,
hour:1,
time_spent:333
}];
The problem is that I want to construct a tooltip for each of the squares that are created in the heatmap. The tooltip is here:
var heatMap = svg.selectAll(".hour")
.data(stays)
.enter().append("rect")
.attr("x", function(d) { return (d.hour - 1) * gridSize; })
.attr("y", function(d) { return (d.day - 1) * gridSize; })
.attr("class", "hour bordered")
.attr("width", gridSize)
.attr("height", gridSize)
.style("stroke", "white")
.style("stroke-opacity", 0.6)
.style("stroke-width", 0.8)
.style("fill", function(d) { return colorScale(d.time_spent); })
.on("mouseover", function(d, i) {
// Construct tooltip
var tooltip_html = '';
tooltip_html += '<div class="header"><strong>' + 'Stays' + ' </strong></div><br>';
// Add info to the tooltip
angular.forEach(stays, function (d) {
tooltip_html += '<div><span><strong>' + makeid() + '</strong></span>';
tooltip_html += '<span>' + ' ' + d.time_spent + '</span></div>';
console.log(d.time_spent);
}, days);
// Set tooltip width
tooltip.html(tooltip_html)
.style("width", 300 + "px")
.style("left", (d3.event.layerX+10) + "px")
.style("top", (d3.event.layerY+10) + "px");
// Tooltip transition and more styling
tooltip.style('display', 'block')
.transition()
.ease('ease-in')
.duration(100)
.style("opacity", .9);
})
.on("mouseout", function(d) {
tooltip.transition()
.duration(100)
.ease('ease-in')
.style('opacity', 0);
});
The idea here is, for each square that I visit, the tooltip will show the labels associated (I'm still not using labels but they are going to be part of the dataset I've shown, I'm using the makeid() function to create random names) to that square, together with a breakdown of the associated time_spent data. With what I'm using now it is writing the full list of numbers and not the ones associated to each square. Ideas? Thank you.
Rather than building tootip HTML for each cell, consider attaching the data to each cell rect. Create a single data div and fill it when the cursor moves over the cell.
I added this to the heatmap gradient example. Give it a try.
var accidents=[{day:2,hour:1,count:127},{day:4,hour:1,count:141},{day:1,hour:1,count:134},{day:5,hour:1,count:174},{day:3,hour:1,count:131},{day:6,hour:1,count:333},{day:7,hour:1,count:311},{day:2,hour:2,count:79},{day:4,hour:2,count:99},{day:1,hour:2,count:117},{day:5,hour:2,count:123},{day:3,hour:2,count:92},{day:6,hour:2,count:257},{day:7,hour:2,count:293},{day:2,hour:3,count:55},{day:4,hour:3,count:73},{day:1,hour:3,count:107},{day:5,hour:3,count:89},{day:3,hour:3,count:66},{day:6,hour:3,count:185},{day:7,hour:3,count:262},{day:2,hour:4,count:39},{day:4,hour:4,count:67},{day:1,hour:4,count:59},{day:5,hour:4,count:83},{day:3,hour:4,count:45},{day:6,hour:4,count:180},{day:7,hour:4,count:220},{day:2,hour:5,count:48},{day:4,hour:5,count:57},{day:1,hour:5,count:73},{day:5,hour:5,count:76},{day:3,hour:5,count:72},{day:6,hour:5,count:168},{day:7,hour:5,count:199},{day:2,hour:6,count:129},{day:4,hour:6,count:102},{day:1,hour:6,count:129},{day:5,hour:6,count:140},{day:3,hour:6,count:117},{day:6,hour:6,count:148},{day:7,hour:6,count:193},{day:2,hour:7,count:314},{day:4,hour:7,count:284},{day:1,hour:7,count:367},{day:5,hour:7,count:270},{day:3,hour:7,count:310},{day:6,hour:7,count:179},{day:7,hour:7,count:192},{day:2,hour:8,count:806},{day:4,hour:8,count:811},{day:1,hour:8,count:850},{day:5,hour:8,count:609},{day:3,hour:8,count:846},{day:6,hour:8,count:208},{day:7,hour:8,count:144},{day:2,hour:9,count:1209},{day:4,hour:9,count:1214},{day:1,hour:9,count:1205},{day:5,hour:9,count:960},{day:3,hour:9,count:1073},{day:6,hour:9,count:286},{day:7,hour:9,count:152},{day:2,hour:10,count:750},{day:4,hour:10,count:808},{day:1,hour:10,count:610},{day:5,hour:10,count:655},{day:3,hour:10,count:684},{day:6,hour:10,count:482},{day:7,hour:10,count:253},{day:2,hour:11,count:591},{day:4,hour:11,count:593},{day:1,hour:11,count:573},{day:5,hour:11,count:695},{day:3,hour:11,count:622},{day:6,hour:11,count:676},{day:7,hour:11,count:326},{day:2,hour:12,count:653},{day:4,hour:12,count:679},{day:1,hour:12,count:639},{day:5,hour:12,count:736},{day:3,hour:12,count:687},{day:6,hour:12,count:858},{day:7,hour:12,count:402},{day:2,hour:13,count:738},{day:4,hour:13,count:749},{day:1,hour:13,count:631},{day:5,hour:13,count:908},{day:3,hour:13,count:888},{day:6,hour:13,count:880},{day:7,hour:13,count:507},{day:2,hour:14,count:792},{day:4,hour:14,count:847},{day:1,hour:14,count:752},{day:5,hour:14,count:1033},{day:3,hour:14,count:942},{day:6,hour:14,count:983},{day:7,hour:14,count:636},{day:2,hour:15,count:906},{day:4,hour:15,count:1031},{day:1,hour:15,count:954},{day:5,hour:15,count:1199},{day:3,hour:15,count:1014},{day:6,hour:15,count:1125},{day:7,hour:15,count:712},{day:2,hour:16,count:1101},{day:4,hour:16,count:1158},{day:1,hour:16,count:1029},{day:5,hour:16,count:1364},{day:3,hour:16,count:1068},{day:6,hour:16,count:1062},{day:7,hour:16,count:736},{day:2,hour:17,count:1303},{day:4,hour:17,count:1426},{day:1,hour:17,count:1270},{day:5,hour:17,count:1455},{day:3,hour:17,count:1407},{day:6,hour:17,count:883},{day:7,hour:17,count:666},{day:2,hour:18,count:1549},{day:4,hour:18,count:1653},{day:1,hour:18,count:1350},{day:5,hour:18,count:1502},{day:3,hour:18,count:1507},{day:6,hour:18,count:830},{day:7,hour:18,count:652},{day:2,hour:19,count:998},{day:4,hour:19,count:1070},{day:1,hour:19,count:787},{day:5,hour:19,count:1027},{day:3,hour:19,count:1019},{day:6,hour:19,count:575},{day:7,hour:19,count:519},{day:2,hour:20,count:661},{day:4,hour:20,count:756},{day:1,hour:20,count:596},{day:5,hour:20,count:730},{day:3,hour:20,count:648},{day:6,hour:20,count:494},{day:7,hour:20,count:486},{day:2,hour:21,count:431},{day:4,hour:21,count:539},{day:1,hour:21,count:430},{day:5,hour:21,count:509},{day:3,hour:21,count:457},{day:6,hour:21,count:443},{day:7,hour:21,count:421},{day:2,hour:22,count:352},{day:4,hour:22,count:428},{day:1,hour:22,count:362},{day:5,hour:22,count:462},{day:3,hour:22,count:390},{day:6,hour:22,count:379},{day:7,hour:22,count:324},{day:2,hour:23,count:329},{day:4,hour:23,count:381},{day:1,hour:23,count:293},{day:5,hour:23,count:393},{day:3,hour:23,count:313},{day:6,hour:23,count:374},{day:7,hour:23,count:288},{day:2,hour:24,count:211},{day:4,hour:24,count:249},{day:1,hour:24,count:204},{day:5,hour:24,count:417},{day:3,hour:24,count:211},{day:6,hour:24,count:379},{day:7,hour:24,count:203}];
///////////////////////////////////////////////////////////////////////////
//////////////////// Set up and initiate svg containers ///////////////////
///////////////////////////////////////////////////////////////////////////
var days = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
times = d3.range(24);
var margin = {
top: 170,
right: 50,
bottom: 70,
left: 50
};
var width = Math.max(Math.min(window.innerWidth, 1000), 500) - margin.left - margin.right - 20,
gridSize = Math.floor(width / times.length),
height = gridSize * (days.length+2);
//SVG container
var svg = d3.select('#trafficAccidents')
.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 + ")");
//Reset the overall font size
var newFontSize = width * 62.5 / 900;
d3.select("html").style("font-size", newFontSize + "%");
///////////////////////////////////////////////////////////////////////////
//////////////////////////// Draw Heatmap /////////////////////////////////
///////////////////////////////////////////////////////////////////////////
//Based on the heatmap example of: http://blockbuilder.org/milroc/7014412
var colorScale = d3.scale.linear()
.domain([0, d3.max(accidents, function(d) {return d.count; })/2, d3.max(accidents, function(d) {return d.count; })])
.range(["#FFFFDD", "#3E9583", "#1F2D86"])
//.interpolate(d3.interpolateHcl);
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 >= 8 && i <= 17) ? "timeLabel mono axis axis-worktime" : "timeLabel mono axis"); });
var heatMap = svg.selectAll(".hour")
.data(accidents)
.enter().append("rect")
//----attach data to rect---
.attr("data","This is the data for this cell")
.attr("onmouseover","showData(evt)")
.attr("onmouseout","hideData(evt)")
.attr("x", function(d) { return (d.hour - 1) * gridSize; })
.attr("y", function(d) { return (d.day - 1) * gridSize; })
.attr("class", "hour bordered")
.attr("width", gridSize)
.attr("height", gridSize)
.style("stroke", "white")
.style("stroke-opacity", 0.6)
.style("fill", function(d) { return colorScale(d.count); });
//Append title to the top
svg.append("text")
.attr("class", "title")
.attr("x", width/2)
.attr("y", -90)
.style("text-anchor", "middle")
.text("Number of Traffic accidents per Day & Hour combination");
svg.append("text")
.attr("class", "subtitle")
.attr("x", width/2)
.attr("y", -60)
.style("text-anchor", "middle")
.text("The Netherlands | 2014");
//Append credit at bottom
svg.append("text")
.attr("class", "credit")
.attr("x", width/2)
.attr("y", gridSize * (days.length+1) + 80)
.style("text-anchor", "middle")
.text("Based on Miles McCrocklin's Heatmap block");
///////////////////////////////////////////////////////////////////////////
//////////////// Create the gradient for the legend ///////////////////////
///////////////////////////////////////////////////////////////////////////
//Extra scale since the color scale is interpolated
var countScale = d3.scale.linear()
.domain([0, d3.max(accidents, function(d) {return d.count; })])
.range([0, width])
//Calculate the variables for the temp gradient
var numStops = 10;
countRange = countScale.domain();
countRange[2] = countRange[1] - countRange[0];
countPoint = [];
for(var i = 0; i < numStops; i++) {
countPoint.push(i * countRange[2]/(numStops-1) + countRange[0]);
}//for i
//Create the gradient
svg.append("defs")
.append("linearGradient")
.attr("id", "legend-traffic")
.attr("x1", "0%").attr("y1", "0%")
.attr("x2", "100%").attr("y2", "0%")
.selectAll("stop")
.data(d3.range(numStops))
.enter().append("stop")
.attr("offset", function(d,i) {
return countScale( countPoint[i] )/width;
})
.attr("stop-color", function(d,i) {
return colorScale( countPoint[i] );
});
///////////////////////////////////////////////////////////////////////////
////////////////////////// Draw the legend ////////////////////////////////
///////////////////////////////////////////////////////////////////////////
var legendWidth = Math.min(width*0.8, 400);
//Color Legend container
var legendsvg = svg.append("g")
.attr("class", "legendWrapper")
.attr("transform", "translate(" + (width/2) + "," + (gridSize * days.length + 40) + ")");
//Draw the Rectangle
legendsvg.append("rect")
.attr("class", "legendRect")
.attr("x", -legendWidth/2)
.attr("y", 0)
//.attr("rx", hexRadius*1.25/2)
.attr("width", legendWidth)
.attr("height", 10)
.style("fill", "url(#legend-traffic)");
//Append title
legendsvg.append("text")
.attr("class", "legendTitle")
.attr("x", 0)
.attr("y", -10)
.style("text-anchor", "middle")
.text("Number of Accidents");
//Set scale for x-axis
var xScale = d3.scale.linear()
.range([-legendWidth/2, legendWidth/2])
.domain([ 0, d3.max(accidents, function(d) { return d.count; })] );
//Define x-axis
var xAxis = d3.svg.axis()
.orient("bottom")
.ticks(5)
//.tickFormat(formatPercent)
.scale(xScale);
//Set up X axis
legendsvg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + (10) + ")")
.call(xAxis);
//--show/hide data---
function showData(evt)
{
var target=evt.target
target.setAttribute("opacity",".8")
//---locate dataDiv near cursor--
var x = evt.clientX;
var y = evt.clientY;
//---scrolling page---
var offsetX=window.pageXOffset
var offsetY=window.pageYOffset
dataDiv.style.left=10+x+offsetX+"px"
dataDiv.style.top=20+y+offsetY+"px"
//---data--
var data=target.getAttribute("data")
//---format as desired---
var html=data
dataDiv.innerHTML=html
dataDiv.style.visibility="visible"
}
function hideData(evt)
{
dataDiv.style.visibility="hidden"
var target=evt.target
target.removeAttribute("opacity")
}
<head>
<meta charset="utf-8">
<!-- D3.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="utf-8"></script>
<!-- Google Font -->
<link href='http://fonts.googleapis.com/css?family=Open+Sans:300,400' rel='stylesheet' type='text/css'>
<style>
html { font-size: 62.5%; }
body {
font-size: 1rem;
font-family: 'Open Sans', sans-serif;
font-weight: 400;
fill: #8C8C8C;
text-align: center;
}
.timeLabel, .dayLabel {
font-size: 1.6rem;
fill: #AAAAAA;
font-weight: 300;
}
text.axis-workweek, text.axis-worktime {
fill: #404040;
font-weight: 400;
}
.title {
font-size: 2.8rem;
fill: #4F4F4F;
font-weight: 300;
}
.subtitle {
font-size: 1.4rem;
fill: #AAAAAA;
font-weight: 300;
}
.credit {
font-size: 1.2rem;
fill: #AAAAAA;
font-weight: 400;
}
.axis path, .axis tick, .axis line {
fill: none;
stroke: none;
}
text {
font-size: 1.2rem;
fill: #AAAAAA;
font-weight: 400;
}
.legendTitle {
font-size: 1.6rem;
fill: #4F4F4F;
font-weight: 300;
}
</style>
</head>
<body>
<div id="trafficAccidents"></div>
<div id=dataDiv style='box-shadow: 4px 4px 4px #888888;-webkit-box-shadow:2px 3px 4px #888888;padding:2px;position:absolute;top:0px;left:0px;visibility:hidden;background-color:linen;border: solid 1px black;border-radius:5px;'></div>
</body>
I think you may be making this too complex.
Try using foxToolTip.js
https://github.com/MichaelRFox/foxToolTip.jS
After you create your unique I'd just use the .each method to add a unique tooltip.

Categories