How to get JSON key for d3.js chart - javascript

Here is my problem : I an array with JSON object in it ad I would like to get the keys of JSON objects to build a bar chart with it.
Here is my array :
var dataset = [{
"T1": 4.23
},
{
"T2": 45.62
},
{
"T3": 24.78
},
{
"T4": 11.41
},
{
"T5": 5.19
},
{
"T6": 5.15
},
{
"T7": 1.99
},
{
"T8": 0.93
},
{
"T9": 0.61
}
];
And here is my code to draw the chart :
let width = 500;
let heigth = 200;
let barPadding = 1;
let svg = d3.select("#containerChart")
.append("svg")
.attr("width", width)
.attr("height", heigth);
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("x", function (d, i) {
return i + (20 + barPadding);
})
.attr("y", function (d) {
return heigth - (**values of JSON object** * 2)
})
.att("width", 20)
.attr("heigth", function (d) {
return (**values of JSON object** * 2)
})
.attr("fill", "teal")
Do you have any idea of how I could do ?
Thanks in advance for your help !!

Here's the codepen and here's the code for testing on your local:
<!DOCTYPE html>
<meta charset="utf-8">
<head>
<style>
.bar{
fill: steelblue;
}
.bar:hover{
fill: brown;
}
.axis {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
</style>
</head>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
// set the dimensions of the canvas
var margin = {top: 20, right: 20, bottom: 70, left: 40},
width = 600 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
// set the ranges
var x = d3.scale.ordinal().rangeRoundBands([0, width], .05);
var y = d3.scale.linear().range([height, 0]);
// define the axis
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(10);
// add the SVG element
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 + ")");
// load the data
d3.json("data.json", function(error, data) {
// scale the range of the data
x.domain(data.map(function(d) { return Object.keys(d); }));
y.domain([0, d3.max(data, function(d) { return +d[Object.keys(d)]; })]);
// add axis
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", "-.55em")
.attr("transform", "rotate(-90)" );
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 5)
.attr("dy", ".71em")
.style("text-anchor", "end");
// Add bar chart
svg.selectAll("bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(Object.keys(d)); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(+d[Object.keys(d)]); })
.attr("height", function(d) { return height - y(+d[Object.keys(d)]); });
});
</script>
</body>
I replaced all references for the y axis with the json keys using Object.keys() and got the values for these keys to put into the x axis. You could also use for... in loop to do the same thing.
To test it locally, you will have to host both files on a local server or else you will get CORS errors. You could include a parsed inline json to avoid CORS altogether.

This one should work :
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("x", function (d, i) {
return i + (20 + barPadding);
})
.attr("y", function (d,i) {
return heigth - d["T" + (i+1)]
})
.attr("width", 20)
.attr("heigth", function (d,i) {
return heigth - d["T" + (i+1)]
})
.attr("fill", "teal")

Related

I can't load a javascript scatter plot in any browser

Recently, maybe after updating Java, I'm unable to load a scatter plot on Chrome, Firefox or Internet Explorer.
One month ago, everything loaded fine. I don't know what happened. I've updated Java to the last version and enabled ActiveX on Internet options, but nothing works for me.
I'll paste the html code. It loads the point info using a csv file saved in the same folder:
<!DOCTYPE html> <meta charset="utf-8"> <style>
body { font: 12px Arial;}
.axis path, .axis line { fill: none; stroke: grey; stroke-width: 1;
shape-rendering: crispEdges; }
</style> <body>
<script src="http://d3js.org/d3.v4.js"></script> <script>
// Set the dimensions of the canvas / graph var margin = {top: 30, right: 20, bottom: 50, left: 60}, width = 900 - margin.left - margin.right, height = 360 - margin.top - margin.bottom;
// Parse the date / time
var parseDate = d3.timeParse("%a %b %d %H:%M:%S %Z %Y "); // Set the ranges
var y = d3.scaleTime().range([height, 0]); var x = d3.scaleLinear().range([0, width]);
// Define the axes
var xAxis = d3.axisBottom().scale(x) .ticks(5);
var yAxis = d3.axisLeft().scale(y)
.ticks(5);
// Adds the svg canvas 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 + ")"); // Get the data
d3.csv("data.csv", function(error, data) { var max = data.length; console.log(max)
data.forEach(function(d, i) { d.dateParsed = parseDate(d.date); d.close = max - i;
}); // Scale the range of the data
y.domain(d3.extent(data, function(d) { return d.dateParsed; })); x.domain([0, d3.max(data, function(d) { return d.close; })]);
// Add the scatterplot svg.selectAll("dot") .data(data)
.enter().append("circle") .attr("r", 0.5)
.attr("fill","#2980B9")
.attr("cy", function(d) { return y(d.dateParsed); }) .attr("cx", function(d) { return x(d.close); });
svg.append("g") .attr("class", "x axis")
.attr("transform", "translate(0," + height + ")") .call(xAxis);
svg.append("text") .attr("class", "x label") .attr("text-anchor", "middle") .attr("x", width / 2) .attr("y", height + 40) .text("followers");
svg.append("g")
.attr("class", "y axis") .call(yAxis);
svg.append("text") .attr("class", "y label") .attr("text-anchor", "middle") .attr("x", -height / 2 ) .attr("y", -50)
.attr("transform", "rotate(-90)") .text("account creation date");
});
</script> </body>
It looks like main issue is code is not formatted properly.
Many comments get mixed with the code caused the syntax errors.
I try to format the code and try to fix those errors.
As we don't have your data.csv file so we are not able to load that data but code creates the chart now.
You can try to use this code on your side with your data.csv file. It will help you to load the chart properly.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body { font: 12px Arial;}
.axis path, .axis line { fill: none; stroke: grey; stroke-width: 1;
shape-rendering: crispEdges; }
</style>
</head>
<body>
<script src="http://d3js.org/d3.v4.js"></script> <script>
// Set the dimensions of the canvas / graph
var margin = {top: 30, right: 20, bottom: 50, left: 60}, width = 900 - margin.left - margin.right, height = 360 - margin.top - margin.bottom;
// Parse the date / time
var parseDate = d3.timeParse("%a %b %d %H:%M:%S %Z %Y "); // Set the ranges
var y = d3.scaleTime().range([height, 0]); var x = d3.scaleLinear().range([0, width]);
// Define the axes
var xAxis = d3.axisBottom().scale(x) .ticks(5);
var yAxis = d3.axisLeft().scale(y)
.ticks(5);
// Adds the svg canvas
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 + ")"); // Get the data
d3.csv("data.csv", function(error, data) { var max = data.length;
console.log(max);
data.forEach(function(d, i) { d.dateParsed = parseDate(d.date); d.close = max - i;
});
// Scale the range of the data
y.domain(d3.extent(data, function(d) { return d.dateParsed; })); x.domain([0, d3.max(data, function(d) { return d.close; })]);
// Add the scatterplot
svg.selectAll("dot") .data(data)
.enter().append("circle") .attr("r", 0.5)
.attr("fill","#2980B9")
.attr("cy", function(d) { return y(d.dateParsed); }) .attr("cx", function(d) { return x(d.close); });
svg.append("g") .attr("class", "x axis")
.attr("transform", "translate(0," + height + ")") .call(xAxis);
svg.append("text") .attr("class", "x label") .attr("text-anchor", "middle") .attr("x", width / 2) .attr("y", height + 40) .text("followers");
svg.append("g")
.attr("class", "y axis") .call(yAxis);
svg.append("text") .attr("class", "y label") .attr("text-anchor", "middle") .attr("x", -height / 2 ) .attr("y", -50)
.attr("transform", "rotate(-90)") .text("account creation date");
});
</script>
</body>
</html>

Plot bar chart using D3 JS

I am plotting a bar chart in D3.js (Version 3). Which has two axis, one is receive_data and another one is responses. I have a JSON file where I stored the data. JSON format looks like,
[{"receive_date":"2013-11-04","responses":"2"}]
In my JSON, I have two responses values for the same date 2013-11-04 .
Like,
[{"receive_date":"2013-11-04","responses":"2"},{"receive_date":"2013-11-04","responses":"8668"}
This is the JSON Source :- https://api.myjson.com/bins/gdpu7
So, when I am plotting the graph, it is not taking the sum of the values for the same receive_date instead it is showing two times. I want it to show the sum of responses. responses should be (8668+2) for the receive_date 2013-11-04
I also found it that by using reduce we can do this. I tried to use d3.json.reduce . But it is showing error d3.json.reduce is not a function.
var margin = {
top: 20,
right: 30,
bottom: 30,
left: 40
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// scale to ordinal because x axis is not numerical
var x = d3.scale.ordinal().rangeRoundBands([0, width], .1);
//scale to numerical value by height
var y = d3.scale.linear().range([height, 0]);
var chart = d3.select("#chart")
.append("svg") //append svg element inside #chart
.attr("width", width + (2 * margin.left) + margin.right) //set width
.attr("height", height + margin.top + margin.bottom); //set height
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom"); //orient bottom because x-axis will appear below the bars
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
d3.json("https://api.myjson.com/bins/gdpu7", function(error, data) {
x.domain(data.map(function(d) {
return d.receive_date
}));
y.domain([0, d3.max(data, function(d) {
return d.responses
})]);
var bar = chart.selectAll("g")
.data(data)
.enter()
.append("g")
.attr("transform", function(d, i) {
return "translate(" + x(d.receive_date) + ", 0)";
});
bar.append("rect")
.attr("y", function(d) {
return y(d.responses);
})
.attr("x", function(d, i) {
return x.rangeBand() + (margin.left / 2);
})
.attr("height", function(d) {
return height - y(d.responses);
})
.attr("width", x.rangeBand()); //set width base on range on ordinal data
bar.append("text")
.attr("x", x.rangeBand() + margin.left)
.attr("y", function(d) {
return y(d.responses) - 10;
})
.attr("dy", ".75em")
.text(function(d) {
return d.responses;
});
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(" + margin.left + "," + height + ")")
.call(xAxis);
chart.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + margin.left + ",0)")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("responses");
});
function type(d) {
d.receive_date = +d.receive_date; // coerce to number
return d;
}
#chart rect {
fill: #4aaeea;
}
#chart text {
fill: white;
font: 10px sans-serif;
text-anchor: end;
}
.axis text {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #fff;
shape-rendering: crispEdges;
}
body {
background: #1a1a1a;
color: #eaeaea;
padding: 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="chart"></div>
JSfiddle :- https://jsfiddle.net/bL9940at/
The relevant part:
var array1 = data; //input
var array2 = [];
var last_d;
array1.reduce(function (accumulator, currentValue, i) {
var r = Number(currentValue.responses),
d = currentValue.receive_date;
if (d == last_d) r += accumulator;
array2[i] = {
receive_date: d,
responses: r
};
last_d = d;
return accumulator + Number(currentValue.responses);
}, 0);
data = array2; //output
var margin = {
top: 20,
right: 30,
bottom: 30,
left: 40
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// scale to ordinal because x axis is not numerical
var x = d3.scale.ordinal().rangeRoundBands([0, width], .1);
//scale to numerical value by height
var y = d3.scale.linear().range([height, 0]);
var chart = d3.select("#chart")
.append("svg") //append svg element inside #chart
.attr("width", width + (2 * margin.left) + margin.right) //set width
.attr("height", height + margin.top + margin.bottom); //set height
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom"); //orient bottom because x-axis will appear below the bars
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
d3.json("https://api.myjson.com/bins/gdpu7", function(error, data) {
//create new arrays
var array1 = data; //input
var array2 = [];
var last_d;
array1.reduce(function (accumulator, currentValue, i) {
var r = Number(currentValue.responses),
d = currentValue.receive_date;
if (d == last_d) r += accumulator;
array2[i] = {
receive_date: d,
responses: r
};
last_d = d;
return accumulator + Number(currentValue.responses);
}, 0);
data = array2; //output
x.domain(data.map(function(d) {
return d.receive_date;
}));
y.domain([0, d3.max(data, function(d) {
return d.responses;
})*1.1]);
var bar = chart.selectAll("g")
.data(data)
.enter()
.append("g")
.attr("transform", function(d, i) {
return "translate(" + x(d.receive_date) + ", 0)";
});
bar.append("rect")
.attr("y", function(d) {
return y(d.responses);
})
.attr("x", function(d, i) {
return x.rangeBand() + (margin.left / 2);
})
.attr("height", function(d) {
return height - y(d.responses);
})
.attr("width", x.rangeBand()); //set width base on range on ordinal data
bar.append("text")
.attr("x", x.rangeBand() + margin.left)
.attr("y", function(d) {
return y(d.responses) - 10;
})
.attr("dy", ".75em")
.text(function(d) {
return d.responses;
});
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(" + margin.left + "," + height + ")")
.call(xAxis);
chart.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + margin.left + ",0)")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("responses");
});
function type(d) {
d.receive_date = +d.receive_date; // coerce to number
return d;
}
#chart rect {
fill: #4aaeea;
}
#chart text {
fill: white;
font: 10px sans-serif;
text-anchor: end;
}
.axis text {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #fff;
shape-rendering: crispEdges;
}
body {
background: #1a1a1a;
color: #eaeaea;
padding: 10px;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js"></script>
<script src="https://code.jquery.com/jquery-git.js"></script>
<div id="chart"></div>
</body>
</html>

d3 - multiple overlaying charts with custom DOM element

I'm new to d3, but pretty familiar with the HighCharts api.
I've seen lots of examples of multiple d3 charts on the same page; but can't seem to find examples of one chart overlaying/sitting directly on top of another chart. Is this possible?
With HighCharts, you can define multiple chart types in the plotOptions config object. Is there something similar with d3? Or, how could you do this with d3?
I would effectively like to have a line graph on top of a bar chart. There will be different 'stages' according to the data, so some of the bar's could be inactive/empty.
Additionally, I need to display an indicator to show where the 'stage' is currently; and ensure that this is all responsive.
Example (rough mockup):
After researching d3 and looking for similar examples, I am thinking that maybe d3 isn't the best choice for this; maybe a custom CSS/JS/HTML solution (inside an angular app) would be better.
Any recommendations or pointers would be very appreciated.
Here's a quick mock-up started from this excellent bar chart example:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.point rect {
fill: steelblue;
}
.point circle {
fill: orange;
}
.point rect:hover {
fill: brown;
}
.axis {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
.line {
fill: none;
stroke: orange
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 75, right: 20, bottom: 30, left: 40},
width = 600 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
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");
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 + ")");
var data = "abcdefghijklmnopqrstuvwxyz".split("").map(function(d){
return {
letter: d,
bar: Math.random() * 10,
line: Math.random() * 10
};
})
x.domain(data.map(function(d) { return d.letter; }));
y.domain([0, d3.max(data, function(d) { return d3.max([d.bar, d.line]); })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text");
var points = svg.selectAll(".point")
.data(data)
.enter().append("g")
.attr("class", "point");
points.append('rect')
.attr("x", function(d) { return x(d.letter); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.bar); })
.attr("height", function(d) { return height - y(d.bar); });
points.append('circle')
.attr("r", 5)
.attr("cx", function(d){ return x(d.letter) + x.rangeBand() / 2; })
.attr("cy", function(d){ return y(d.line)});
var line = d3.svg.line()
.x(function(d) { return x(d.letter) + x.rangeBand() / 2; })
.y(function(d) { return y(d.line); });
svg.append("path")
.attr("class", "line")
.datum(data)
.attr("d", line);
var indicator = svg.append("g")
.attr("r", 5)
.attr("transform", "translate(" + (x("q") + x.rangeBand() / 2) + "," + -20 + ")");
indicator.append("circle")
.attr("r", 40)
.style("fill", "red");
indicator.append("text")
.text("!")
.style("fill", "white")
.style("text-anchor", "middle")
.style("alignment-baseline", "middle")
.style("font-size", 70);
indicator.append("line")
.attr("y1", 20)
.attr("y2", height + 20)
.attr("x1", 0)
.attr("x2", 0)
.style("stroke", "red")
.style("stroke-width", "4px");
</script>
New Solution Based on Comments
Given your input data, here's a new example. I went a bit overboard here, so please ask question on any confusing bits. I tried to comment it out:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
rect {
fill: steelblue;
}
circle {
fill: orange;
}
rect:hover {
fill: brown;
}
.axis {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
.line {
fill: none;
stroke: orange
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var margin = {
top: 75,
right: 20,
bottom: 30,
left: 40
},
width = 600 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
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");
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 + ")");
// here's your data
var data =
{
'point1': [{
'value': 50
}, {
'value': 100
}, {
'value': 100
}, {
'value': 150
}],
'point2': [{
'value': 25
}, {
'value': 40
}, {
'value': 60
}],
'point3': [{
'value': 25
}]
};
// d3ify your data
// d3 likes arrays of objects, you have an object of objects
// so first make it an array
var barData = d3.entries(data);
// set x domain
x.domain(barData.map(function(d){ return d.key }));
// create lineData
var lineData = [];
barData.forEach(function(d0, i){
d0.mean = d3.mean(d0.value, function(d1){ return d1.value });
d0.max = d3.max(d0.value, function(d1){ return d1.value});
var N = d0.value.length,
// this is an inner scale
// that represents each bar
s = d3.scale.linear().range([
x(d0.key) + (x.rangeBand() / N) / 2,
x(d0.key) + x.rangeBand()
]).domain([
0, N
])
d0.value.forEach(function(d1, j){
lineData.push({
x: s(j), // this is the pixel position of x, it's jittered on the bar
y: d1.value // this is the user position of y
})
});
});
// set y domain
y.domain([0, d3.max(barData, function(d) {
return d.max;
})]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text");
// draw bars
var bars = svg.selectAll(".bar")
.data(barData)
.enter()
.append('rect')
.attr('class', 'bar')
.attr("x", function(d) {
return x(d.key);
})
.attr("width", x.rangeBand())
.attr("y", function(d) {
return y(d.mean);
})
.attr("height", function(d) {
return height - y(d.mean);
});
// add points
var points = svg.selectAll('point')
.data(lineData)
.enter()
.append('circle')
.attr('class', 'point')
.attr("r", 5)
.attr("cx", function(d) {
return d.x; // already pixel position
})
.attr("cy", function(d) {
return y(d.y)
});
var line = d3.svg.line()
.x(function(d) {
return d.x; // already pixel position
})
.y(function(d) {
return y(d.y);
});
svg.append("path")
.attr("class", "line")
.datum(lineData)
.attr("d", line);
var indicator = svg.append("g")
.attr("transform", "translate(" + (x("point2") + x.rangeBand() / 2) + "," + -20 + ")");
indicator.append("circle")
.attr("r", 40)
.style("fill", "red");
indicator.append("text")
.text("!")
.style("fill", "white")
.style("text-anchor", "middle")
.style("alignment-baseline", "middle")
.style("font-size", 70);
indicator.append("line")
.attr("y1", 20)
.attr("y2", height + 20)
.attr("x1", 0)
.attr("x2", 0)
.style("stroke", "red")
.style("stroke-width", "4px");
</script>
Happy d3ing!

error updating my barchart using d3

I came through this problem just when I though I had d3 undercontrol... I can't find out why my code is crashing when I update my barChart using an empty array of data...
here is my code:
<!DOCTYPE html>
<html>
<head>
<title>Simple tables in D3</title>
<meta charset="utf-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="utf-8"></script>
<style type="text/css">
.chart rect {
fill: steelblue;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
.axis {
font: 10px sans-serif;
}
</style>
</head>
<body>
<svg class="chart">
</svg>
<script>
var scanCounters = JSON.parse("[{\"scan\":\"111\",\"repetition\":3},{\"scan\":\"222\",\"repetition\":2},{\"scan\":\"333\",\"repetition\":4},{\"scan\":\"123\",\"repetition\":2},{\"scan\":\"456\",\"repetition\":1},{\"scan\":\"789\",\"repetition\":1}]");
var scanCounters2 = JSON.parse("[{\"scan\":\"111\",\"repetition\":8},{\"scan\":\"222\",\"repetition\":6},{\"scan\":\"333\",\"repetition\":5},{\"scan\":\"123\",\"repetition\":3}]");
var scanCounters3 = JSON.parse("[]");
var columns = ["scan", "repetition"];
var margin = {top: 20, right: 30, bottom: 30, left: 40};
var width = 960;
var height = 500;
var innerWidth = width - margin.left - margin.right;
var innerHeight = height - margin.top - margin.bottom;
var bcScan = barChart(600, 400, "chart");
bcScan.update(scanCounters);
setTimeout(function(){
bcScan.update(scanCounters2);
}, 1000);
setTimeout(function(){
bcScan.update(scanCounters3);
}, 2000);
function barChart(w, h, node) {
var bC = {};
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = w - margin.left - margin.right,
height = h - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
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");
var svg = d3.select("." + node)
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")");
svg.append("g")
.attr("class", "y axis")
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", -40)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Frequency");
bC.update = function(data) {
x.domain(data.map(function (d) {
return d[Object.keys(d)[0]];
}));
y.domain([0, d3.max(data, function (d) {
return d[Object.keys(d)[1]];
})]);
svg.select(".x.axis")
.transition()
.duration(300).call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", "rotate(-65)");
svg.select(".y.axis")
.transition()
.duration(300)
.call(yAxis);
var bars = svg.selectAll(".bar").data(data, function (d) {
return d[Object.keys(d)[0]];
});
bars.exit()
.transition()
.duration(300)
.attr("y", y(0))
.attr("height", height - y(0))
.style('fill-opacity', 1e-6)
.remove();
bars.enter()
.append("rect")
.attr("class", "bar")
.attr("y", y(0))
.attr("height", height - y(0));
var trans = bars.transition().duration(300).attr("x", function (d) {
return x(d[Object.keys(d)[0]]);
});
trans.attr("width", x.rangeBand())
.attr("y", function (d) {
return y(d[Object.keys(d)[1]]);
})
.attr("height", function (d) {
return height - y(d[Object.keys(d)[1]]);
});
};
return bC;
}
</script>
</body>
</html>
The problem is when I call bcScan.update(scanCounters3); where scanCounters3 is an empty array.
This is the error message:
Error: Invalid value for attribute height="NaN"
Error: Invalid value for attribute y="NaN"
QUESTON: Could someone help me to understand why it is crashing?
thanks
#EthanJewett, is correct, if you look at your y.domain() on each iteration you get:
[0, 4]
[0, 8]
[0, NaN]
So on your last iteration, your exit transition is doing math on an NaN:
.attr("y", y(0))
.attr("height", height - y(0))
I would just drop those lines altogether and make the exit transition:
bars.exit()
.transition()
.duration(300)
.style('fill-opacity', 1e-6)
.remove();

Hyperlink chart bars using D3

I am using the D3 toolkit to create a stacked bar graph of some data. I want link the bars to a separate page with more detail about the underlying data for that bar. I can't figure out how to turn the bars into hyperlinks. The code below is based on a demo that I have been tinkering with. When I inspect the code in Google Chrome it looks correct but the bars aren't clickable. Tremendous thanks in advance for ideas and suggestions on this!
Source, attempting to add link to Google to each bar:
<!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: steelblue;
}
.x.axis path {
display: none;
}
</style>
<body>
<script src="d3.min.js"></script>
<script>
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear()
.rangeRound([height, 0]);
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));
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 + ")");
d3.csv("./data.csv", function(error, data) {
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "State"; }));
data.forEach(function(d) {
var y0 = 0;
d.ages = color.domain().map(function(name) { return {name: name, y0: y0, y1: y0 += +d[name]}; });
d.total = d.ages[d.ages.length - 1].y1;
});
data.sort(function(a, b) { return b.total - a.total; });
x.domain(data.map(function(d) { return d.State; }));
y.domain([0, d3.max(data, function(d) { return d.total; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
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("Population");
var state = svg.selectAll(".state")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("xlink:href", function(d) { return "http://www.google.com"; })
.attr("transform", function(d) { return "translate(" + x(d.State) + ",0)"; });
state.selectAll("rect")
.data(function(d) { return d.ages; })
.enter().append("rect")
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.y1); })
.attr("height", function(d) { return y(d.y0) - y(d.y1); })
.style("fill", function(d) { return color(d.name); });
state.append("a")
.attr("xlink:href", "http://www.google.com")
;
var legend = svg.selectAll(".legend")
.data(color.domain().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; });
});
</script>
I'm not sure that you can add "a" html mark in SVG.
But you can add an event on each bar segment:
state.on('click',function(d){
... some great code ...
})

Categories

Resources