// Generated by CoffeeScript 2.0.0 (function() { // layout setting var H, W, all_items, draw, enter_items, flag, height, items, legend, margin, stack, svg, vis, width, x, y, y_axis; width = document.body.getBoundingClientRect().width; height = document.body.getBoundingClientRect().height; margin = 30; W = width - margin * 3; H = height - margin * 3; svg = d3.select('svg'); vis = svg.append('g').attrs({ width: width - margin, height: height - margin, transform: `translate(${margin}, ${margin})` }); // button switch flag = true; d3.select('.switch').on('click', function() { if (flag) { draw('data_2.json'); } else { draw('data_1.json'); } return flag = !flag; }); // scales x = d3.scaleLinear().domain([0, 24]).range([0, W]); y = d3.scaleLinear().rangeRound([H, 0]); // x-axis vis.append('g').attrs({ transform: `translate(0, ${H})` }).call(d3.axisBottom(x).ticks(24)).append('text').attrs({ fill: '#000', transform: `translate(${W}, 30)`, 'text-anchor': 'middle' }).text("Hours"); // y-axis y_axis = vis.append('g'); y_axis.append('text').attrs({ fill: '#000', x: 10, y: -5, dy: '0.71em', 'text-anchor': 'start' }).text("Bike Slots status"); // stack layout stack = d3.stack().keys(["free_bikes", "empty_slots", "unavailable_slots"]); // legend legend = vis.append('g').attrs({ class: 'legend', transform: `translate(0, ${height - 60})` }); items = legend.selectAll('.item').data([ { "label": "free bikes", "color": "#ccebc5" }, { "label": "empty slots", "color": "#f2f2f2" }, { "label": "unavailable slots", "color": "#fbb4ae" } ]); enter_items = items.enter().append('g').attrs({ class: 'item' }); all_items = enter_items.merge(items); all_items.append('rect').attrs({ x: function(d, i) { return i * 100; }, width: 10, height: 20, fill: function(d) { return d.color; } }); all_items.append('text').attrs({ x: function(d, i) { return 15 + i * 100; }, y: 10, dy: '0.35em' }).text(function(d) { return d.label; }); items.exit().remove(); // MAIN visualization function draw = function(filename) { // data loading return d3.json(filename, function(data) { var all_bars, all_groups, bars, enter_bars, enter_groups, groups, stacked, t_data; /* data transformation */ t_data = data.map(function(datum) { var empty_slots, free_bikes, unavailable_slots; free_bikes = datum.bin.map(function(doc) { return doc.doc.free_bikes * doc.duration; }).reduce(function(acc, cur) { return acc + cur; }); empty_slots = datum.bin.map(function(doc) { return doc.doc.empty_slots * doc.duration; }).reduce(function(acc, cur) { return acc + cur; }); unavailable_slots = datum.bin.map(function(doc) { return doc.doc.unavailable_slots * doc.duration; }).reduce(function(acc, cur) { return acc + cur; }); return { hour: datum.hour, free_bikes: free_bikes / 3600, empty_slots: empty_slots / 3600, unavailable_slots: unavailable_slots / 3600 }; }); /* y-axis setting */ // update y scale domain according to data y.domain([ 0, d3.max(t_data, function(d) { return d.free_bikes + d.empty_slots + d.unavailable_slots; }) + 1 ]); y_axis.call(d3.axisLeft(y).ticks(y.domain()[1])); /* stacked bar chart */ stacked = stack(t_data); // groups groups = vis.selectAll('.group').data(stacked); enter_groups = groups.enter().append('g').attrs({ class: function(d) { return `group ${d.key}`; } }); all_groups = enter_groups.merge(groups); groups.exit().remove(); // bars bars = all_groups.selectAll('.bar').data(function(d) { return d; }); bars.transition().duration(750).attrs({ x: function(d) { return x(d.data.hour) + W / 24 / 20; }, y: function(d) { return y(d[1]); }, width: W / 24 - W / 24 / 10, height: function(d) { return H - y(d[1] - d[0]); } }); enter_bars = bars.enter().append('rect').attrs({ class: 'bar', x: function(d) { return x(d.data.hour) + W / 24 / 20; }, y: function(d) { return y(d[1]); }, width: W / 24 - W / 24 / 10, height: function(d) { return H - y(d[1] - d[0]); } }); all_bars = enter_bars.merge(bars); return bars.exit().remove(); }); }; draw('data_1.json'); }).call(this);