|
(function() { |
|
|
|
/** |
|
* Initialize D3 by making AJAX call to tsv data |
|
*/ |
|
d3.tsv("inflation.tsv", function(error, data) { |
|
data = buildInflation(data); |
|
|
|
vis = {}; //init vis object |
|
buildVis(data); |
|
|
|
}); |
|
|
|
|
|
/** |
|
* Initiate and build vis |
|
*/ |
|
function buildVis(data) { |
|
|
|
// vis attributes |
|
vis.margin = { |
|
top: 50, |
|
right: 50, |
|
bottom: 0, |
|
left: 50 |
|
}; |
|
vis.width = 450 - vis.margin.left - vis.margin.right; |
|
vis.height = 450 - vis.margin.top - vis.margin.bottom; |
|
vis.gridSize = Math.floor(vis.width / Math.sqrt(data.length)); |
|
vis.basePrice = 600; |
|
vis.ratio = 1; |
|
|
|
// create main svg |
|
vis.svg = d3.select("#vis").append("svg") |
|
.attr("width", vis.width + vis.margin.left + vis.margin.right) |
|
.attr("height", vis.height + vis.margin.top + vis.margin.bottom) |
|
.append("g") |
|
.attr("transform", "translate(" + vis.margin.left + ", " + vis.margin.top + ")"); |
|
|
|
// create vis colorscale |
|
vis.colorScale = d3.scale.quantile() |
|
.domain([0, d3.max(data, function(d) { return d.value; })]) |
|
.range(colorbrewer.RdYlBu[10].reverse()); |
|
|
|
// create selected tooltip |
|
vis.tooltipSelected = d3.select("body").append("div") |
|
.classed("tooltip", true) |
|
.classed("tooltip-selected", true) |
|
.style("opacity", 0); |
|
|
|
// create tooltip |
|
vis.tooltip = d3.select("body").append("div") |
|
.classed("tooltip", true) |
|
.style("opacity", 0); |
|
|
|
// add x-label (target year) |
|
vis.svg.append("text") |
|
.classed("x-label", true) |
|
.attr("text-anchor", "middle") |
|
.attr("x", vis.width / 2) |
|
.attr("y", -10) |
|
.text("Base Year"); |
|
|
|
// add y-label (base year) |
|
vis.svg.append("text") |
|
.classed("y-label", true) |
|
.attr("text-anchor", "middle") |
|
.attr("x", -vis.width / 2) |
|
.attr("y", -15) |
|
.attr("dy", ".75em") |
|
.attr("transform", "rotate(-90)") |
|
.text("Target Year"); |
|
|
|
// create heatmap tiles |
|
vis.tiles = vis.svg.selectAll(".tiles") |
|
.data(data).enter() |
|
.append("rect").classed("tiles", true) |
|
.attr("x", function(d) { return d.x * vis.gridSize; }) |
|
.attr("y", function(d) { return d.y * vis.gridSize; }) |
|
.attr("rx", 1) |
|
.attr("ry", 1) |
|
.attr("stroke", function(d) { |
|
return d.targetYear == 2015 || d.baseYear == 2015 ? "orange" : "lightgray"; |
|
}) |
|
.attr("stroke-width", "1px") |
|
.attr("width", vis.gridSize) |
|
.attr("height", vis.gridSize) |
|
.attr("fill", function(d) { return vis.colorScale(d.value); }) |
|
.on("mouseover", function(d) { |
|
tooltipShow(vis.tooltip, d); |
|
d3.select(this).attr("stroke", "red"); |
|
}) |
|
.on("mouseleave", function(d) { |
|
tooltipHide(vis.tooltip); |
|
d3.select(this) |
|
.attr("stroke", function(d) { |
|
return d.targetYear == 2015 || d.baseYear == 2015 ? "orange" : "lightgray"; |
|
}); |
|
}) |
|
.on("click", function(d) { |
|
tooltipHide(vis.tooltipSelected); |
|
tooltipShow(vis.tooltipSelected, d); |
|
updateYears(d); |
|
}); |
|
|
|
d3.select("#price").on("input", function() { |
|
updatePrice(+this.value); |
|
}); |
|
} |
|
|
|
/** |
|
* Helper function to build inflation data from tsv data file |
|
*/ |
|
function buildInflation(data) { |
|
result = []; |
|
for (var x = 0; x < data.length; x++) { |
|
baseCPI = +data[x].cpi; |
|
baseYear = +data[x].year; |
|
for (var y = 0; y < data.length; y++) { |
|
targetCPI = +data[y].cpi; |
|
targetYear = +data[y].year; |
|
d = { |
|
baseYear: baseYear, |
|
targetYear: targetYear, |
|
baseCPI: baseCPI, |
|
targetCPI: targetCPI, |
|
value: targetCPI / baseCPI, |
|
x: x, |
|
y: y |
|
}; |
|
result.push(d); |
|
} |
|
} |
|
return result; |
|
} |
|
|
|
/** |
|
* Helper function to update price |
|
*/ |
|
function updatePrice(price) { |
|
vis.basePrice = price; |
|
d3.selectAll(".base-price").text(price.toFixed(2)); |
|
d3.selectAll(".target-price").text((vis.ratio * vis.basePrice).toFixed(2)); |
|
} |
|
|
|
function updateYears(d) { |
|
vis.ratio = +d.value; |
|
d3.select(".base-year").text(+d.baseYear); |
|
d3.select(".target-year").text(+d.targetYear); |
|
d3.select(".target-price").text((+d.value * vis.basePrice).toFixed(2)); |
|
} |
|
|
|
/** |
|
* Helper function to show tooltip |
|
*/ |
|
function tooltipShow(tooltip, d) { |
|
tooltip.style("opacity", 0.9) |
|
.html( |
|
"$<span class='base-price'>" + vis.basePrice.toFixed(2) + "</span>" + |
|
"<sub> " + d.baseYear + " (CPI: " + d.baseCPI.toFixed(2) + ") " + "</sub>" + |
|
"<br />" + |
|
"$<span class='target-price'>" + (vis.basePrice * d.value).toFixed(2) + "</span>" + |
|
"<sub> " + d.targetYear + " (CPI: " + d.targetCPI.toFixed(2) + ") " + "</sub>" |
|
) |
|
.style("left", (d3.event.pageX) + "px") |
|
.style("top", (d3.event.pageY) + "px"); |
|
} |
|
|
|
/** |
|
* Helper function to hide tooltip |
|
*/ |
|
function tooltipHide(tooltip) { |
|
tooltip.style("opacity", 0); |
|
} |
|
})(); |