|
// change to any of the .CSV files (latency, duration, etc) |
|
// e.g. blocked, connected, duration, latency, req_body, res_body, rx, service, tx |
|
var filename = "latency.csv"; |
|
var containerSelection = d3.select('.heatmap-container') |
|
var container = containerSelection.node() |
|
var cw = container.clientWidth, ch = container.clientHeight |
|
var margin = {top: 25, right: 100, bottom: 25, left: 50}, |
|
width = cw - margin.left - margin.right, |
|
height = ch - margin.top - margin.bottom; |
|
|
|
var parseDate = d3.timeParse("%Y-%m-%d-%H"), |
|
formatDate = d3.timeFormat("%m/%d @ %H"), |
|
tickFormat = function(d, i) { return d3.format(".1s")(d) }, |
|
coerceTypes = function(d) { |
|
d.date = parseDate(d.date); |
|
d.bucket = +d.bucket; |
|
d.count = +d.count; |
|
} |
|
; |
|
|
|
var pluck = function (key) { |
|
return function (obj) { |
|
return obj[key]; |
|
} |
|
}; |
|
var pipe = function () { |
|
var args = [].slice.call(arguments).reverse(), |
|
f = args.shift(), |
|
g = args.shift(), |
|
fog = g ? function() { return f(g.apply(this, arguments)); } : f; |
|
|
|
return args.length ? compose.apply(this, [fog].concat(args)) : fog; |
|
}; |
|
|
|
var x = d3.scaleTime().range([0, width]), |
|
y = d3.scaleBand().rangeRound([height, 0]), |
|
z = d3.scaleLinear().range(["white", "steelblue"]); |
|
|
|
var xAxis = d3.axisBottom() |
|
.scale(x) |
|
.ticks() |
|
.tickFormat(formatDate); |
|
|
|
var yAxis = d3.axisLeft() |
|
.scale(y) |
|
.tickFormat(tickFormat) |
|
|
|
// The size of the buckets in the CSV data file. |
|
// This could be inferred from the data if it weren't sparse. |
|
var dayMs = 864e5, |
|
xStep = dayMs/24; |
|
|
|
var svg = containerSelection.append("svg") |
|
.attr("width", width + margin.left + margin.right) |
|
.attr("height", height + margin.top + (3*margin.bottom)) |
|
.append("g") |
|
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); |
|
|
|
var tooltip = d3.select('.histogram-container') |
|
var barChart = BarChart() |
|
barChart |
|
.margin(margin) |
|
.height(height) |
|
.width(width) |
|
|
|
d3.csv(filename, function(error, rows) { |
|
if (error) throw error; |
|
drawHeatmap(rows) |
|
}); |
|
function drawHeatmap(rows) { window.rows = rows; |
|
// Coerce the CSV data to the appropriate types. |
|
rows.forEach(coerceTypes); |
|
|
|
var keys = { |
|
bucket: pluck('bucket'), |
|
date: pipe(pluck('date'), formatDate) |
|
}; |
|
|
|
var nest = { |
|
byBucket: d3.nest().key(keys.bucket), |
|
byDate: d3.nest().key(keys.date) |
|
}; |
|
|
|
var map = { |
|
byBucket: nest.byBucket.map(rows, d3.map), |
|
byDate: nest.byDate.map(rows, d3.map) |
|
}; |
|
|
|
// Compute the scale domains. |
|
x.domain(d3.extent(rows, pluck('date'))); |
|
y.domain(map.byBucket.keys()) |
|
z.domain(d3.extent(rows, function(d) { return d.count * d.bucket; })); |
|
|
|
// Extend the x-domain to fit the last bucket. |
|
x.domain([x.domain()[0], +x.domain()[1] + xStep]); |
|
|
|
// Display the tiles for each non-zero bucket. |
|
svg.selectAll(".tile") |
|
.data(rows) |
|
.enter().append("rect") |
|
.attr("class", "tile") |
|
.attr("x", function(d) { return x(d.date); }) |
|
.attr("y", function(d) { return y(d.bucket)-y.bandwidth()/2; }) |
|
.attr("width", x(xStep) - x(0)) |
|
.attr("height", y.bandwidth()) |
|
.style("fill", function(d) { return z(d.count * d.bucket); }) |
|
.on('mouseover', function (d, i, all) { |
|
var histogram = map.byDate.get(keys.date(d)) |
|
var data = d3.range(20).map(function(d){return {"x":d,"y":Math.floor(Math.random() * 100)}}); |
|
var data2 = histogram.map(d => ({x: d.bucket, y: d.count })) |
|
console.log(data2) |
|
tooltip |
|
.datum(data2) |
|
.call(barChart); |
|
var message = d.count + ' @ ' + d.bucket +' = ' + d.count * d.bucket |
|
}) |
|
// .on('mouseout', function (d) { tooltip.text('') }) |
|
; |
|
|
|
// Add a legend for the color values. |
|
var legend = svg.selectAll(".legend") |
|
.data(z.ticks(6).slice(1).reverse()) |
|
.enter().append("g") |
|
.attr("class", "legend") |
|
.attr("transform", function(d, i) { return "translate(" + (width + 20) + "," + (20 + i * 20) + ")"; }); |
|
|
|
legend.append("rect") |
|
.attr("width", 20) |
|
.attr("height", 20) |
|
.style("fill", z); |
|
|
|
legend.append("text") |
|
.attr("x", 26) |
|
.attr("y", 10) |
|
.attr("dy", ".35em") |
|
.text(tickFormat); |
|
|
|
svg.append("text") |
|
.attr("class", "label") |
|
.attr("x", width + 20) |
|
.attr("y", 10) |
|
.attr("dy", ".35em") |
|
.text("Count * Value"); |
|
|
|
|
|
// Add an x-axis with label. |
|
svg.append("g") |
|
.attr("class", "x axis") |
|
.attr("transform", "translate(0," + height + ")") |
|
.call(xAxis) |
|
.append("text") |
|
.attr("class", "label") |
|
.attr("x", width) |
|
.attr("y", -6) |
|
.attr("text-anchor", "end") |
|
.text("Date"); |
|
|
|
// Add a y-axis with label. |
|
svg.append("g") |
|
.attr("class", "y axis") |
|
.call(yAxis) |
|
.append("text") |
|
.attr("class", "label") |
|
.attr("y", 6) |
|
.attr("dy", ".71em") |
|
.attr("text-anchor", "end") |
|
.attr("transform", "rotate(-90)") |
|
.text("Value") |
|
; |
|
} |