This is a d3.js powered schedule chart that shows datetime data as bars on a calendar. I designed this to visualize scheduling preferences, to easily spot the darker colored overlaps in different collaborators' preferences. Within a webapp, collaborators could select preferences, and these (sql datetime format) would be fed to the visualization. Shown here with static dummy data.
Last active
May 9, 2017 13:07
-
-
Save LauraHornbake/6248343 to your computer and use it in GitHub Desktop.
Schedule chart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<head> | |
<meta charset="utf-8"> | |
<style> | |
body { | |
margin: 0; | |
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; | |
font-size: 13px; | |
} | |
.axis path { | |
fill:none; | |
stroke:grey; | |
shape-rendering:crispEdges; | |
} | |
.grid .tick { | |
stroke: lightgrey; | |
opacity: 0.8; | |
stroke-dasharray: 2,12; | |
} | |
.grid path { | |
stroke-width: 0; | |
} | |
rect.times.bar { | |
fill:#006D2C; | |
fill-opacity:0.5; | |
stroke:#006D2C; | |
stroke-width:1px; | |
} | |
</style> | |
</head> | |
<body> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<script> | |
var data = [{"start":"2015-08-18 18:00:00","stop":"2015-08-18 20:00:00"},{"start":"2015-08-21 06:00:00","stop":"2015-08-21 10:00:00"},{"start":"2015-08-23 18:00:00","stop":"2015-08-23 23:00:00"},{"start":"2015-08-23 20:21:00","stop":"2015-08-23 21:21:00"},{"start":"2015-08-25 17:01:00","stop":"2015-08-25 22:01:00"},{"start":"2015-08-26 17:23:00","stop":"2015-08-26 23:23:00"},{"start":"2015-08-26 17:52:00","stop":"2015-08-26 23:52:00"},{"start":"2015-08-26 21:01:00","stop":"2015-08-26 23:01:00"},{"start":"2015-08-27 11:23:00","stop":"2015-08-27 23:23:00"},{"start":"2015-08-29 20:21:00","stop":"2015-08-29 21:21:00"},{"start":"2015-08-31 20:21:00","stop":"2015-08-31 21:21:00"}]; | |
var first = d3.time.day.floor( new Date(data[0].start)), | |
last = d3.time.day.ceil( new Date(data[data.length-1].stop)), | |
dRange = [d3.min(data,function(d){ | |
return d3.time.day.floor(new Date(d.start))}), | |
d3.max(data,function(d){ | |
return d3.time.day.ceil(new Date(d.stop))})]; | |
var m = {top: 40, right: 20, bottom: 20, left: 60}, | |
width = window.innerWidth*.8, | |
barSize = 25, | |
height = ((dRange[1]-dRange[0])/(24*60*60*1000))* barSize; | |
var day = d3.time.format("%w"), | |
week = d3.time.format("%U"), | |
hour = d3.time.format("%X"), | |
format = d3.time.format("%Y-%m-%d %X"), | |
now = new Date(); | |
var svg = d3.select("body").append("div") | |
.attr("class","d3-container container") | |
.selectAll("svg").data(d3.range(1)) | |
.enter().append("svg") | |
.attr("id","viz") | |
.attr("width",width + m.right +m.left) | |
.attr("height",height + m.top + m.bottom) | |
.append("g") | |
.attr('transform', 'translate(' + m.left + ', ' + m.top + ')'); | |
function viewBars (data) { | |
/* set up scales */ | |
var x = d3.time.scale() | |
.domain([0,24]) | |
.range([0, width]); | |
var y =d3.time.scale() | |
.domain(dRange) | |
.range([0, height]); | |
var tfh = d3.time.scale() //TwentyFourHour scale | |
.domain([d3.time.hour(new Date(2014,0,1,0,0,0)), | |
d3.time.hour(new Date(2014,0,2,0,0,0)),]) | |
.range([0,width]); | |
/* add bars to chart */ | |
svg.append("g") | |
.attr("class","chart") | |
.selectAll("rect") | |
.data(data) | |
.enter() | |
.append("rect") | |
.attr("class","times bar") | |
.attr("x",function(d){ | |
var h = hour(new Date(d.start)).split(":"), //changes datum from string, to proper Date Object, back to hour string and splits | |
xh = parseFloat(h[0])+parseFloat(h[1]/60); //time (hour and minute) as decimal | |
return x(xh); | |
;}) | |
.attr("y",function(d) { return y(d3.time.day.floor(new Date(d.start)))} | |
) | |
.attr("width",function(d){ | |
var hstart = new Date(d.start), | |
hstop = new Date(d.stop); | |
return x((hstop-hstart)/3600000); //date operations return a timestamp in miliseconds, divide to convert to hours | |
}) | |
.attr("height",barSize) | |
.attr("rx",3) | |
.attr("ry",3) | |
.append("svg:title") | |
.text(function(d){return(d.start)+' - '+(d.stop);}) | |
.datum(function(d){return Date.parse(d)}) | |
; | |
/*add axes and grid*/ | |
var xAxis = d3.svg.axis() | |
.scale(tfh) | |
.tickFormat(d3.time.format("%H:%M")); | |
var xGrid = d3.svg.axis() | |
.scale(tfh) | |
.orient("bottom") | |
.ticks(12); | |
var yAxis = d3.svg.axis() | |
.scale(y) | |
.tickFormat(d3.time.format("%m/%d")); | |
svg.append("g") | |
.attr("class", "x top axis") | |
.call(xAxis.orient("top")); | |
svg.append("g") | |
.attr("class","x grid") | |
.call(xGrid | |
.tickSize(height, 0, 0) | |
.tickFormat("")); | |
svg.append("g") | |
.attr("class","y left axis") | |
.call(yAxis.orient("left")); | |
}; | |
viewBars(data); | |
</script> | |
</body> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@LauraHornbake you may run into trouble if you're trying to change the domain of the
x
scale to be let's say work hours. If any duration is less than your left end, you'll get a negative width.I changed line 98 to:
And thanks, your chart really helped me!