D3.js Bar Chart - javascript

HTML
<div id="searchVolume"></div>
CSS
#tooltip {
position: absolute;
width: 50px;
height: auto;
padding: 10px;
background-color: white;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
-webkit-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
-moz-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
pointer-events: none;
}
#tooltip.hidden {
display: none;
}
#tooltip p {
margin: 0;
font-family: sans-serif;
font-size: 12px;
line-height: 16px;
}
.indent{
padding-left: 5px;
}
rect {
-moz-transition: all 0.3s;
-webkit-transition: all 0.3s;
-o-transition: all 0.3s;
transition: all 0.3s;
}
rect:hover{
fill: orange;
}
.axis path,
.axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
}
Script
var margin = {top: 25, right: 40, bottom: 35, left: 85},
w = 500 - margin.left - margin.right,
h = 350 - margin.top - margin.bottom;
var padding = 10;
var colors = [ ["Morning", "#F64BEE"],
["Midday", "#25B244"],
["Afternoon", "#2BA3F4"],
["Evening","#FD7680"]];
var dataset = [
{ "Morning": 1400000, "Midday": 673000, "Afternoon": 43000, "Evening":50000},
{ "Morning": 165000, "Midday": 160000, "Afternoon": 21000, "Evening":23000 },
{"Morning": 550000, "Midday": 301000, "Afternoon": 34000, "Evening":43000},
{"Morning": 550320, "Midday": 351000, "Afternoon": 24000, "Evening":38000},
{"Morning": 55000, "Midday": 3010, "Afternoon": 24000, "Evening":43054},
{"Morning": 750000, "Midday": 401000, "Afternoon": 84000, "Evening":42100},
{"Morning": 578000, "Midday": 306000, "Afternoon": 54000, "Evening":43400},
];
var xScale = d3.scale.ordinal()
.domain(d3.range(dataset.length))
.rangeRoundBands([0, w], 0.05);
// ternary operator to determine if global or local has a larger scale
var yScale = d3.scale.linear()
.domain([0, d3.max(dataset, function(d) { return Math.max(d.Morning,d.Midday,d.Afternoon,d.Evening);})])
.range([h, 0]);
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
.ticks(5);
var commaFormat = d3.format(',');
//SVG element
var svg = d3.select("#searchVolume")
.append("svg")
.attr("width", w + margin.left + margin.right)
.attr("height", h + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// Graph Bars
var sets = svg.selectAll(".set")
.data(dataset)
.enter()
.append("g")
.attr("class","set")
.attr("transform",function(d,i){
return "translate(" + xScale(i) + ",0)";
})
;
sets.append("rect")
.attr("class","Morning")
.attr("width", xScale.rangeBand()/4)
.attr("y", function(d) {
return yScale(d.Morning);
})
.attr("x", xScale.rangeBand()/4)
.attr("height", function(d){
return h - yScale(d.Morning);
})
.attr("fill", colors[0][1])
.append("text")
.text(function(d) {
return commaFormat(d.Morning);
})
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return xScale(i) + xScale.rangeBand() / 4;
})
.attr("y", function(d) {
return h - yScale(d.Morning) + 14;
})
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "black")
;
sets.append("rect")
.attr("class","Midday")
.attr("width", xScale.rangeBand()/4)
.attr("y", function(d) {
return yScale(d.Midday);
})
.attr("height", function(d){
return h - yScale(d.Midday);
})
.attr("fill", colors[1][1])
.append("text")
.text(function(d) {
return commaFormat(d.Midday);
})
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return xScale(i) + xScale.rangeBand() / 4;
})
.attr("y", function(d) {
return h - yScale(d.Midday) + 14;
})
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "red")
;
sets.append("rect")
.attr("class","Afternoon")
.attr("width", xScale.rangeBand()/4)
.attr("y", function(d) {
return yScale(d.Afternoon);
})
.attr("height", function(d){
return h - yScale(d.Afternoon);
})
.attr("fill", colors[2][1])
.append("text")
.text(function(d) {
return commaFormat(d.Afternoon);
})
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return xScale(i) + xScale.rangeBand() / 4;
})
.attr("y", function(d) {
return h - yScale(d.Afternoon) + 14;
})
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "red")
;
sets.append("rect")
.attr("class","Evening")
.attr("width", xScale.rangeBand()/4)
.attr("y", function(d) {
return yScale(d.Evening);
})
.attr("height", function(d){
return h - yScale(d.Evening);
})
.attr("fill", colors[3][1])
.append("text")
.text(function(d) {
return commaFormat(d.Evening);
})
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return xScale(i) + xScale.rangeBand() / 4;
})
.attr("y", function(d) {
return h - yScale(d.Evening) + 14;
})
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "red")
;
// xAxis
svg.append("g") // Add the X Axis
.attr("class", "x axis")
.attr("transform", "translate(0," + (h) + ")")
.call(xAxis)
;
// yAxis
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(0 ,0)")
.call(yAxis)
;
// xAxis label
svg.append("text")
.attr("transform", "translate(" + (w / 4) + " ," + (h + margin.bottom - 5) +")")
.style("text-anchor", "middle")
.text("Keyword");
//yAxis label
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left)
.attr("x", 0 - (h / 4))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Searches");
// Title
svg.append("text")
.attr("x", (w / 2))
.attr("y", 0 - (margin.top / 2))
.attr("text-anchor", "middle")
.style("font-size", "16px")
.style("text-decoration", "underline")
.text("Weekly Consumption");
// add legend
var legend = svg.append("g")
.attr("class", "legend")
//.attr("x", w - 65)
//.attr("y", 50)
.attr("height", 100)
.attr("width", 100)
.attr('transform', 'translate(-20,50)');
var legendRect = legend.selectAll('rect').data(colors);
legendRect.enter()
.append("rect")
.attr("x", w - 65)
.attr("width", 10)
.attr("height", 10);
legendRect
.attr("y", function(d, i) {
return i * 20;
})
.style("fill", function(d) {
return d[1];
});
var legendText = legend.selectAll('text').data(colors);
legendText.enter()
.append("text")
.attr("x", w - 52);
legendText
.attr("y", function(d, i) {
return i * 20 + 9;
})
.text(function(d) {
return d[0];
});
D3 Fiddle
In the above fiddle, I have attempted to make a bar chart using d3.js library. I am stuck on following basic things which shouldn't even take much time. I am not able to grasp the functions of d3. You can play around with the fiddle and any help would be hugely beneficial:
1. I want four different bars grouped on a single x value. Like for '0' there would be four of them unlike the current one where it is merging everything into two.
2. Change the content of x-axis from numbers to days like from Monday to Friday.
3. For y-axis, I am trying to display the values like, instead of 20000, it should show 20k and the bar should recognise that while dynamically creating it. Is this possible?
Any help would be greatly beneficial. I couldn't figure it out.

The main issue is that you start joining your data correctly but afterwards you start doing some manual stuff that could have been solved by using the data function to join the data into your child elements. Let me explain:
First of all we will need two x axis scales, one to hold our days domain and the other one will hold our time domain using as rangeRoundBands the days scale.
var day_scale = d3.scale.ordinal()
.domain(d3.range(dataset.length))
.rangeRoundBands([0, w], 0.05);
var time_scale = d3.scale.ordinal();
time_scale.domain(['Morning', 'Midday', 'Afternoon', 'Evening'])
.rangeRoundBands([0, day_scale.rangeBand()]);
Let's tackle the x axis formatting while we are setting up our scales. I created an array of days and within our tickFormat function lets return the value in the array based in the index of the data we passed.
var days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
var day_axis = d3.svg.axis()
.scale(day_scale)
.orient("bottom")
.tickFormat(function(d,i) {
return days[i];
});
Now the y axis formatting, we can solve this by using a d3.formatPrefix more info here
var prefix = d3.formatPrefix(1.21e9);
var searches_axis = d3.svg.axis()
.scale(searches_scale)
.orient("left")
.ticks(5)
.tickFormat(function(d) {
var prefix = d3.formatPrefix(d);
return prefix.scale(d) + prefix.symbol;
});
Now lets skip our svg and axis config, to get to the data join issue:
var day_groups = svg.selectAll(".day-group")
.data(dataset) // join data to our selection
.enter().append("g")
.attr("class", function(d, i) {
return 'day-group day-group-' + i;
})
.attr("transform", function(d, i) {
// position a g element with our day_scale and data index
return "translate(" + day_scale(i) + ",0)";
});
With our day-groups positioned correctly now we are able to append our time data.
var times_g = day_groups.selectAll(".time-group")
.data(function(d) {
// this is the tricky part, we are creating an array of
// objects with key (time...'Morning', 'Midday', 'Afternoon', 'Evening')
// and value (the value of the time)
// in order to create a time group for each time event
return Object.keys(d).map(function(key) {
return {
key: key,
value: d[key]
}
});
})
.enter().append("g")
.attr("class", function(d) {
return 'time-group time-group-' + d.key;
})
.attr("transform", function(d) {
// use our time scale to position
return "translate(" + time_scale(d.key) + ",0)";
});
Now lets add our rects!
var rects = times_g.selectAll('.rect')
.data(function(d) {
// use as data our object
return [d];
})
.enter().append("rect")
.attr("class", "rect")
.attr("width", time_scale.rangeBand()) // get width of rect based in our time_scale
.attr("x", function(d) {
return 0; // returning 0 since the group is in charge of positioning
})
.attr("y", function(d) {
return searches_scale(d.value); // use our y_scale
})
.attr("height", function(d) {
return h - searches_scale(d.value); // use our y_scale
})
.style("fill", function(d) {
return colors[d.key]; // map colors by using an object
});
Mapping color object:
var colors = {
"Morning":"#F64BEE",
"Midday": "#25B244",
"Afternoon": "#2BA3F4",
"Evening": "#FD7680"
};
If you have any doubt of how this works here is the updated jsfiddle: https://jsfiddle.net/706gsjfg/3/ (I removed certain things, but you can add them later I guess)

Related

How to add text-based legend to D3.js chart

I was just wondering how to add a box with text in it to my D3.js chart. It will be acting as my legend, and will describe each variable on my x-axis. It should look something like this like this .
I would like to match each of the "wuc" labels to the "nomenclature" labels in this json array:
data = [{
"Wuc": "23A",
"Nomenclature": "Engine, Basic (F117-PW)",
"Hours": 155899.90
},
{
"Wuc": "23V",
"Nomenclature": "F‌​an Thrust Reverser",
"Hours": 56576
}
]
The bottom code is for my D3.js chart. Thanks for the help!
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<style>
.bars:hover {
fill: blue;
}
.legend:hover {
fill: blue;
}
/* tooltip for bar chart */
div.tooltip {
position: absolute;
text-align: center;
width: 50px;
height: 60px;
padding: 2px;
font: 12px sans-serif;
background: lightsteelblue;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
</style>
<div id="bar_chart">
</div>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
// d3.json("data.json", function(error, data) {
// if (error) throw error;
// var parseTime = d3.timeParse("%M:%S");
// var timeformat = d3.timeFormat("%M:%S")
data = [{
"Wuc": "23A",
"Nomenclature": "Engine, Basic (F117-PW)",
"Hours": 155899.90
},
{
"Wuc": "23V",
"Nomenclature": "F‌​an Thrust Reverser",
"Hours": 56576
}
]
data.forEach(function(d) {
// d.atime = parseTime(d.atime);
d.Hours = +d.Hours;
});
var margin = {
top: 70,
right: 50,
bottom: 100,
left: 80
},
width = 600 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom;
//Define the div for the tooltip
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
var x = d3.scaleBand()
.domain(data.map(function(d) {
return d.Wuc
}))
.range([0, width])
.padding([0.8]); //sets decimal of the space between bar centres
var y = d3.scaleLinear()
.domain([0, d3.max(data, function(d) {
return d.Hours
})])
.range([height, 0]);
var svg = d3.select("#bar_chart")
.data(data)
.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 + ")");
// Add the X Axis
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// text label for the x axis
svg.append("text")
.attr("x", width / 2)
.attr("y", margin.top + height)
.style("text-anchor", "middle")
.style("font-weight", "bold")
.text("x-Axis Title");
// Add the Y Axis
svg.append("g")
.attr("class", "axis")
.call(d3.axisLeft(y)
.ticks(5));
// text label for the y axis
svg.append("text")
.attr("class", "blah")
.attr("transform", "rotate(-90)")
.attr("x", 0 - height / 2)
.attr("y", -50)
.style("text-anchor", "middle")
.style("font-weight", "bold")
.text("y-Axis Title");
// graph main title
svg.append("text")
.attr("x", width / 2)
.attr("y", -20)
.style("text-anchor", "middle")
.style("font-weight", "bold")
.style("font-size", "20px")
.text("Main Title");
//********* Bar Chart ****************
var rects = svg.selectAll('rect')
.data(data)
.enter();
rects.append('rect')
.on("mouseover", function(d, i, node) { //this is repeated should be in a function
div.transition()
.duration(200)
.style("opacity", .85);
div.html("<strong> Name:</strong> " + d.Wuc + "</br><strong> Value:</strong> " + d.Hours)
.style("left", (d3.event.pageX + 5) + "px")
.style("top", (d3.event.pageY - 28) + "px");
d3.select(this)
.style("fill", "blue");
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
d3.select(this)
.style("fill", "lightblue");
})
.attr("class", "bars") //should fill blue on mouseover, not working???
.attr('x', function(d, i) {
return x(d.Wuc);
})
.attr('y', function(d, i) {
return y(d.Hours);
})
.attr('height', function(d, i) {
return height - y(d.Hours)
})
.attr('width', x.bandwidth())
.attr("transform", "translate(0,0)")
.style('fill', 'lightblue')
.style('stroke', 'lightgray');
// }); //closes function d3.json("data.json", function(error, data) {.....
</script>
You can simply append a 'rect' to the svg and also append text with data.
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<style>
.bars:hover {
fill: blue;
}
.legend:hover {
fill: lightblue;
}
/* tooltip for bar chart */
div.tooltip {
position: absolute;
text-align: center;
width: 50px;
height: 60px;
padding: 2px;
font: 12px sans-serif;
background: lightsteelblue;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
</style>
<div id="bar_chart">
</div>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
// d3.json("data.json", function(error, data) {
// if (error) throw error;
// var parseTime = d3.timeParse("%M:%S");
// var timeformat = d3.timeFormat("%M:%S")
data = [{
"Wuc": "23A",
"Nomenclature": "Engine, Basic (F117-PW)",
"Hours": 155899.90
},
{
"Wuc": "23V",
"Nomenclature": "F‌​an Thrust Reverser",
"Hours": 56576
}
]
data.forEach(function(d) {
// d.atime = parseTime(d.atime);
d.Hours = +d.Hours;
});
var margin = {
top: 70,
right: 50,
bottom: 100,
left: 80
},
width = 600 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom;
//Define the div for the tooltip
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
var x = d3.scaleBand()
.domain(data.map(function(d) {
return d.Wuc
}))
.range([0, width])
.padding([0.8]); //sets decimal of the space between bar centres
var y = d3.scaleLinear()
.domain([0, d3.max(data, function(d) {
return d.Hours
})])
.range([height, 0]);
var svg = d3.select("#bar_chart")
.data(data)
.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 + ")");
// Add the X Axis
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// text label for the x axis
svg.append("text")
.attr("x", width / 2)
.attr("y", margin.top + height)
.style("text-anchor", "middle")
.style("font-weight", "bold")
.text("x-Axis Title");
// Add the Y Axis
svg.append("g")
.attr("class", "axis")
.call(d3.axisLeft(y)
.ticks(5));
// text label for the y axis
svg.append("text")
.attr("class", "blah")
.attr("transform", "rotate(-90)")
.attr("x", 0 - height / 2)
.attr("y", -50)
.style("text-anchor", "middle")
.style("font-weight", "bold")
.text("y-Axis Title");
// graph main title
svg.append("text")
.attr("x", width / 2)
.attr("y", -20)
.style("text-anchor", "middle")
.style("font-weight", "bold")
.style("font-size", "20px")
.text("Main Title");
//************* Legend ***************
// add legend rect gray
svg.append("rect")
.attr("class", "legend")
.attr("x", 280)
.attr("y", 30)
.attr("rx", "5px")
.attr("width", 230)
.attr("height", 80)
.attr("stroke", "darkgray")
.attr("fill", "white");
var legend_text = svg.selectAll("legend_text")
.data(data)
.enter();
legend_text.append("text")
.attr("class", "legend_text")
.attr("x", 290)
.attr("y", function(d, i) {
return (i * 20) + 40;
})
.attr("dy", "0.32em")
.style("font-weight", "bold")
.text(function(d) {
return d.Wuc + " " + d.Nomenclature;
});
//********* Bar Chart ****************
var rects = svg.selectAll(".bars")
.data(data)
.enter();
rects.append('rect')
.attr("class", "bars")
.on("mouseover", function(d, i, node) { //this is repeated should be in a function
div.transition()
.duration(200)
.style("opacity", .85);
div.html("<strong> Name:</strong> " + d.Wuc + "</br><strong> Value:</strong> " + d.Hours)
.style("left", (d3.event.pageX + 5) + "px")
.style("top", (d3.event.pageY - 28) + "px");
d3.select(this)
.style("fill", "blue");
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
d3.select(this)
.style("fill", "lightblue");
})
.attr("class", "bars") //should fill blue on mouseover, not working???
.attr('x', function(d, i) {
return x(d.Wuc);
})
.attr('y', function(d, i) {
return y(d.Hours);
})
.attr('height', function(d, i) {
return height - y(d.Hours)
})
.attr('width', x.bandwidth())
.attr("transform", "translate(0,0)")
.style('fill', 'lightblue')
.style('stroke', 'lightgray');
// }); //closes function d3.json("data.json", function(error, data) {.....
</script>
You may also want to make the height of the rect dynamic to fit different amounts of of data.

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.

Tooltip displaying undefined values

I am having some issues with tooltips for my bar graph. For each stopname column, there should be a corresponding value of boarding and alightings shown in the tooltip, however, I am getting undefined values like here
Here is my code:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.bar {
fill: orange;
}
.bar:hover {
fill: orangered ;
}
.x.axis path {
display: none;
}
.d3-tip {
line-height: 1;
font-weight: bold;
padding: 12px;
background: rgba(0, 0, 0, 0.8);
color: #fff;
border-radius: 2px;
}
/* Creates a small triangle extender for the tooltip */
.d3-tip:after {
box-sizing: border-box;
display: inline;
font-size: 10px;
width: 100%;
line-height: 1;
color: rgba(0, 0, 0, 0.8);
content: "\25BC";
position: absolute;
text-align: center;
}
/* Style northward tooltips differently */
.d3-tip.n:after {
margin: -1px 0 0 0;
top: 100%;
left: 0;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://labratrevenge.com/d3-tip/javascripts/d3.tip.v0.6.3.js"></script>
<script>
var margin = {top: 40, right: 20, bottom: 300, left: 40},
width = 1250 - margin.left - margin.right,
height = 750 - margin.top - margin.bottom;
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.ordinal()
.range(["orange", "orangered"]);
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
return "<strong>Stop:</strong> <span style='color:red'>" + d.stopname + "</span>" + "<br><br><strong>Mean no. of boardings:</strong> <span style='color:red'>" + d.meanboardings + "</span>"+ "<br><br><strong>Mean no. of alightings:</strong> <span style='color:red'>" + d.meanalightings + "</span>";
})
var svg = d3.select("body").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 + ")");
svg.call(tip);
d3.tsv("data.tsv", function(d){
return {
stopname: d.stopname,
meanboardings: +d.meanboardings,
meanalightings: +d.meanalightings
};
}, function(error, data) {
console.log(data);
var dataset = d3.keys(data[0]).filter(function(key) { return key !== "stopname"
});
data.forEach(function(d) {
d.passengers = dataset.map(function(name) { return {name: name, value: +d[name]}; });
});
x0.domain(data.map(function(d) { return d.stopname; }));
x1.domain(dataset).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(d) { return d3.max(d.passengers, function(d) { return d.value; }); })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", "-.5em")
.attr("transform", function(d) {
return "rotate(-90)"
});
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Mean no. of boardings and alightings");
var stopname = svg.selectAll(".stopname")
.data(data)
.enter().append("g")
.attr("class", "stopname")
.attr("transform", function(d) { return "translate(" + x0(d.stopname) + ",0)"; });
stopname.selectAll("rect")
.data(function(d) { return d.passengers; })
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function(d) { return x1(d.name); })
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
.style("fill", function(d) { return color(d.name); })
.on('mouseover', tip.show)
.on('mouseout', tip.hide);
var legend = svg.selectAll(".legend")
.data(dataset.slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.name); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
});
function type(d) {
d.data = +d.data;
return d;
}
</script>
TSV file:
stopname maxboardings meanboardings minboardings varboardings maxalightings meanalightings minalightings varalightings maxload meanload minload varload noofbuses
The Boardwalk Terminal (3908) 7 2.571428571 0 6.952380952 0 0 0 0 7 2.571428571 0 6.952380952 7
Highview / Sugar Maple (3329) 2 0.333333333 0 0.666666667 1 0.166666667 0 0.166666667 6 3 0 6.4 6
Highview / Highview Pl (3330) 0 0 0 0 1 0.166666667 0 0.166666667 5 2.833333333 0 5.366666667 6
....
Any help is appreciated, thank you!
You are getting undefined because you are using an undefined value for generating tooltip text.
In your html function, the data-object associated d you get for every rectangle/bar is of following format:
{name: "meanlightings", value: <some_value>}
It does not contain the stopname property and will only contain value for one of the fields in the original dataset.
You will have to rewrite the html callback as
.html(function(d) {
return "<strong>" + d.name + " :</strong> <span style='color:red'>" + d.value + "</span>";
})
However, if you want to show more properties, you will have to change the data that you are binding to rectangles.

Handle with d3.json

I'm trying to make a d3 scatterplot recurring to json data. I know that d3 has d3.json to load json data but my code isn't working. I'm not that good dealing with js (it's my first time with this language), that's why I need help in this.
Basically, I need to plot this data (date in xAxis and IADP_mGycm2 in yAxis):
[
{
"imageID": 1,
"value": 288.3,
"date": "2015-10-22"
},
{
"imageID": 2,
"value": 188.1,
"date": "2015-10-22"
}
]
JS:
var margin = { top: 50, right: 300, bottom: 50, left: 50 },
outerWidth = 1050,
outerHeight = 500,
width = outerWidth - margin.left - margin.right,
height = outerHeight - margin.top - margin.bottom;
var x = d3.scale.linear()
.range([0, width]).nice();
var y = d3.scale.linear()
.range([height, 0]).nice();
var xCat = "date",
yCat = "value";
d3.json("CR.json", function(error, rawData) {
if (error) {
console.error(error);
return;
}
var data = rawData.map(function (d) {
return {
date: d.date,
value: d.value
}
});
var xMax = d3.max(data, function(d) { return d["date"]; }),
xMin = d3.min(data, function(d) { return d["date"]; }),
xMin = xMin > 0 ? 0 : xMin,
yMax = d3.max(data, function(d) { return d["value"]; }) ,
yMin = d3.min(data, function(d) { return d["value"]; }),
yMin = yMin > 0 ? 0 : yMin;
x.domain([xMin, xMax]);
y.domain([yMin, yMax]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickSize(-height);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickSize(-width);
var color = d3.scale.category10();
var tip = d3.tip()
.attr("class", "d3-tip")
.offset([-10, 0])
.html(function(d) {
return xCat + ": " + d["date"] + "<br>" + yCat + ": " + d["value"];
});
var zoomBeh = d3.behavior.zoom()
.x(x)
.y(y)
.scaleExtent([0, 500])
.on("zoom", zoom);
var svg = d3.select("#scatter")
.append("svg")
.attr("width", outerWidth)
.attr("height", outerHeight)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoomBeh);
svg.call(tip);
svg.append("rect")
.attr("width", width)
.attr("height", height);
svg.append("g")
.classed("x axis", true)
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.classed("label", true)
.attr("x", width)
.attr("y", margin.bottom - 10)
.style("text-anchor", "end")
.text(xCat);
svg.append("g")
.classed("y axis", true)
.call(yAxis)
.append("text")
.classed("label", true)
.attr("transform", "rotate(-90)")
.attr("y", -margin.left)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text(yCat);
var objects = svg.append("svg")
.classed("objects", true)
.attr("width", width)
.attr("height", height);
objects.append("svg:line")
.classed("axisLine hAxisLine", true)
.attr("x1", 0)
.attr("y1", 0)
.attr("x2", width)
.attr("y2", 0)
.attr("transform", "translate(0," + height + ")");
objects.append("svg:line")
.classed("axisLine vAxisLine", true)
.attr("x1", 0)
.attr("y1", 0)
.attr("x2", 0)
.attr("y2", height);
objects.selectAll(".dot")
.data(data)
.enter().append("circle")
.classed("dot", true)
.attr("cy", function (d) { return d.value; })
.attr("cx", function (d) { return d.date; })
.attr("transform", transform)
.style("fill", "red")
.on("mouseover", tip.show)
.on("mouseout", tip.hide);
var legend = svg.selectAll(".legend")
.data(color.domain())
.enter().append("g")
.classed("legend", true)
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("circle")
.attr("r", 3.5)
.attr("cx", width + 20)
.attr("fill", color);
legend.append("text")
.attr("x", width + 26)
.attr("dy", ".35em")
.text(function(d) { return d; });
d3.select("input").on("click", change);
function change() {
xCat = "date";
xMax = d3.max(data, function(d) { return d["date"]; });
xMin = d3.min(data, function(d) { return d["date"]; });
zoomBeh.x(x.domain([xMin, xMax])).y(y.domain([yMin, yMax]));
var svg = d3.select("#scatter").transition();
svg.select(".x.axis").duration(750).call(xAxis).select(".label").text("date");
objects.selectAll(".dot").transition().duration(1000).attr("transform", transform);
}
function zoom() {
svg.select(".x.axis").call(xAxis);
svg.select(".y.axis").call(yAxis);
svg.selectAll(".dot")
.attr("transform", transform);
}
function transform(d) {
return "translate(" + x(d["date"]) + "," + y(d["value"]) + ")";
}
});
HTML:
<html>
<head>
<meta charset="utf-8">
<title>Visualization</title>
<link rel="stylesheet" href="scatter.css" charset="utf-8">
</head>
<body>
<div id="scatter"></div>
<input type="button" name="xAxis" value="xAxis">
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="http://labratrevenge.com/d3-tip/javascripts/d3.tip.v0.6.3.js"></script>
<script src="scatter.js" charset="utf-8"></script>
</body>
</html>
CSS:
rect {
fill: transparent;
shape-rendering: crispEdges;
}
.axis path,
.axis line {
fill: none;
stroke: rgba(0, 0, 0, 0.1);
shape-rendering: crispEdges;
}
.axisLine {
fill: none;
shape-rendering: crispEdges;
stroke: rgba(0, 0, 0, 0.5);
stroke-width: 2px;
}
.dot {
fill-opacity: .5;
}
.d3-tip {
line-height: 1;
font-weight: bold;
padding: 12px;
background: rgba(0, 0, 0, 0.8);
color: #fff;
border-radius: 2px;
}
/* Creates a small triangle extender for the tooltip */
.d3-tip:after {
box-sizing: border-box;
display: inline;
font-size: 10px;
width: 100%;
line-height: 1;
color: rgba(0, 0, 0, 0.8);
content: "\25BC";
position: absolute;
text-align: center;
}
/* Style northward tooltips differently */
.d3-tip.n:after {
margin: -1px 0 0 0;
top: 100%;
left: 0;
}
Thanks in advance!
One issue I see is that you're missing a }) at the end of this code chunk:
var data = rawData.map(function (d) {
return {
date: d.date,
IADP_mGycm2: d.IADP_mGycm2
};
Try changing it to this:
var data = rawData.map(function (d) {
return {
date: d.date,
IADP_mGycm2: d.IADP_mGycm2
}
});
It also helps in debugging if you include the specific error message you're getting.
Well, you define xCat as:
var xCat = "Date"
but your mapping function uses:
date: d.date
so, when you reference d[xCat] what you are getting is d.Date (which is undefined and would cause NaN) instead of d.date. That's all I can see with a quick look through.
You can fix this by using d['date'] or d.date instead of d[xCat].
Ok, after looking a little further at this, I've identified a couple of problems. The main issue is that the chart you are trying to emulate has numeric values on both the x and y axes. However, you are trying to use dates. In order to do that, you have to use d3.time.scale() for the x-axis instead of a linear scale. You also have to transform the date strings from your data to date objects and use your time scale to scale your x-axis values. Here is scatter.js with the changes:
var margin = { top: 50, right: 300, bottom: 50, left: 50 },
outerWidth = 1050,
outerHeight = 500,
width = outerWidth - margin.left - margin.right,
height = outerHeight - margin.top - margin.bottom;
// Use a time scale for the x-axis
var x = d3.time.scale()
.range([0, width]).nice();
var y = d3.scale.linear()
.range([height, 0]).nice();
var xCat = "date",
yCat = "Dose";
d3.json("CR.json", function(error, rawData) {
if (error) {
console.error(error);
return;
}
var data = rawData.map(function(d) {
return {
// Create date objects, not strings
date: new Date(d.date),
IADP_mGycm2: d.IADP_mGycm2
}
});
var xMax = d3.max(data, function(d) { return d["date"]; }),
xMin = d3.min(data, function(d) { return d["date"]; }),
//xMin = xMin > 0 ? 0 : xMin,
yMax = d3.max(data, function(d) { return d["IADP_mGycm2"]; }),
yMin = d3.min(data, function(d) { return d["IADP_mGycm2"]; });
//yMin = yMin > 0 ? 0 : yMin;
x.domain([xMin, xMax]);
y.domain([yMin, yMax]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickSize(-height);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickSize(-width);
var color = d3.scale.category10();
var tip = d3.tip()
.attr("class", "d3-tip")
.offset([-10, 0])
.html(function(d) {
return xCat + ": " + d["date"] + "<br>" + yCat + ": " + d["IADP_mGycm2"];
});
var zoomBeh = d3.behavior.zoom()
.x(x)
.y(y)
.scaleExtent([0, 500])
.on("zoom", zoom);
var svg = d3.select("#scatter")
.append("svg")
.attr("width", outerWidth)
.attr("height", outerHeight)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoomBeh);
svg.call(tip);
svg.append("rect")
.attr("width", width)
.attr("height", height);
svg.append("g")
.classed("x axis", true)
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.classed("label", true)
.attr("x", width)
.attr("y", margin.bottom - 10)
.style("text-anchor", "end")
.text(xCat);
svg.append("g")
.classed("y axis", true)
.call(yAxis)
.append("text")
.classed("label", true)
.attr("transform", "rotate(-90)")
.attr("y", -margin.left)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text(yCat);
var objects = svg.append("svg")
.classed("objects", true)
.attr("width", width)
.attr("height", height);
objects.append("svg:line")
.classed("axisLine hAxisLine", true)
.attr("x1", 0)
.attr("y1", 0)
.attr("x2", width)
.attr("y2", 0)
.attr("transform", "translate(0," + height + ")");
objects.append("svg:line")
.classed("axisLine vAxisLine", true)
.attr("x1", 0)
.attr("y1", 0)
.attr("x2", 0)
.attr("y2", height);
objects.selectAll(".dot")
.data(data)
.enter().append("circle")
.classed("dot", true)
.attr("cy", function(d) { return d.IADP_mGycm2; })
// Use the time scale to scale the values for the x-axis
.attr("cx", function(d) { return x(d.date); })
.attr("transform", transform)
.style("fill", "red")
.on("mouseover", tip.show)
.on("mouseout", tip.hide);
var legend = svg.selectAll(".legend")
.data(color.domain())
.enter().append("g")
.classed("legend", true)
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("circle")
.attr("r", 3.5)
.attr("cx", width + 20)
.attr("fill", color);
legend.append("text")
.attr("x", width + 26)
.attr("dy", ".35em")
.text(function(d) { return d; });
d3.select("input").on("click", change);
function change() {
xCat = "date";
xMax = d3.max(data, function(d) { return d["date"]; });
xMin = d3.min(data, function(d) { return d["date"]; });
zoomBeh.x(x.domain([xMin, xMax])).y(y.domain([yMin, yMax]));
var svg = d3.select("#scatter").transition();
svg.select(".x.axis").duration(750).call(xAxis).select(".label").text("date");
objects.selectAll(".dot").transition().duration(1000).attr("transform", transform);
}
function zoom() {
svg.select(".x.axis").call(xAxis);
svg.select(".y.axis").call(yAxis);
svg.selectAll(".dot")
.attr("transform", transform);
}
function transform(d) {
return "translate(" + x(d["date"]) + "," + y(d["IADP_mGycm2"]) + ")";
}
});
This gets rid of the errors you were seeing, but it still doesn't plot the circles correctly. Sorry, I don't have the time to work all that out. However, this should move you forward and get you closer to figuring it out yourself. To learn more about time scales, see https://github.com/mbostock/d3/wiki/Time-Scales. Also, if you really want to learn D3, I highly recommend the book D3.js in Action by Elijah Meeks. https://www.manning.com/books/d3-js-in-action. One of the better programming books I have read.

D3 bar charts bar values display is improper

I want to display the bar charts each bar peak value at the outer top of each bar. But as far as i could get is to represent each bar value inside the bar. But thats not what i wanted. As you can see in the image attached the bottom of the x axis is messed up. I think we have to move the svg code out of the data loop and use svg.data(someData).enter() to fix this issue but i tried ways to do it but unsuccessful. Kindly suggest how i can modify the follow code.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.axis {
font: 12px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.chart text {
fill: black;
font: 15px sans-serif;
text-anchor: middle;
}
</style>
<svg class="chart"></svg>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 30, right: 80, bottom: 40, left: 100},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var color = d3.scale.category10();
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .2);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
function unit (d) {
count = 0, download = 0;
num = d.value;
while((num) >= 1)
{
num/=10;
++count;
}
switch (count) {
case 4 : download = ((d.value/1000).toFixed(2)) + " MB" ;
break;
case 8 : download = ((d.value/1000 * 1000).toFixed(2)) + " GB" ;
break;
default: download = ((d.value/1000).toFixed(2)) + " MB" ;
}
return download;
}
var chart = d3.select("body").append("svg")
// line = d3.select("body").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 bar;
d3.csv("data.csv", type, function(error, data) {
x.domain(data.map(function(d) { return d.name; }));
y.domain([0, d3.max(data, function(d) { return d.value; })]);
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("x", 790 )
.attr("y", 15 )
.style("text-anchor", "bottom")
.text("Clients");
chart.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
//.attr("transform", "rotate(-90)")
.attr("y", 1)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Kilo Bytes");
bar = chart.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function(d) { return "translate(" + x(d.name) + ",0)"; });
bar.append("rect")
.style("fill", function(d) { return color(d.name); })
.attr("y", function(d) { return y(d.value);})
.attr("height", function(d) { return height - y(d.value); })
.attr("width", x.rangeBand());
bar.append("text")
.attr("x", x.rangeBand() / 2 )
.attr("y", function(d) { return y(d.value) + 2 ; })
.attr("dy", "1em")
.attr("text-anchor", "middle")
.attr("font-size", "14px")
.attr("fill", "black")
.text(function(d) { return unit(d); });
});
function type(d) {
d.value = +d.value; // coerce to number
return d;
}
</script>
and the data is in the csv format as below:
name,timesatmp,value
98.226.148.140,1341677167454,1
98.226.148.140,1341677167461,3
98.226.148.141,1341677167916,4
98.226.148.141,1341677167935,6
98.226.148.142,1341677167944,7
98.226.148.142,1341677167945,32
Kindly suggest how i can show the display properly.
First modify your bar charts a little, otherwise no bars will be shown there:
html:
<svg class="chart"></svg>
to
<body class="chart"></body>
js:
bar = chart.selectAll(".bar")
.data(data)
.enter().append("g")
.attr('class', 'bar')
.attr("transform", function(d) { return "translate(" + x(d.name) + ",0)"; });
and for adjusting the peak value, just add a translate as follows:
bar.append("text")
.attr("x", x.rangeBand() / 2 )
.attr("y", function(d) { return y(d.value) + 2 ; })
.attr("dy", "1em")
.attr("text-anchor", "middle")
.attr("font-size", "14px")
.attr("fill", "black")
.attr("transform", function(d) { return "translate(0, -20)"; })
.text(function(d) { return unit(d); });
Rplace:
d3.csv("data.csv", type, function(error, data) {
x.domain(data.map(function(d) { return d.name; }));
y.domain([0, d3.max(data, function(d) { return d.value; })]);
with:
d3.csv("data.csv", type, function(error, data) {
x.domain(data.map(function(d) { return d.name; }));
y.domain([0, d3.max(data, function(d) { return d.value; })]);
var nestedData = d3.nest()
.key(function(d) { return d.name; }).entries(data);
var newData = [];
nestedData.forEach(function(d){
newData.push({name:d.key, value: d3.max(d.values, function(d) {return d.value})});
});

Categories