// initialize the chart var data = null; var svg = d3.select("#chart").append("svg"); var margin= 20; var margins = { top: 20, right:20, bottom:20, left:20 }, w = $(svg).parent().width(), h = $(svg).parent().height() - (margins.top + margins.bottom); function setupTooltipListeners(selection) { return selection.on("mouseover", function(d) { d3.select(this).attr("stroke","pink"); d3.select(this).attr("stroke-width",3); tooltip.html(tooltipFormatter(d)); tooltip.transition().call(_.partialRight(fadeIn,10)); }) .on("mousemove", function() { tooltip.style("top", (event.pageY-10)+"px") .style("left",(event.pageX+10)+"px"); }) .on("mouseout", function(){ tooltip.transition().call(fadeOut); d3.select(this).attr("stroke","none"); }); } function fadeOut(selection, duration) { return selection.transition() .duration(duration || 250) .style("opacity",0); } function fadeIn(selection, duration) { return selection.transition() .duration(duration || 250) .style("opacity",1); } function fadeOutAndRemove(selection, duration) { return fadeOut(selection,duration).remove(); } $("#percentage").change(function() { var pct = $(this).val(); var rowsToDrop = (1-(parseInt(pct,10) / 100)) * data.Results.length; var partialData = _.dropRight(data.Results, rowsToDrop); render(partialData); }); function rand(min, max) { return parseInt(Math.random() * (max-min+1), 10) + min; } function rc() { var h = rand(1, 360); // color hue between 1 and 360 var s = 58; // saturation 30-100% var l = 32; // lightness 30-70% return 'hsl(' + h + ',' + s + '%,' + l + '%)'; } var tooltip = d3.select("body") .append("div") .style("border-color", rc()) .attr("class","tooltip") .text("a simple tooltip"); var template = _.template( "<b><%=d.Name%></b><br/>" + "Days in Process: <%=ip%><br/>" + "PlanEstimate: <%=d.PlanEstimate%><br/>" + "on <%=when%>"); function tooltipFormatter(d) { return template({ d:d, ip: d.daysInProcess ? d.daysInProcess : Math.round(d.hoursInProcess/24*10)/10, when:moment(d._ValidFrom).format("YYYY-MM-DD")}); } /** * handle the incoming ajax data */ function onData(d) { data = d; render(data.Results); } function render(rows) { rows.forEach(function(row) { var d = new Date(row._ValidFrom); row.vf = d.getUTCFullYear() * 10000 + d.getUTCMonth() * 100 + d.getUTCDate(); var dt = new Date(row._ValidTo); if (dt.getUTCFullYear() >= 3000) { dt = d; row._ValidTo = row._ValidFrom; } row.vt = dt.getUTCFullYear() * 10000 + dt.getUTCMonth() * 100 + dt.getUTCDate(); }); var minDate = d3.min(rows, function(d) { return d._ValidFrom; }); var maxDate = d3.max(rows, function(d) { return d._ValidTo; }); var grouped = d3.nest() .key(function(d) { return d.ObjectID; }) .entries(rows); grouped.forEach(function(g) { g.ObjectID = g.values[0].ObjectID; g._ValidFrom = d3.min(g.values, function(v) { return v._ValidFrom; }); g._ValidTo = d3.max(g.values, function(v) { return v._ValidTo; }); g.daysInProcess = moment(g._ValidTo).diff(moment(g._ValidFrom),'days'); g.hoursInProcess = moment(g._ValidTo).diff(moment(g._ValidFrom),'hours'); g.PlanEstimate = _.max(_.compact(_.pluck(g.values,"PlanEstimate"))); g.Name = g.values[0].Name; }); var dip = function(d) { return d.daysInProcess; }; var maxDip = d3.max(grouped, dip); var median = d3.median(grouped, dip); var barWidth=(w / grouped.length); var xDomain = [new Date(minDate),new Date(maxDate)]; var xScale = d3.time.scale() .range([margins.left*2.5, w - (2.5 * margins.right)]) .domain(xDomain); var yScale = d3.scale .linear() .range([margins.top*5,h - margins.bottom]) .domain([Math.max(maxDip, 1.0),0]); var yAxis = d3.svg.axis() .orient("left") .scale(yScale); var xAxis = d3.svg.axis() .tickSize(0) .tickFormat(function(d){ return (d.getUTCMonth()+1) + '/' + d.getUTCDate(); }) .scale(xScale); svg.selectAll(".axis") .call(fadeOutAndRemove); svg.append("svg:g") .attr("class","axis") .attr("transform", "translate(0," + (h - margins.bottom*2) + ")") .call(xAxis) .selectAll("text") .style("text-anchor", "end") .attr("dx",-3) .attr("transform", function(d) { return "rotate(-90)"; }); svg.append("svg:g") .attr("class","axis") .attr("transform", "translate(" + (margins.left * 2) + "," + ((margins.top + margins.bottom) * -1) + ")").call(yAxis); svg.append("text") .attr("transform", "rotate(-90)") .attr("y", w-50) .attr("x", 0 - (h/2)) .attr("dy", "1em") .style("text-anchor", "middle") .text("Days in Process"); var r = function(d){ return 3 + (_.first(d.values).PlanEstimate || 0); }; var cx = function(d) { return xScale(new Date(d._ValidTo)) }; var cy = function(d) { return yScale(d.daysInProcess) - (margins.top + margins.bottom); } var circles = svg.selectAll("circle") .data(grouped, function(d) { return d.key; }); // after the join, these are 'circles to be added' circles.enter() .append("circle") .attr("r", 0) .attr("cx", w) .attr("cy", h) .attr("fill",rc) .attr("id", function(d){ return "circle_" + d.ObjectID; }) .call(setupTooltipListeners); // update existing circles circles.transition() .duration(1000) .attr("r", r) .attr("cx", cx) // x and y will move .attr("cy", cy); // remove circles circles.exit() .transition() .duration(1000) .attr("r",0) .attr("cx", w) .attr("cy", h) .remove(); var medianData = svg.selectAll(".median") .data([median], function(d) {return "theMedian";}); var medianY = yScale(median) - (margins.top + margins.bottom); medianData.enter() .append("line") .attr("class","median") .attr("x1",0) .attr("x2",w) .attr("stroke","red") .style("opacity","0.5"); medianData.transition() .attr("y1", medianY) .attr("y2", medianY); var mt = svg.selectAll(".medianText") .data([median],function(d) {return "theMedian";}); mt.enter() .append("text") .attr("y", medianY - 3) .attr("x", w-(margins.left+margins.right)) .style("font-size", "0.8em") .attr("fill","red") .attr("text-anchor", "end") .attr("class", "medianText"); mt.transition() .attr("y", medianY - 3) .text("Median : " + median); } d3.json(url, function(err,data){ onData(data); });
<link href='http://fonts.googleapis.com/css?family=Lato&subset=latin,latin-ext' rel='stylesheet' type='text/css'> <script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script> <script> url = "https://s3.amazonaws.com/trevershick-experiments/tip.json" </script> <div class='form-group'> <label for="percentage">Percentage of Data Set</label> <select name="percentage" id="percentage"> <option>1</option> <option>10</option> <option>25</option> <option>75</option> <option selected>100</option> </select> </div> <div class="well"> <div id="chart"></div> </div>
body { font-family:Lato; } #chart { border:1px solid grey; padding:0px; margin:0; width:100%; height:400px; } svg { display:block; width:100%; height:100%; } .axis { font-size:10px; } .tooltip b { font-size:1.1em; color:black; } .tooltip { color:grey; background-color:white; border-radius:4px; border:1px solid; padding: 5px; margin:3px; position :absolute; z-index:10; opacity:0; } .axis path { fill: none; stroke: #777; shape-rendering: crispEdges; } .axis text { font-size: 13px; }