JSON data structure for D3jS report and D3jS code - json

I am trying to achieve the below report from JSON data
var data = [{"MonthYearShortName":"2014-09-13T00:00:00","Product":"Deposits","Actual":330393232.5,"Forecast":495589848.75,"Target":495589848.75},{"MonthYearShortName":"2014-09-13T00:00:00","Product":"Fee Based","Actual":111868709.42,"Forecast":167803064.13,"Target":167803064.13},{"MonthYearShortName":"2014-09-13T00:00:00","Product":"Lending","Actual":18146873.33,"Forecast":27220309.995,"Target":27220309.995}]
I am not sure if this is the right data structure. If I am picking the wrong structure to create the report, some help would be great.
I am using the below code for generating the report.
<div id="ReportContent" runat="server" style="border: 1px solid #58595d; border: 1px solid rgba(88, 89, 93, .3);"><svg></svg></div>
function ChartCreate() {
var color = d3.scale.linear()
.domain([0, d3.max(data, function (d) { return d.Target; })]) //change
.range(["#f3c40e", "#7d6507"]);
var width = 420,
margin = 100, //add
barHeight = 20;
var x = d3.scale.linear()
.domain([0, d3.max(data, function(d) { return d.Actual; })]) //change
.range([0, width]);
var chart = d3.select('#ReportContent_ReportContent svg')
.attr("width", width + margin)
.attr("height", barHeight * data.length);
var bar = chart.selectAll("g")
.data(data, function(d) { return d.Actual; })
.enter().append("g")
.attr("transform", function(d, i) { return "translate(0," + i * barHeight + ")"; });
bar.append("rect")
.attr("width", function(d) { return x(d.Actual); }) //change
.attr("height", barHeight) //change
.attr("fill", function(d) { return color(d.Actual); }); //change
bar.append("text")
.attr("text-anchor", "start") //add
.attr("x", function(d) { return x(d.Actual); }) //change
.attr("y", barHeight / 2)
.attr("dy", ".35em")
.text(function(d) { return d.Product; });
}

Related

D3 v4 simple bar chart - date always returns null. timeParsing/format issue

For some odd reason, my dates won't properly show up on my bar chart. When checking for errors, my dates always show up as "null" in the console. My professor and I have tried re-arranging the format of the code regarding the dates, as well as the .csv spreadsheet itself.
The gradient still works fine, but ever since we've been stumped on implementing the dated timeline feature, the bar chart info is out of view, as well.
I'm using the core sample 'Dogecoin' from this website:
https://coinmetrics.io/data-downloads/
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>DOGECOIN BAR CHART</title>
<script src="https://d3js.org/d3.v4.min.js"></script>
<style type="text/css">
</style>
</head>
<body>
<script type="text/javascript">
var w = 1000;
var h = 500;
var margin = ({
top: 20,
right: 20,
bottom: 30,
left: 50
});
var svgMAIN = d3.select("body")
.append("svg")
.attr("height", h - margin.top - margin.bottom)
.attr("width", w - margin.left - margin.right);
var g = svgMAIN.append("g").attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
var gradient = svgMAIN.append("defs")
.append("linearGradient")
.attr("id", "gradient")
.attr("x1", "0%")
.attr("y1", "0%")
.attr("x2", "100%")
.attr("y2", "100%")
.attr("spreadMethod", "pad");
gradient.append("stop")
.attr("offset", "0%")
.attr("stop-color", "#3f51b5")
.attr("stop-opacity", 1);
gradient.append("stop")
.attr("offset", "100%")
.attr("stop-color", "#009688")
.attr("stop-opacity", 0);
svgMAIN.append("rect")
.attr("width", w)
.attr("height", h)
.style("fill", "url(#gradient)");
var price = [];
var date = [];
var fixTime = d3.timeParse("%e-%b-%y");
var x = d3.scaleTime().range([0, w]);
var y = d3.scaleLinear().range([h, 0]);
d3.csv("doge.csv", function(error, data) {
data.map(function(d) {
d.date = fixTime(d.date);
price.push(d.priceUSD);
y.domain([0, d3.max(data, function(d) {
return d.priceUSD;
})]);
x.domain(d3.extent(data, function(d) {
return d.date;
}));
});
svgMAIN.append("g")
.attr("transform", "translate(0," + h + ")")
.call(d3.axisBottom(x))
g.append("g")
.call(d3.axisLeft(y))
.append("text")
.attr("fill", "#000")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.attr("text-anchor", "end")
.text("Price ($)");
var rect = svgMAIN.selectAll("rect")
.data(price)
.enter()
.append("rect")
.attr("x", function(d, i) {
return i * (w / price.length);
})
.attr("y", function(d) {
return d;
})
.attr("width", w / price.length)
.attr("height", function(d) {
return d;
})
.attr("fill", "midnightblue")
.on("mouseover", mouseOverPrice)
.on("mouseout", mouseOutPrice);
function mouseOverPrice(d, i) {
d3.select(this).raise()
.attr("fill", "white");
svgMAIN.append("text")
.attr("id", "priceValue")
.attr("x", "50")
.attr("y", "150")
.text("$" + d)
.attr("font-family", "sans-serif")
.attr("font-size", "150px")
.attr("fill", "white")
.attr("font-weight", "bold");
};
function mouseOutPrice(d, i) {
d3.select(this).attr("fill", "midnightblue");
d3.select("#priceValue").remove();
};
});
</script>
</body>
</html>

Legend text background change on mouse-hover over d3 doughnut chart

I have one doughnut chart whose values display on mouse-hover. But, I want to change the legend text background while hovering over the respective doughnut chart area.
return {
restrict: 'E',
scope: {
values: '='
},
link: function (scope, element, attrs) {
scope.$watch('values', function(values) {
var data = [];
if(values) {
console.log('values from directive: ', values);
var w = 700,
h = 700,
r = 290,
inner = 190,
color = d3.scale.category20();
data=values;
var total = d3.sum(data, function(d) {
return d3.sum(d3.values(d));
});
var vis = d3.select("#pieChartsD3")
.append("svg:svg")
.data([data])
.attr("width", w)
.attr("height", h)
.append("svg:g")
.attr("transform", "translate(" +320+ "," +320+ ")")
var textTop = vis.append("text")
.attr("dy", ".35em")
.style("text-anchor", "middle")
.attr("class", "textTop")
.text( "TOTAL" )
.attr("y", -10),
textBottom = vis.append("text")
.attr("dy", ".35em")
.style("text-anchor", "middle")
.attr("class", "textBottom")
.text(total.toFixed(0) )
.attr("y", 10);
var arc = d3.svg.arc()
.innerRadius(inner)
.outerRadius(r);
var arcOver = d3.svg.arc()
.innerRadius(inner + 20)
.outerRadius(r + 20);
var pie = d3.layout.pie()
.value(function(d) { return d.val; });
var arcs = vis.selectAll("g.slice")
.data(pie)
.enter()
.append("svg:g")
.attr("class", "slice")
.style('word-wrap', 'break-word')
.on("mouseover", function(d) {
d3.select(this).select("path").transition()
.duration(200)
.attr("d", arcOver)
textTop.text(d3.select(this).datum().data.name)
.style('fill', 'red')
.attr("y", -10);
textBottom.text(d3.select(this).datum().data.val.toFixed(0))
.style('fill', 'blue')
.attr("y", 10);
})
.on("mouseout", function(d) {
d3.select(this).select("path").transition()
.duration(100)
.attr("d", arc);
textTop.text( "TOTAL" )
.attr("y", -10);
textBottom.text(total.toFixed(0));
});
arcs.append("svg:path")
.attr("fill", function(d, i) { return color(i); } )
.attr("d", arc);
console.log("datas length: "+data.length)
var legend = d3.select("#legend").append("svg")
.attr("class", "legend")
.attr("width", 400)
.attr("height", 20*data.length)
.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("width", 18)
.attr("height", 18)
.style("fill", function(d, i) { return color(i); });
legend.append("text")
.attr("x", 24)
.attr("y", 9)
.attr("dy", ".35em")
.text(function(d) { return d.name; });
}
})
}
}
(BTW I'm inferring about your request because you haven't explicitly stated a question. With that said...)
Inside the .on("mouseover", function(d) { block, add another section:
legend.filter(function(l){
l === d; // remember d is the value bound to the donut segment
})
.attr("fill","yellow")
And be sure to write the logic to undo it, by setting fill back to grey or none, in .on("mouseout".

The values on x-axis are descending values on grouped bar chart using d3.js

I have transformed the vertical grouped bar chart from horizontal grouped bar chart. I have taken the code from the site https://bl.ocks.org/mbostock/3887051. The problem is when I have transformed horizontal to vertical axis the values on x-axis are not ascending but they are descending i.e., the values are 10M, 8M, 6M, 4M etc....I am not sure where I have made a mistake.
<!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="//d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 20, right: 20, bottom: 50, left: 140},
width = 800 - margin.left - margin.right,
height = 900 - 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],.1);
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var xAxis = d3.svg.axis()
.scale(y)
.orient("top").ticks(5);
var yAxis = d3.svg.axis()
.scale(x0)
.orient("left");
var svg = d3.select("body").append("svg")
.attr("height", width + margin.left + margin.right)
.attr("width", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.csv("data.csv", function(error, data) {
if (error) throw error;
var ageNames = d3.keys(data[0]).filter(function(key) { return key !== "State"; });
data.forEach(function(d) {
d.ages = ageNames.map(function(name) { return {name: name, value: +d[name]}; });
});
x0.domain(data.map(function(d) { return d.State; }));
x1.domain(ageNames).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(d) { return d3.max(d.ages, function(d) { return d.value; }); })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0,0)")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "translate(0," + height + ")")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Population");
var state = svg.selectAll(".state")
.data(data)
.enter().append("g")
.attr("class", "state")
.attr("transform", function(d) { return "translate(0," + x0(d.State) + ")"; });
state.selectAll("rect")
.data(function(d) { return d.ages; })
.enter().append("rect")
.attr("height", x1.rangeBand())
.attr("y", function(d) { return x1(d.name); })
//.attr("x", function(d) { return y(d.value); })
.attr("width", function(d) { return height - y(d.value); })
.style("fill", function(d) { return color(d.name); });
var legend = svg.selectAll(".legend")
.data(ageNames.slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 10)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 14)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
});
</script>
You y-scale which is used on your x-axis is defined as:
var y = d3.scale.linear()
.range([height, 0] , 0.1);
This runs it in reverse and should be:
var y = d3.scale.linear()
.range([0, height] , 0.1);

Unable to create bar chart from d3 using URL

I am trying to create bar chart from json file which is a URL but i am unable to do so please help me, as i am new to d3. The code is here please tell me where i am making mistake.
var width = 960,
height = 500;
var y = d3.scale.linear().range([height, 0]);
var chart = d3.select("#chart")
.append("svg")
.attr("width", width)
.attr("height", height);
generateChart();
function generateChart() {
d3.json('http://dataserver.org:8009/topic/domain/cancer',
function(error, data) {
y.domain([0, d3.max(data, function(d) {
return d.occurrence
})]);
var barWidth = width / data.length; //width of each bar
var bar = chart.selectAll("g")
.data(data)
.enter()
.append("g")
.attr("transform", function(d, i) {
return "translate(" + i * barWidth + ", 0)";
});
bar.append("rect")
.attr("y", function(d) {
return y(d.occurrence);
})
.attr("height", function(d) {
return height - y(d.occurrence);
})
.attr("width", barWidth - 1);
bar.append("text")
.attr("x", barWidth / 2)
.attr("y", function(d) {
return y(d.occurrence) - 10;
})
.attr("dy", ".75em")
.text(function(d) {
return d.authorName;
});
});
};
It is not a problem of cross domain as I checked your API. Assuming that you are fetching d3.js from CDN, I am suggesting a change. Your chart will depend on "institute" as it is containing all the required values for chart. So I changed it as per structure your API's response.
**in some case data was coming as "undefined" so I am fetching d3.js from CDN
Here is whole modified file:-
function generateChart() {
d3.json('http://srvgal78.deri.ie:8009/topic/domain/cancer',
function(error, data) {
data = data.institute;
y.domain([0, d3.max(data, function(d) {
return d.occurrence
})]);
var barWidth = width / data.length; //width of each bar
var bar = chart.selectAll("g")
.data(data)
.enter()
.append("g")
.attr("transform", function(d, i) {
return "translate(" + i * barWidth + ", 0)";
});
bar.append("rect")
.attr("y", function(d) {
return y(d.occurrence);
})
.attr("height", function(d) {
return height - y(d.occurrence);
})
.attr("width", barWidth - 1);
bar.append("text")
.attr("x", barWidth / 2)
.attr("y", function(d) {
return y(d.occurrence) - 10;
})
.attr("dy", ".75em")
.text(function(d) {
return d.authorName;
});
});
};

Why is my d3.tool tip not working for the two of the lines?

The below is the codes that will be used to create the line charts. But the problem I am facing is that my d3.tool tip for the lines are not working accordingly. Also, I would like to be able to shorten the y-axis so that the lines will look to have a bigger gap in between.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: black ;
}
.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
h3 {
margin-left:220px;
}
.d3-tip {
line-height: 1;
font-weight: bold;
padding: 12px;
background: rgba(0, 0, 0, 0.8);
color: #fff;
border-radius: 2px;
}
</style>
<div>
<h3>Life Expectancy History</h3>
<body>
<script src="http://d3js.org/d3.v3.js"></script>
<script src="http://labratrevenge.com/d3-tip/javascripts/d3.tip.v0.6.3.js"></script>
<script>
var margin = { top: 30, right: 40, bottom: 30, left: 50 },
width = 600 - margin.left - margin.right,
height = 270 - margin.top - margin.bottom;
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
return "<strong>Male:</strong> <span style='color:white'>" + d.male + "%" + "</span></br><strong>World Rank:</strong> <span style='color:white'>" + d.rankmale;
});
var tip1 = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
return "<strong>Female:</strong> <span style='color:white'>" + d.female + "%" + "</span></br><strong>World Rank:</strong> <span style='color:white'>" + d.rankfemale;
});
var tip2 = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
return "<strong>Both Gender:</strong> <span style='color:white'>" + d.both + "%" + "</span></br><strong>World Rank:</strong> <span style='color:white'>" + d.rankall;
});
var parseDate = d3.time.format("%Y").parse;
var x = d3.time.scale()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var y1= d3.scale.linear()
.range([height, 0]);
var y2= d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom").ticks(5);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left").ticks(10);
var line = d3.svg.line()
.x(function (d) { return x(d.year); })
.y(function (d) { return y(d.male); });
var line2 = d3.svg.line()
.x(function (d) { return x(d.year); })
.y(function (d) { return y(d.female); });
var line3 = d3.svg.line()
.x(function (d) { return x(d.year); })
.y(function (d) { return y(d.both); });
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).call(tip1).call(tip2);
d3.json("lifehistory.json", function (error, data) {
data.forEach(function (d)
{
d.year = parseDate(d.year.toString());
d.male = +d.male;
d.female = +d.female;
d.both = +d.both;
});
x.domain(d3.extent(data, function (d) { return d.year; }));
y.domain([0, d3.max(data, function (d) { return d.male; })]);
y1.domain([0, d3.max(data, function (d) { return d.female; })]);
y2.domain([0, d3.max(data, function (d) { return d.both; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("text") // text label for the x axis
.attr("x", 265)
.attr("y", 240)
.style("text-anchor", "middle")
.text("Years");
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("Life Expectancy History");
svg.selectAll('.yaxis1')
.data(data)
.enter()
.append('circle')
.attr('class', 'yaxis1')
.attr('cx', function(datum){return x(datum.year)})
.attr('cy', function(datum){return y(datum.male)})
.attr('r', 3)
.attr('fill', 'red')
.on('mouseover', tip.show)
.on('mouseout', tip.hide);
svg.selectAll('.yaxis2')
.data(data)
.enter()
.append('circle')
.attr('class', 'yaxis2')
.attr('cx', function(datum){return x(datum.year)})
.attr('cy', function(datum){return y1(datum.female)})
.attr('r', 3)
.attr('fill', 'blue')
.on('mouseover', tip1.show)
.on('mouseout', tip1.hide);
svg.selectAll('.yaxis3')
.data(data)
.enter()
.append('circle')
.attr('class', 'yaxis3')
.attr('cx', function(datum){return x(datum.year)})
.attr('cy', function(datum){return y2(datum.both)})
.attr('r', 3)
.attr('fill', 'blue')
.on('mouseover', tip2.show)
.on('mouseout', tip2.hide);
svg.append("path")
.datum(data)
.attr("class", "line")
.style("stroke", "red")
.attr("d", line(data));
svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line2(data));
svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line3(data));
});
</script></div>
This is the line chart that I have created. The d3.tool tip is not working as what I would like as only one the the d3.tool tip is working on one line but the others are not appearing on the other lines. I would like to show the y-axis to have a bigger gap between each of the value and in other words, it means that the gap between lines will be bigger so that people can easily analysis the graph. Is there any method for me to do it that way?
This is the JSON file that I am using.
[{"year":"1960","male":"61.7","female":"65.7","both":"63.7","rankmale":"47","rankfemale":"50","rankall":"45"},
{"year":"1970","male":"65.4","female":"70.2","both":"67.7","rankmale":"45","rankfemale":"45","rankall":"44"},
{"year":"1980","male":"68.9","female":"74.2","both":"71.5","rankmale":"33","rankfemale":"36","rankall":"29"},
{"year":"1990","male":"71.9","female":"76.9","both":"74.3","rankmale":"28","rankfemale":"30","rankall":"30"},
{"year":"2000","male":"76.1","female":"80.1","both":"78.1","rankmale":"11","rankfemale":"24","rankall":"15"},
{"year":"2011","male":"80.1","female":"84.6","both":"82.3","rankmale":"6","rankfemale":"8","rankall":"5"}]
The problem with your code is from using different y scales for tooltips. You used the same y scale for the lines so they appear fine. But when you use different y scales for the circles, they will be plotted differently. So instead of having
var y = d3.scale.linear()
.range([height, 0]);
var y1= d3.scale.linear()
.range([height, 0]);
var y2= d3.scale.linear()
.range([height, 0]);
and
y.domain([0, d3.max(data, function (d) { return d.male; })]);
y1.domain([0, d3.max(data, function (d) { return d.female; })]);
y2.domain([0, d3.max(data, function (d) { return d.both; })]);
you can just use
var y = d3.scale.linear().range([height, 0]);
and
y.domain([50, 90]);
And then just use the same y scale when you add the circles.
Notice that I also replace your domain start value from 0 to 50. This will help shortening the y scale and space out your lines.
You can see a live demo at http://jsfiddle.net/B6cUS/