adding mouseover event to d3.js bar chart [duplicate] - html

This question already has an answer here:
D3: .transition() not working with events
(1 answer)
Closed 2 years ago.
So I have a bar chart with buttons that will update preset input data and now I want to include tooltip so that it will show data from y-axis when hovered. I tried to follow some tutorials that I could find but still failed. Below is what I've done so far but I took out most of the data to avoid confusion.
I got unknown type:mouseover for the following code
var AU = [
{group: "NSW", value: 871.8},
{group: "VIC", value: 736.8},
{group: "QLD", value: 517.9},
{group: "SA", value: 460.1},
{group: "WA", value: 498.5},
{group: "TAS", value: 451.4},
{group: "NT", value: 410.1},
{group: "ACT", value: 699.1},
{group: "Australia", value: 678.5}
];
// set the dimensions and margins of the graph
var margin = {top: 30, right: 30, bottom: 70, left: 60},
width = 600 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
.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 + ")");
// Initialize the X axis
var x = d3.scaleBand().range([ 0, width ])
.paddingInner(0.2);
var xAxis = svg.append("g")
.attr("transform", "translate(0," + height + ")")
// Initialize the Y axis
var y = d3.scaleLinear()
.range([ height, 0]);
var yAxis = svg.append("g")
.attr("class", "myYaxis")
// A function that create / update the plot for a given variable:
function update(data) {
// Update the X axis
x.domain(data.map(function(d) { return d.group; }))
xAxis.call(d3.axisBottom(x))
// text label for the x axis
// text label for the x axis
svg.append("text")
.attr("transform",
"translate(" + (width/2) + " ," +
(height + margin.top + 20) + ")")
.style("text-anchor", "middle")
.text("Postcode");
// Update the Y axis
y.domain([0, d3.max(data, function(d) { return d.value }) ]);
yAxis.transition().duration(1000).call(d3.axisLeft(y));
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left)
.attr("x",0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Price ('000k)");
// Create the u variable
var u = svg.selectAll("rect")
.data(data)
u
.enter()
.append("rect") // Add a new rect for each new elements
.merge(u) // get the already existing elements as well
.transition() // and apply changes to all of them
.duration(1000)
.attr("x", function(d) { return x(d.group); })
.attr("y", function(d) { return y(d.value); })
.attr("width", x.bandwidth())
.attr("height", function(d) { return height - y(d.value); })
.attr("fill", "#69b3a2")
.on("mouseover", function(d){tooltip.text(d); return tooltip.style("visibility", "visible");})
.on("mousemove", function(){return tooltip.style("top", (d3.event.pageY-10)+"px").style("left",(d3.event.pageX+10)+"px");})
.on("mouseout", function(){return tooltip.style("visibility", "hidden");});
// If less group in the new dataset, I delete the ones not in use anymore
u
.exit()
.remove()
}
// Initialize the plot with the first dataset
update(AU)

The reason for this error is that by calling .transition(), the selected element changes from the result of d3.select to the currently active transition. And while transition.on supports events ("start", "end", "interrupt"), it does not support "mouseover". You can fix this by moving the .on() to before your call to .transition(). I've illustrated below how this error occurs.
const svg = d3.select("body")
.append("svg");
svg
.append("rect")
.attr("width", 100)
.attr("height", 100)
.attr("fill", "blue")
.on("mouseover", function() { d3.select(this).attr("fill", "red"); });
svg
.append("rect")
.attr("x", 100)
.attr("width", 100)
.attr("height", 100)
.attr("fill", "white")
.transition()
.duration(500)
.attr("fill", "green")
.on("mouseover", function() { d3.select(this).attr("fill", "red"); });
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>

Related

Error: <rect> attribute width: Expected length for svg d3 v4

I wanted to produce a graph with the years range from 2012-2017 and the number of incidents occurs in the year. I have nested the data. I am not able to produce a bar graph and was given the errors : Error: attribute height: Expected length, "NaN". and Error: attribute width: Expected length, "15,430". I tried to fix the error using previous posts suggestions but no luck. Any help with the code or suggestions would be appreciated it. thanks
js:
//set the dimensions and margins of the graph
var margin = {
top: 20,
right: 20,
bottom: 30,
left: 50
},
width = 500 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
padding = 15;
// Fomat timeStamp to year
var dateFormat = d3.timeFormat("%Y");
//append the svg object to the body of the page
var svg = d3.select("#graph").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.json("https://moto.data.socrata.com/resource/jfwn-iu5d.json",
function(data) {
// Objects
data.forEach(function(data) {
data.incident_description = data.incident_description;
data.incident_datetime = dateFormat(new
Date(data.incident_datetime));
});
// Nest data by year of incident
var NestbyDate = d3.nest()
.key(function(d) {
return d.incident_datetime;
})
.key(function(d) {
return d.incident_description + " " + d.incident_datetime;
})
.rollup(function(leaves) {
return d3.sum(leaves, function(d) {
return (d.incident_description)
});
})
.entries(data);
console.log(NestbyDate);
data.forEach(function(data) {
data.incident_description = +data.incident_description;
data.incident_datetime = +data.incident_datetime;
});
var x_domain = d3.extent(NestbyDate, function(d) {
return d.key;
});
var y_domain = d3.extent(NestbyDate, function(d) {
return d.values;
});
// set the ranges
var x = d3.scaleLinear().domain(x_domain).range([padding, width]);
var y = d3.scaleLinear().domain([0, 550]).range([height, 0]);
// Add the X Axis
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x).ticks(6));
// Add the Y Axis
svg.append("g")
.attr("class", "axis")
.call(d3.axisLeft(y));
// Text label for the x-axis
svg.append("text")
.attr("x", width / 2)
.attr("y", height + margin.top + 9)
.style("text-anchor", "center")
.text("Day Date Format")
.text("Year");
// Text Label for y-axis
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left)
.attr("x", 0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Number of crime incidents");
// Draw the bars
svg.selectAll(".rect")
.data(NestbyDate)
.enter()
.append("rect")
.attr("class", "bar")
.attr("x", function(d) {
return (d.key);
})
.attr("y", function(d) {
return (d.values);
})
.attr("width", x.range())
.attr("height", function(d) {
return height - y(d.values);
});
});
You did a few things wrong.
var x should be a d3.scaleBand() not a linear scale.
Therefore the domain for your x-axis should look like this in this case:
var x = d3.scaleBand().domain(NestbyDate.map(d => d.key))
and since you're nesting the data you should change the y attr to d.values.length (you also forgot to add the y() and x() in some of the rect attr) Here are these changes:
svg.selectAll(".rect")
.data(NestbyDate)
.enter()
.append("rect")
.attr("class", "rect")
.attr("x", function(d) {
return x(d.key);
})
.attr("y", function(d) {
return y(d.values.length);
})
.attr("width", x.bandwidth())
.attr("height", function(d) {
return y(0) - y(d.values.length);
});
});
I didn't look super closely at the data but it should give you some idea what you need to change at least
Here's a working Plunker

Bar graph is not rendering

I'm trying to duplicate this code found on bl.ocks.org, but I can't get my bar graph to render. I'm pulling from a csv instead of a tsv, but I don't think that would made a difference. There aren't any errors written to the console. Where am I going wrong?
<script type="text/javascript">
$( document ).ready(function() {
loadChart();
});
function loadChart() {
var svg = d3.select("svg");
var margin = { top: 20, right: 20, bottom: 30, left: 40},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;
var x = d3.scaleBand().rangeRound([0,width]).padding(0.1),
y = d3.scaleLinear().rangeRound([height, 0]);
var g = svg.append(g).attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.csv("Data/data.csv", function(d) {
d.NumProducts = +d.NumProducts;
return d;
}, function(error, data) {
if (error) throw error;
x.domain(data.map(function(d) { return d.Category; }));
y.domain([0, d3.max(data, function(d) { return d.NumProducts; })]);
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
g.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(y).ticks(10))
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("Dy", "0.71em")
.attr("text-anchor", "end")
.text("Number of Products");
g.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class" , "bar")
.attr("x", function(d) { return x(d.Category); })
.attr("y", function(d) { return y(d.NumProducts); })
.attr("width", x.bandwidth())
.attr("height", function(d) { return height - y(d.NumProducts); });
});
}
</script>
The csv:
Category,NumProducts
Beverages,12
Condiments,12
Confections,13
Dairy Products,10
Grains/Cereals,7
Meat/Poultry,6
Produce,5
Seafood,12
The main issue that you have is down to missing speech marks.
var g = svg.append(g)
Should read:
var g = svg.append("g")
If you were to inspect your DOM you would see
<svg>
<undefined transform="translate(40,20)">
... (correct DOM)
</undefined>
</svg>
The reason that nothing renders after this point, is because the browser doesn't know how to render an <undefined> element, therefore it stops trying to render.
This immediately indicated that one of the append calls wasn't working because the DOM didn't understand the type of element given to it. Making the change produces this:

d3 v4 padding inner set to .001 doesn't actually result in small padding between ranges

Is there a way to minimize the padding between ranges in a grouped horizontal bar chart?
I am building a horizontal bar chart directive for AngularJS and at the moment I am pretty close but it's the spacing that I am not exactly happy with. If there is a better way to build the vertical ranges I would very much appreciate any tips. Here's my result so far:
angular.module('MissionControlApp').directive('d3GroupedHorizontalBarChart2', ['d3', '$timeout', function(d3, $timeout) {
return {
restrict: 'E',
scope: {
data: '=',
onClick: '&d3OnClick'
},
link: function(scope, ele) {
var refreshScope = function() {
scope.$apply();
};
var svg = d3.select(ele[0])
.append("svg")
.attr("width", "100%");
// on window resize, re-render d3 canvas
window.onresize = function() {
return scope.$apply();
};
scope.$watch(function(){
return angular.element(window)[0].innerWidth;
}, function(){
return scope.render(scope.data);
}
);
// watch for data changes and re-render
scope.$watch("data", function(newVals) {
if(!newVals) return;
return scope.render(newVals);
}, true);
// define render function for grouped bar charts
scope.render = function(data){
if(!data) return;
// remove all previous items before render
svg.selectAll("*").remove();
// setup variables
var margin = {top: 25, right: 40, bottom: 70, left: 150};
var width = d3.select(ele[0])._groups[0][0].offsetWidth - margin.left - margin.right;
var height = (scope.data.length * 60);
svg.attr('height', height + margin.top + margin.bottom);
var y0 = d3.scaleBand()
.rangeRound([0, height])
.paddingInner(0.01);
var y1 = d3.scaleBand()
.padding(0.01);
var x = d3.scaleLinear()
.rangeRound([0, width]);
var color = d3.scaleLinear()
.domain([0, 25, 50, 75, 100])
.range(["#51b75d", "#90eb9d","#ffff8c","#f5c93f","#c45c44"])
.interpolate(d3.interpolateHcl);
var xAxis = d3.axisBottom(x)
.tickSizeInner(-(height-5))
.tickPadding(8);
var keys = d3.keys(data[0]).filter(function(key) { return key !== "user"; });
y0.domain(data.map(function(d) { return d.user; }));
y1.domain(keys).rangeRound([0, y0.bandwidth()]);
x.domain([0, 100]);
// Define bars
var bar = svg.selectAll(".bar")
.data(data)
.enter().append("g")
.attr("transform", function(d) { return "translate(" + margin.left + "," + (y0(d.user) + (y0.bandwidth()/2) + margin.top - y1.bandwidth()) + ")"; });
var barEnter = bar.selectAll("rect")
.data(function(d) { return d.values; })
.enter();
barEnter.append("rect")
.attr("height", y1.bandwidth())
.attr("y", function(d) {return y1(d.name); })
.attr("x", 0)
.attr("value", function(d){return d.name;})
.attr("width", 0)
.attr("fill", function(d) { return color(d.value); })
.on("mouseover", function() { d3.select(this).style("cursor", "pointer");})
.on("mouseout", function() { d3.select(this).style("cursor", "default");})
.on("click", function(d){
scope.onClick({item: d});
d3.select(".selectedBlueFill").classed("selectedBlueFill", false);
d3.select(this).classed("selectedBlueFill", true);
$timeout(refreshScope, 0, false); // flush the digest cycle
})
.transition()
.duration(1000)
.attr("width", function(d) { return x(d.value); });
barEnter.append("text")
.attr("fill", "#000")
.attr("y", function(d){return y1(d.name) + (y1.bandwidth() / 2);})
.attr("x", function(d){return x(d.value);})
.attr("dx", 5)
.attr("dy", ".35em")
.text(function(d){return parseFloat(d.value).toFixed(0) + "%";})
.attr("fill-opacity", 0)
.transition()
.duration(1500)
.attr("fill-opacity", 1);
// Set up x axis
svg.append("g")
.attr("class", "axisHorizontal")
.attr("transform", "translate(" + margin.left + "," + (height + margin.top) + ")")
.call(xAxis);
// Set up y axis
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(d3.axisLeft(y0));
// Draw the legend
// Create the gradient for the legend
svg.append("defs")
.append("linearGradient")
.attr("id", "legend-traffic")
.attr("x1", "0%").attr("y1", "0%")
.attr("x2", "100%").attr("y2", "0%")
.selectAll("stop")
.data(color.range())
.enter().append("stop")
.attr("offset", function(d,i) { return i/(color.range().length-1); })
.attr("stop-color", function(d) { return d; });
// Legend variables
var legendWidth = width * 0.6;
var legendHeight = 10;
// Legend container
var legendSvg = svg.append('g')
.attr("class", "legendWrapper")
.attr("transform", "translate(" + ((width + margin.left + margin.right)/2) + "," + (height + margin.top + margin.bottom) + ")");
// Draw the rectangle
legendSvg.append("rect")
.attr("class", "legendRect")
.attr("x", -legendWidth/2)
.attr("y", -30)
.attr("width", legendWidth)
.attr("height", legendHeight)
.attr("fill", "url(#legend-traffic)");
// Append title
legendSvg.append("text")
.attr("class", "legendTitle")
.attr("x", 0)
.attr("y", -35)
.attr("text-anchor", "middle")
.text("Worksets Opened %");
// Set scale for x-axis
var xScale = d3.scaleLinear()
.range([0, legendWidth])
.domain([0,100]);
// Define x-axis
var legendAxis = d3.axisBottom(xScale).ticks(5);
// Set up x-axis
legendSvg.append("g")
.attr("class", "axisLegend")
.attr("transform", "translate(" + (-legendWidth/2) + "," + (legendHeight-30) + ")")
.call(legendAxis);
};
}
};
}]);
However the result I am getting is with large inner spacing. I am setting the .paddingInner property to 0.001 and still pretty much end up with large spacing......ideas?
The idea would be to make groups, where each group hold unique bars for the group.
Now once you have the group you can alter the between distance with some maths, as shown below in snippet below:
var bar = chart
.selectAll("g")
.data(zippedData)
.enter().append("g")
.attr("transform", function(d, i) {
//here barHeight is the width of the bars.
return "translate(" + spaceForLabels + ","
+ (i * barHeight + gapBetweenGroups * (0.5 + Math.floor(i/data.series.length))) + ")";
});
By changing value of gapBetweenGroups you can regulate the distance of the groups.
Working code here

How can I use d3.layout.stack with this code to draw a line chart using D3.js?

I'm trying to draw a Area chart . I couldn't access to the data and I don't know WHY . In the firefox console " data is not defined". This is my code and data .
This data which is JSON format is located on the server side :
{"Id":466,"Name":"korea",
"Occurrences":[
{"OccurrenceDate":"\/Date(1398207600000+0100)\/","OccurrenceFrequency":27},
{"OccurrenceDate":"\/Date(1398726000000+0100)\/","OccurrenceFrequency":1},
{"OccurrenceDate":"\/Date(1398898800000+0100)\/","OccurrenceFrequency":4},
{"OccurrenceDate":"\/Date(1399071600000+0100)\/","OccurrenceFrequency":303}
]
}
This is my code
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://code.jquery.com/jquery-1.4.2.min.js"></script>
<script>
var margin = {top: 80, right: 80, bottom: 80, left: 80},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var parseDate1 = d3.time.format.iso.parse;
// Scales and axes. Note the inverted domain for the y-scale: bigger is up!
var x = d3.time.scale().range([0, width]),
y = d3.scale.linear().range([height, 0]),
xAxis = d3.svg.axis().scale(x).tickSize(-height).tickSubdivide(true),
yAxis = d3.svg.axis().scale(y).ticks(4).orient("right");
// An area generator, for the light fill.
var area = d3.svg.area()
.interpolate("monotone")
.x(function(d) { return x(d.OccurrenceDate); })
.y0(height)
.y1(function(d) { return y(d.OccurrenceFrequency); });
// A line generator, for the dark stroke.
var line = d3.svg.line()
.interpolate("monotone")
.x(function(d) { return x(d.OccurrenceDate); })
.y(function(d) { return y(d.OccurrenceFrequency); });
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 strURI = "http://tr-l6/STWebService/Service.svc/session/Fetchnodehistorybyname?
strnodename=korea";
// use jquery to get the json dataset because I cannot get d3.json to work with Firefox/Chrome (but it is OK with IE)
// this is probably a jsonp / cross domain issue that requires further tweaking in the WCF web,config file
// d3.json(strURI,function(error, graph) {
$.ajax({
type: "GET",
url: strURI,
dataType: 'jsonp',
success: function (graph) {
x
data.forEach(function(d) {
// Convert unix date format to regular format
var dc
//dc = (d.date).substring(1, 26);
dc = d.OccurrenceDate.substring(6, 16)
console.log(dc);
dc = new Date(dc*1000)
console.log(dc);
d.OccurrenceDate= parseDate1(dc);
d.OccurrenceFrequency = +d.OccurrenceFrequency;
return d
});
x.domain(d3.extent(data, function(d) { return d.OccurrenceDate; }));
y.domain([0, d3.max(data, function(d) { return d.OccurrenceFrequency; })]);
svg.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area);
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")
}});
I dont know if it is the reason for the Error but you missed the </script> Script at the End

How can I perform this d3.js magic (Grouped Bar Chart) with JSON rather than CSV?

I am attempting to follow this example: http://bl.ocks.org/3887051
However, instead of a CSV, I have a JSON object.
Now, I saw that I could convert my JSON into a CSV by following the instructions here: How to convert JSON to CSV format and store in a variable
That feels like a hack though.
Here's my JSON:
[{"YEAR":2012,"MONTH":1,"MMM":"JAN","Total_Flights":30,"Completed":21,"Canceled":7,"Aborted":2},
{"YEAR":2012,"MONTH":2,"MMM":"FEB","Total_Flights":54,"Completed":28,"Canceled":20,"Aborted":6},
{"YEAR":2012,"MONTH":3,"MMM":"MAR","Total_Flights":39,"Completed":25,"Canceled":12,"Aborted":2},
{"YEAR":2012,"MONTH":4,"MMM":"APR","Total_Flights":27,"Completed":21,"Canceled":6,"Aborted":0},
{"YEAR":2012,"MONTH":5,"MMM":"MAY","Total_Flights":35,"Completed":21,"Canceled":12,"Aborted":2},
{"YEAR":2012,"MONTH":6,"MMM":"JUN","Total_Flights":15,"Completed":10,"Canceled":4,"Aborted":1},
{"YEAR":2012,"MONTH":7,"MMM":"JUL","Total_Flights":42,"Completed":18,"Canceled":21,"Aborted":3},
{"YEAR":2012,"MONTH":8,"MMM":"AUG","Total_Flights":43,"Completed":29,"Canceled":8,"Aborted":6},
{"YEAR":2012,"MONTH":9,"MMM":"SEP","Total_Flights":28,"Completed":20,"Canceled":8,"Aborted":0},
{"YEAR":2012,"MONTH":10,"MMM":"OCT","Total_Flights":43,"Completed":24,"Canceled":18,"Aborted":1},
{"YEAR":2012,"MONTH":11,"MMM":"NOV","Total_Flights":35,"Completed":18,"Canceled":17,"Aborted":0},
{"YEAR":2012,"MONTH":12,"MMM":"DEC","Total_Flights":45,"Completed":9,"Canceled":32,"Aborted":4},
{"YEAR":2013,"MONTH":1,"MMM":"JAN","Total_Flights":49,"Completed":4,"Canceled":43,"Aborted":2}]
My game plan is to have the chart display four bars for each month: Total, Completed, Canceled and Aborted.
How would I transform this example code to deal with my JSON?
I'll be swimming through the d3.js tutorials here: https://github.com/mbostock/d3/wiki/Tutorials until I figure it out or someone wise shows me the ropes.
I'll periodically update this post with my progress.
UPDATE #1: Anyone coming here should check out these godsend tutorials: http://alignedleft.com/tutorials/d3/
I'm still working on it. Such a powerful library. I'll report back again within a few hours.
After a few days of diving all in D3's documentation and the great tutorials I linked to earlier, I finally created my first graph.
// Make a JSON object out of the data.d string receieved.
json_data = jQuery.parseJSON(data.d)
// width = 960 - 40 - 20 = 900
// height = 500 - 20 - 30 = 450
var margin = { top: 20, right: 20, bottom: 30, left: 40 },
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// Information on ordinal scales can be found at: https://github.com/mbostock/d3/wiki/Ordinal-Scales
// An ordinal scale that sets the output range from the specified continuous interval (that is, [0, width]).
// The array interval contains two elements representing the min and max numeric values.
// This interval is subdivided into n evenly-spaced bands, where n is the number of (unique) values in the domain.
// The bands may be offset from the edge of the interval and other bands according to the specifided padding,
// which defaults to zero.
// The padding is typically in the range [0,1] (0.1 in this example) and corrseponds to the amount of space
// in the range interval to allocate to padding.
// A value of 0.5 means that the band width will be equal to the padding width.
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
// Constructs a new ordinal scale with an empty domain and an empty range.
// The ordinal scale is invalid (always returning undefined) until an output range is specified).
var x1 = d3.scale.ordinal();
// Information on linear scales can be found at: https://github.com/mbostock/d3/wiki/Quantitative-Scales
// Quantitative scales have a continuous domain, such as the set of real numbers, or dates.
// Linear scales are a type of quantitative scale.
// Linear scales are the most common scale, and a good default choice to map a continuous input domain to a
// continous output range.
// The mapping is linear in that the output range value y can be expressed as a linear function of the
// input domain value x: y = mx + b.
// The input domain is typically a dimension of the data that you want to visualize, such as the height of
// students (measured in meters) in a sample population.
// The output range is typically a dimension of the desired output visualization, such as the height of bars
// (measured in pixels) in a histogram.
// This will set up our y height scale.
var y = d3.scale.linear()
.range([height, 0]);
// Colors of the graph.
//
// First : Total flights #097054 (green)
// Second : Completed flights #6599FF (blue)
// Third : Cancelled flights #FFDE00 (yellow)
// Fourth : Aborted flights #FF9900 (orange)
var color = d3.scale.ordinal()
.range(["#097054", "#6599FF", "#FFDE00", "#FF9900"]);
// Set up the xAxis to use our x0 scale and be oriented on the bottom.
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom");
// We don't worry about tickFormat here, as the ticks will be determined by the data.
// Set up the yAxis to use our y scale and be oriented on the left.
// Additionally, set the tick format to display appropriate labels on the axis (taking out for now).
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
// .tickFormat(d3.format(".2s"));
// Set up the svg canvas with the width and height we calculated earlier.
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 + ")");
// Move it to the right margin.left pixels, and move it down margin.top pixels
// Our JSON looks like:
// [{ "YEAR": 2012, "MONTH": 1, "MMM": "JAN", "Total": 36, "Completed": 21, "Canceled": 10, "Aborted": 5 },
// { "YEAR": 2012, "MONTH": 2, "MMM": "FEB", "Total": 60, "Completed": 30, "Canceled": 21, "Aborted": 9 }]
// data = d3.nest()
// .key(function (d) { return d.MMM + " " + d.YEAR; })
// .entries(json_data)
data = json_data
// seriesNames = "Total", "Completed", "Canceled" and "Aborted" See, we're filtering out "YEAR", "MONTH" and "MMM"
var seriesNames = d3.keys(data[0]).filter(function (key) { return (key !== "YEAR") && (key !== "MONTH") && (key !== "MMM"); });
// alert(JSON.stringify(seriesNames));
// alert(seriesNames);
data.forEach(function (d) {
d.Flights = seriesNames.map(function (name) { return { name: name, value: +d[name] }; });
//alert("hi --- " + JSON.stringify(d.Flights));
});
//alert(JSON.stringify(data));
//x0.domain(data.map(function (d) { return d.State; }));
// Change State to be MMM, YEAR (for example: "Jan 2012") Could change this to Jan '12
x0.domain(data.map(function (d) { return d.MMM + " " + d.YEAR; }));
//alert(JSON.stringify(data.map(function (d) { return d.MMM + " " + d.YEAR; })));
// //x1.domain(seriesNames).rangeRoundBands([0, x0.rangeBand()]);
x1.domain(seriesNames).rangeRoundBands([0, x0.rangeBand()]);
// //y.domain([0, d3.max(data, function (d) { return d3.max(d.ages, function (d) { return d.value; }); })]);
// // Make the y domain go from 0 up to the max of d.Total (Total flights)
// y.domain([0, d3.max(data, function (d) { return d3.max(d.Total); })]);
y.domain([0, (10 + d3.max(data, function (d) { return d3.max(d.Flights, function (d) { return d.value; }); }))]);
// The axis business
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("# of Flights");
// From this point to...
//var state = svg.selectAll(".state")
// .data(data)
//.enter().append("g")
// .attr("class", "g")
// .attr("transform", function (d) { return "translate(" + x0(d.State) + ",0)"; });
var state = svg.selectAll(".state")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function (d) { return "translate(" + x0(d.MMM + " " + d.YEAR) + ",0)"; });
//alert(JSON.stringify(d.Flights[0]));
state.selectAll("rect")
.data(function (d) { return d.Flights; })
.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); });
var legend = svg.selectAll(".legend")
.data(seriesNames.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; })
.on("click", function (d) {
alert(d);
//state.selectAll("rect")
//.update()
// .exit().transition()
// .attr("height", 0)
// .remove();
//state.selectAll("rect")
//.update()
//state.selectAll("rect").exit().transition().attr("height", 0).remove();
});
After this graph, it was very easy to make the multiple donut graphs as well as the stacked chart.