This chart is a variation of my relative-budgets chart, part of the d3-charts
collection available here.)
The data for the chart was based on this one from All in with Chris.
xxxxxxxxxx
<html lang="en">
<head>
<meta charset="utf-8">
<style>
svg {
margin-left: auto; margin-right: auto;
display: block;
}
line,
rect {
shape-rendering: crispEdges;
}
text {
font: 10px sans-serif;
}
text.title {
font-weight: bold;
}
path {
display: none;
}
path.symbol,
.legend path {
display: inline;
stroke: #000;
}
line {
stroke: #000;
}
rect {
fill: #ABDDA4;
}
line.divider {
stroke-dasharray: 4,2;
}
</style>
</head>
<body>
<!-- <script src="https://d3js.org/d3.v3.min.js"></script> -->
<script src="d3.min.js?v=3.2.8"></script>
<script type="text/javascript"charset="utf-8">
// Settings
var width = 440,
height = 150; // Compute dynanically?
/**
Accounting for
* valueTypes.length
* keys.length
*/
var barHeight = 8,
dividerHeight = 7;
var textContainerSize = 16;
// fontsize + lineheight;
var margin = {
'top': 15,
'right': 20,
'bottom': 0,
'left': 20
};
margin.hor = margin.left + margin.right;
margin.ver = margin.top + margin.bottom;
// Config
var thresholdTitle = "2014 Budgets Compared",
valueTypes = ["money"], // ["percentage", "money", "quantity"]
moneyPrefix = "$", // "$", "£", "€"
moneySuffix = "", // "USD", "GBP", "EUR"
quantityNoun = "", // "Jobs", "F-35s"
valueLabel = "symbol", // "text", "symbol", "none"
useMultipleSymbols = false, // true, false
symbolType = "cross";
// "circle", "cross", "diamond", "square",
// "triangle-up", "triangle-down"
// Data in JSON format
var jsonData = {
"Obama Budget": {
"money": 1235000000000
},
"Ryan Budget": {
"money": 967000000000
},
"Murray-Ryan Deal": {
"money": 1012000000000
},
"Senate Budget": {
"money": 1058000000000
}
};
var domain = getValues(jsonData, valueTypes[0], true),
keys = Object.keys(jsonData);
// Helper functions
function getValues(jsonData, valueType, isFloat) {
return Object.keys(jsonData).map(function(key){
if (isFloat === true) {
return parseFloat(
jsonData[key][valueType]
);
} else {
return jsonData[key][valueType];
}
});
};
function displayValue(d, i, valueType) {
if (valueType === "percentage") {
if (d === 0) {
return 0 + "%";
}
else if (d < 0) {
return "-" + -1*d + "%";
} else { // d < 0
return shownPlusSign + d + "%";
}
}
else if (valueType === "money") {
var val = parseMoney.scale(money[i]);
if (d === 0) {
return moneyPrefix + 0 + moneySuffix;
}
else if (d < 0) {
return "-" + moneyPrefix + -1*val + parseMoney.symbol + moneySuffix;
} else { // d < 0
return shownPlusSign + moneyPrefix + val + parseMoney.symbol + moneySuffix;
}
}
else if (valueType === "quantity") {
var val = parseQuantity.scale(quantity[i]);
if (d === 0) {
return 0 + "" + quantityNoun;
}
else if (d < 0) {
return "-" + -1*val + parseQuantity.symbol + "" + quantityNoun;
} else { // d < 0
return shownPlusSign + val + parseQuantity.symbol + "" + quantityNoun;
}
}
};
for (var i = 0; i < valueTypes.length; i++) {
if (valueTypes[i] === "percentage") {
var percentage = getValues(jsonData, "percentage", true);
}
else if (valueTypes[i] === "money") {
var money = getValues(jsonData, "money", false);
var parseMoney = d3.formatPrefix(d3.max(money));
}
else if (valueTypes[i] === "quantity") {
var quantity = getValues(jsonData, "quantity", false);
var parseQuantity = d3.formatPrefix(d3.max(quantity));
}
}
var svg = d3.select("body")
.append("svg")
.attr({
"width": width,
"height": height
});
var x = d3.scale.linear()
.domain(d3.extent(domain))
.range([0, width - margin.hor]);
// Show value plus signs for distinction, if negative values exist
var shownPlusSign = (d3.min(x.domain()) < 0) ? "+" : "";
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickSize(13)
.tickValues(domain)
.tickFormat("");
var g = svg.append("g")
.attr({
"class": "bar",
"transform": "translate(" + margin.left + "," + margin.top + ")"
});
// Bar
g.append("rect")
.attr({
"height": barHeight,
"x": x.range()[0],
"width": x.range()[1]
});
// Divider showing the fabled "centre" of the budget bar
g.append("line")
.attr("class", "divider")
.attr({
"x1": (x.range()[0] + x.range()[1])/2,
"x2": (x.range()[0] + x.range()[1])/2,
"y1": -dividerHeight,
"y2": dividerHeight + barHeight
});
// Ticks and title
g.call(xAxis)
// Title
.append("text")
.attr({
"class": "title",
"y": -6
})
.text(thresholdTitle);
// Display symbols
if (valueLabel === "symbol") {
var color = d3.scale.category10().domain(domain);
// Bar symbol
g.selectAll(".symbol")
.data(domain)
.enter().append("path")
.attr("class", "symbol")
.attr({
"transform": function(d) {
return "translate(" + x(d) + "," + barHeight/2 + ")";
},
"d": function(d, i) {
return (keys[i] === "Murray-Ryan Deal") ? d3.svg.symbol().type(symbolType)() : d3.svg.symbol().type("circle")();
},
"fill": function(d) { return color(d); }
});
var legendX = (x.range()[0] + x.range()[1])/2 - 25, // Revise
legendY = textContainerSize*valueTypes.length + barHeight + 32,
yPos = function(d, i) {
return i*textContainerSize;
};
var legend = svg.selectAll(".legend")
.data(domain)
.enter().append("g")
.attr("class", "legend")
.attr("transform", "translate(" + legendX + "," + legendY + ")");
// Legend symbols
legend.append("path")
.attr("class", "legend-symbol")
.attr({
"transform": function(d, i) {
return "translate(" + 0 + "," + yPos(d, i) + ")";
},
"d": function(d, i) {
return (keys[i] === "Murray-Ryan Deal") ? d3.svg.symbol().type(symbolType)() : d3.svg.symbol().type("circle")();
},
"fill": function(d) { return color(d); }
});
// Legend text
legend.append("text")
.attr("class", "legend-text")
.attr({
"x": 12, // Revise
"y": function(d, i) {
return yPos(d, i);
},
"dy": ".3em" // Revise
})
.text(function(d, i) { // fetch string
return keys[i];
});
}
// Display text labels --- work in progress
if (valueLabel === "text") {
g.selectAll(".name")
.data(domain)
.enter().append("text")
.attr("class", "name")
.attr({
"text-anchor": function(d) {
return (x(d) > (x.range()[0] + x.range()[1])/2) ? "end" : "start";
}, // Revise
"x": x,
"y": 0 // Revise
})
.text(function(d, i) { // fetch string
return keys[i];
});
}
// Display any additional values, if they exist
var labels = g.selectAll(".values")
.data(domain)
.enter();
for (var c = 0; c < valueTypes.length; c++) {
labels.append("text")
.attr({
"text-anchor": "middle",
"background-color": function(d, i) {
return (keys[i] === "Murray-Ryan Deal") ? "yellow" : "blue";
},
"x": function(d) { return x(d); },
"y": function() {
// Revise
return c*textContainerSize + barHeight + 15;
}
})
.text(function(d, i) {
return displayValue(d, i, valueTypes[c]);
});
}
</script>
</body>
</html>
Modified http://d3js.org/d3.v3.min.js to a secure url
https://d3js.org/d3.v3.min.js