#zui53 Zoom Adaptation of Mike Bostock’s Line Chart I have not seen anyone pair zui53 with so I thought I would experiment by adding the zoom and drag functionality to this line chart. I also wanted to test my skills by using a long form csv dataset with two series. zui53 zoom and drag does not work in Internet Explorer. Works best when opened in full window
Original Readme.md is below
A line chart with mouseover so that you can read the y-value based on the closest x-value. The x-value is found using d3.mouse and scale.invert. The y-value is then found by bisecting the data.
<!DOCTYPE html>
<html style='width: 100%; height:100%;'>
<head>
<script src='//ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js'></script>
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="zui53.js"></script>
<script>
function initZUI() {
console.log(ZUI53);
zui = new ZUI53.Viewport(document.getElementById('zui'));
zui.addSurface(new ZUI53.Surfaces.SVG($("#linechart")));
var pan_tool = new ZUI53.Tools.Pan(zui);
zui.toolset.add(pan_tool);
pan_tool.attach();
zui.showBounds({ width: 1000, height: 1000, x: 0, y: 0 });
}
function buildSVG() {
var margin = { top: 20, right: 20, bottom: 30, left: 50 },
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
d3.selectAll("#zui").append("svg")
.attr("width", "100%")
.attr("height", "100%")
.append("g")
.attr("id", "linechart")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
drawd3line(width, height);
}
function drawd3line(width, height) {
var svg = d3.selectAll("#zui").selectAll("#linechart");
var parseDate = d3.time.format("%Y-%m-%d").parse,
bisectDate = d3.bisector(function (d) { return d.date; }).left;
var x = d3.time.scale()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var line = d3.svg.line()
.x(function (d) { return x(d.date); })
.y(function (d) { return y(d.cumul); });
var color = d3.scale.category20();
d3.csv("indexdata.csv", function (error, data) {
data.forEach(function (d) {
d.date = parseDate(d.date);
d.price = +d.price;
});
var indexkeys = d3.keys(d3.nest().key(function (d) { return d.indexname; }).map(data))
indexkeys.forEach(function (name) {
//ugly ugly but can't think of the best place to put this transform
//gets cumulative growth by dividing price by first price
var temp = data.filter(function (d) { return d.indexname == name; });
temp.forEach(function (d) { d.cumul = +d.price / temp[0].price });
})
indexdata = d3.nest().key(function(d) {return d.indexname;}).entries(data);
x.domain(d3.extent(data, function (d) { return d.date; }));
y.domain(d3.extent(data, function (d) { return d.cumul; }));
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("Cumulative Growth");
var indexGroup = svg.selectAll(".indexname")
.data(indexdata)
.enter().append("g")
.attr("class", "indexname")
.attr("id", function (d) { return d.key });
indexGroup.append("path")
.attr("class", "line")
.attr("d", function (d) { return line(d.values); })
.attr("stroke", function (d) { return color(d.key); });
//original ugly way of doing it until I found Mike Bostock's example
// var nodes = indexGroup.selectAll()
// .data(function (d) { return d.values; })
// .enter().append("g")
// .attr("class", "points")
// .attr("transform", function (d) { return "translate(" + x(d.date) + "," + y(d.cumul) + ")"; });
//nodes.append('circle')
// .attr("r", 10)
// .attr("fill", function (d) { return color(d.indexname); })
// nodes.append('text')
// .attr("text-anchor", "middle")
// .attr("dx", 12)
// .attr("dy", "-.35em")
// .text(function (d) { return d.indexname + "\n " + Math.round(d.cumul * 100) / 100; })
// .attr("fill", "gray");
var focus = svg.selectAll(".focus")
.data(indexkeys).enter().append("g")
.attr("class", "focus")
.attr("id", function (d) { return "focus-" + d; })
.style("display", "none");
focus.append("circle")
.attr("r", 4.5);
focus.append("text")
.attr("x", 9)
.attr("dy", ".35em");
svg.append("rect")
.attr("class", "overlay")
.attr("width", width)
.attr("height", height)
.on("mouseover", function () { focus.style("display", null); })
.on("mouseout", function () { focus.style("display", "none"); })
.on("mousemove", mousemove);
function mousemove() {
var x0 = x.invert(d3.mouse(this)[0]);
indexkeys.forEach(function (indexname, i1) {
var i = bisectDate(indexdata[i1].values, x0, 1),
d0 = indexdata[i1].values[i - 1],
d1 = indexdata[i1].values[i],
d = x0 - d0.date > d1.date - x0 ? d1 : d0;
d3.select("#focus-" + indexname)
.attr("transform", "translate(" + x(d.date) + "," + y(d.cumul) + ")")
.attr("fill", color(indexname));
d3.select("#focus-" + indexname).select("text").text(indexname + " " + Math.round(d.cumul*100)/100).attr("fill", color(indexname));
});
}
});
initZUI();
}
</script>
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
.line {
fill: none;
stroke-width: 5px;
}
/*ugly way before I found Mike Bostock's example
.points {opacity: 0;}
.points:hover {opacity: 1;}*/
.overlay {
fill: none;
pointer-events: all;
}
</style>
</head>
<body onload='buildSVG()' style='width:100%; height:100%; margin: 0; padding: 0;'>
<div id='zui'>
</div>
</body>
</html>