An example of a resuable spiral heatmap.
The data is from the Makeover Monday series, about German car and truck production.
Built with blockbuilder.org
| license: mit |
An example of a resuable spiral heatmap.
The data is from the Makeover Monday series, about German car and truck production.
Built with blockbuilder.org
| // A spiral heatmap | |
| // The following options are available | |
| // radius: radius of the overall plot, not including any labels. Default 250 | |
| // holeRadiusProportion: the proportion (0 to 1) of the radius (see above) that is left as a hole. Default 0. | |
| // arcsPerCoil: a 'coil' is one revolution of the spiral. This sets how many arcs (or arcs) you want per coil. Typically this would | |
| // be set according to the periodicity of the data. For example, 12 for months per year, 24 for hours per day, etc | |
| // coilPadding: the proportion (0 to 1) of the coil width that is used for padding between coils. Useful for making the spiral very noticeable | |
| // arcLabel: the field name to use for the labels around the circumference | |
| // coilLabel: the field name to use for the labels at the beginning of each coil | |
| function spiralHeatmap () { | |
| // constants | |
| const radians = 0.0174532925 | |
| // All options that are accessible to caller | |
| // Default values | |
| var radius = 250 | |
| var holeRadiusProportion = 0.3 // proportion of radius | |
| var arcsPerCoil = 12 // assuming months per year | |
| var coilPadding = 0 // no padding | |
| var arcLabel = '' // no labels | |
| var coilLabel = '' // no labels | |
| function chart (selection) { | |
| selection.each(function (data) { | |
| const arcAngle = 360 / arcsPerCoil | |
| const labelRadius = radius + 20 | |
| var arcLabelsArray = [] | |
| for (var i = 0; i < arcsPerCoil; i++) { | |
| arcLabelsArray.push(i) | |
| } | |
| // Create/update the x/y coordinates for the vertices and control points for the paths | |
| // Stores the x/y coordinates on the data | |
| updatePathData(data) | |
| let thisSelection = d3 | |
| .select(this) | |
| .append('g') | |
| .attr('class', 'spiral-heatmap') | |
| var arcLabelsG = thisSelection | |
| .selectAll('.arc-label') | |
| .data(arcLabelsArray) | |
| .enter() | |
| .append('g') | |
| .attr('class', 'arc-label') | |
| arcLabelsG | |
| .append('text') | |
| .text(function (d) { | |
| return data[d][arcLabel] | |
| }) | |
| .attr('x', function (d, i) { | |
| let labelAngle = i * arcAngle + arcAngle / 2 | |
| return x(labelAngle, labelRadius) | |
| }) | |
| .attr('y', function (d, i) { | |
| let labelAngle = i * arcAngle + arcAngle / 2 | |
| return y(labelAngle, labelRadius) | |
| }) | |
| .style('text-anchor', function (d, i) { | |
| return i < arcLabelsArray.length / 2 ? 'start' : 'end' | |
| }) | |
| arcLabelsG | |
| .append('line') | |
| .attr('x2', function (d, i) { | |
| let lineAngle = i * arcAngle | |
| let lineRadius = chartRadius + 10 | |
| return x(lineAngle, lineRadius) | |
| }) | |
| .attr('y2', function (d, i) { | |
| let lineAngle = i * arcAngle | |
| let lineRadius = chartRadius + 10 | |
| return y(lineAngle, lineRadius) | |
| }) | |
| var arcs = thisSelection | |
| .selectAll('.arc') | |
| .data(data) | |
| .enter() | |
| .append('g') | |
| .attr('class', 'arc') | |
| arcs.append('path').attr('d', function (d) { | |
| // start at vertice 1 | |
| let start = 'M ' + d.x1 + ' ' + d.y1 | |
| // inner curve to vertice 2 | |
| let side1 = | |
| ' Q ' + | |
| d.controlPoint1x + | |
| ' ' + | |
| d.controlPoint1y + | |
| ' ' + | |
| d.x2 + | |
| ' ' + | |
| d.y2 | |
| // straight line to vertice 3 | |
| let side2 = 'L ' + d.x3 + ' ' + d.y3 | |
| // outer curve vertice 4 | |
| let side3 = | |
| ' Q ' + | |
| d.controlPoint2x + | |
| ' ' + | |
| d.controlPoint2y + | |
| ' ' + | |
| d.x4 + | |
| ' ' + | |
| d.y4 | |
| // combine into string, with closure (Z) to vertice 1 | |
| return start + ' ' + side1 + ' ' + side2 + ' ' + side3 + ' Z' | |
| }) | |
| // create coil labels on the first arc of each coil | |
| coilLabels = arcs | |
| .filter(function (d) { | |
| return d.arcNumber == 0 | |
| }) | |
| .raise() | |
| coilLabels | |
| .append('path') | |
| .attr('id', function (d) { | |
| return 'path-' + d[coilLabel] | |
| }) | |
| .attr('d', function (d) { | |
| // start at vertice 1 | |
| let start = 'M ' + d.x1 + ' ' + d.y1 | |
| // inner curve to vertice 2 | |
| let side1 = | |
| ' Q ' + | |
| d.controlPoint1x + | |
| ' ' + | |
| d.controlPoint1y + | |
| ' ' + | |
| d.x2 + | |
| ' ' + | |
| d.y2 | |
| return start + side1 | |
| }) | |
| .style('opacity', 0) | |
| coilLabels | |
| .append('text') | |
| .attr('class', 'coil-label') | |
| .attr('x', 3) | |
| .attr('dy', -4) | |
| .append('textPath') | |
| .attr('xlink:href', function (d) { | |
| return '#path-' + d[coilLabel] | |
| }) | |
| .text(function (d) { | |
| return d[coilLabel] | |
| }) | |
| }) | |
| function updatePathData (data) { | |
| let holeRadius = radius * holeRadiusProportion | |
| let arcAngle = 360 / arcsPerCoil | |
| let dataLength = data.length | |
| let coils = Math.ceil(dataLength / arcsPerCoil) // number of coils, based on data.length / arcsPerCoil | |
| let coilWidth = chartRadius * (1 - holeRadiusProportion) / (coils + 1) // remaining chartRadius (after holeRadius removed), divided by coils + 1. I add 1 as the end of the coil moves out by 1 each time | |
| data.forEach(function (d, i) { | |
| let coil = Math.floor(i / arcsPerCoil) | |
| let position = i - coil * arcsPerCoil | |
| let startAngle = position * arcAngle | |
| let endAngle = (position + 1) * arcAngle | |
| let startInnerRadius = holeRadius + i / arcsPerCoil * coilWidth | |
| let startOuterRadius = | |
| holeRadius + | |
| i / arcsPerCoil * coilWidth + | |
| coilWidth * (1 - coilPadding) | |
| let endInnerRadius = holeRadius + (i + 1) / arcsPerCoil * coilWidth | |
| let endOuterRadius = | |
| holeRadius + | |
| (i + 1) / arcsPerCoil * coilWidth + | |
| coilWidth * (1 - coilPadding) | |
| // vertices of each arc | |
| d.x1 = x(startAngle, startInnerRadius) | |
| d.y1 = y(startAngle, startInnerRadius) | |
| d.x2 = x(endAngle, endInnerRadius) | |
| d.y2 = y(endAngle, endInnerRadius) | |
| d.x3 = x(endAngle, endOuterRadius) | |
| d.y3 = y(endAngle, endOuterRadius) | |
| d.x4 = x(startAngle, startOuterRadius) | |
| d.y4 = y(startAngle, startOuterRadius) | |
| // CURVE CONTROL POINTS | |
| let midAngle = startAngle + arcAngle / 2 | |
| let midInnerRadius = | |
| holeRadius + (i + 0.5) / arcsPerCoil * coilWidth | |
| let midOuterRadius = | |
| holeRadius + | |
| (i + 0.5) / arcsPerCoil * coilWidth + | |
| coilWidth * (1 - coilPadding) | |
| // MID POINTS, WHERE THE CURVE WILL PASS THRU | |
| d.mid1x = x(midAngle, midInnerRadius) | |
| d.mid1y = y(midAngle, midInnerRadius) | |
| d.mid2x = x(midAngle, midOuterRadius) | |
| d.mid2y = y(midAngle, midOuterRadius) | |
| d.controlPoint1x = (d.mid1x - 0.25 * d.x1 - 0.25 * d.x2) / 0.5 | |
| d.controlPoint1y = (d.mid1y - 0.25 * d.y1 - 0.25 * d.y2) / 0.5 | |
| d.controlPoint2x = (d.mid2x - 0.25 * d.x3 - 0.25 * d.x4) / 0.5 | |
| d.controlPoint2y = (d.mid2y - 0.25 * d.y3 - 0.25 * d.y4) / 0.5 | |
| d.arcNumber = position | |
| d.coilNumber = coil | |
| }) | |
| return data | |
| } | |
| function x (angle, radius) { | |
| // change to clockwise | |
| let a = 360 - angle | |
| // start from 12 o'clock | |
| a = a + 180 | |
| return radius * Math.sin(a * radians) | |
| } | |
| function y (angle, radius) { | |
| // change to clockwise | |
| let a = 360 - angle | |
| // start from 12 o'clock | |
| a = a + 180 | |
| return radius * Math.cos(a * radians) | |
| } | |
| function chartWH (r) { | |
| return r * 2 | |
| } | |
| } | |
| chart.radius = function (value) { | |
| if (!arguments.length) return radius | |
| radius = value | |
| return chart | |
| } | |
| chart.holeRadiusProportion = function (value) { | |
| if (!arguments.length) return holeRadiusProportion | |
| holeRadiusProportion = value | |
| return chart | |
| } | |
| chart.arcsPerCoil = function (value) { | |
| if (!arguments.length) return arcsPerCoil | |
| arcsPerCoil = value | |
| return chart | |
| } | |
| chart.coilPadding = function (value) { | |
| if (!arguments.length) return coilPadding | |
| coilPadding = value | |
| return chart | |
| } | |
| chart.arcLabel = function (value) { | |
| if (!arguments.length) return arcLabel | |
| arcLabel = value | |
| return chart | |
| } | |
| chart.coilLabel = function (value) { | |
| if (!arguments.length) return coilLabel | |
| coilLabel = value | |
| return chart | |
| } | |
| return chart | |
| } |
| car_type | export_production | date | value | |
|---|---|---|---|---|
| Passenger cars | Production | 1/01/2008 | 475197 | |
| Passenger cars | Production | 1/02/2008 | 531397 | |
| Passenger cars | Production | 1/03/2008 | 492336 | |
| Passenger cars | Production | 1/04/2008 | 579869 | |
| Passenger cars | Production | 1/05/2008 | 444537 | |
| Passenger cars | Production | 1/06/2008 | 516609 | |
| Passenger cars | Production | 1/07/2008 | 434801 | |
| Passenger cars | Production | 1/08/2008 | 340288 | |
| Passenger cars | Production | 1/09/2008 | 535893 | |
| Passenger cars | Production | 1/10/2008 | 448565 | |
| Passenger cars | Production | 1/11/2008 | 452590 | |
| Passenger cars | Production | 1/12/2008 | 279948 | |
| Passenger cars | Production | 1/01/2009 | 310113 | |
| Passenger cars | Production | 1/02/2009 | 297921 | |
| Passenger cars | Production | 1/03/2009 | 435693 | |
| Passenger cars | Production | 1/04/2009 | 373308 | |
| Passenger cars | Production | 1/05/2009 | 427239 | |
| Passenger cars | Production | 1/06/2009 | 478133 | |
| Passenger cars | Production | 1/07/2009 | 410321 | |
| Passenger cars | Production | 1/08/2009 | 325060 | |
| Passenger cars | Production | 1/09/2009 | 553205 | |
| Passenger cars | Production | 1/10/2009 | 508450 | |
| Passenger cars | Production | 1/11/2009 | 493851 | |
| Passenger cars | Production | 1/12/2009 | 351229 | |
| Passenger cars | Production | 1/01/2010 | 376937 | |
| Passenger cars | Production | 1/02/2010 | 450355 | |
| Passenger cars | Production | 1/03/2010 | 560055 | |
| Passenger cars | Production | 1/04/2010 | 464591 | |
| Passenger cars | Production | 1/05/2010 | 470130 | |
| Passenger cars | Production | 1/06/2010 | 528820 | |
| Passenger cars | Production | 1/07/2010 | 392553 | |
| Passenger cars | Production | 1/08/2010 | 335748 | |
| Passenger cars | Production | 1/09/2010 | 536468 | |
| Passenger cars | Production | 1/10/2010 | 502507 | |
| Passenger cars | Production | 1/11/2010 | 519765 | |
| Passenger cars | Production | 1/12/2010 | 414480 | |
| Passenger cars | Production | 1/01/2011 | 398078 | |
| Passenger cars | Production | 1/02/2011 | 519342 | |
| Passenger cars | Production | 1/03/2011 | 583399 | |
| Passenger cars | Production | 1/04/2011 | 470463 | |
| Passenger cars | Production | 1/05/2011 | 563015 | |
| Passenger cars | Production | 1/06/2011 | 456548 | |
| Passenger cars | Production | 1/07/2011 | 462005 | |
| Passenger cars | Production | 1/08/2011 | 396706 | |
| Passenger cars | Production | 1/09/2011 | 562504 | |
| Passenger cars | Production | 1/10/2011 | 502897 | |
| Passenger cars | Production | 1/11/2011 | 546272 | |
| Passenger cars | Production | 1/12/2011 | 410689 | |
| Passenger cars | Production | 1/01/2012 | 443510 | |
| Passenger cars | Production | 1/02/2012 | 508798 | |
| Passenger cars | Production | 1/03/2012 | 546169 | |
| Passenger cars | Production | 1/04/2012 | 426321 | |
| Passenger cars | Production | 1/05/2012 | 448045 | |
| Passenger cars | Production | 1/06/2012 | 463839 | |
| Passenger cars | Production | 1/07/2012 | 460830 | |
| Passenger cars | Production | 1/08/2012 | 363254 | |
| Passenger cars | Production | 1/09/2012 | 450072 | |
| Passenger cars | Production | 1/10/2012 | 454716 | |
| Passenger cars | Production | 1/11/2012 | 482406 | |
| Passenger cars | Production | 1/12/2012 | 340499 | |
| Passenger cars | Production | 1/01/2013 | 397634 | |
| Passenger cars | Production | 1/02/2013 | 459180 | |
| Passenger cars | Production | 1/03/2013 | 474467 | |
| Passenger cars | Production | 1/04/2013 | 502411 | |
| Passenger cars | Production | 1/05/2013 | 428684 | |
| Passenger cars | Production | 1/06/2013 | 475779 | |
| Passenger cars | Production | 1/07/2013 | 444960 | |
| Passenger cars | Production | 1/08/2013 | 394991 | |
| Passenger cars | Production | 1/09/2013 | 514468 | |
| Passenger cars | Production | 1/10/2013 | 456669 | |
| Passenger cars | Production | 1/11/2013 | 537383 | |
| Passenger cars | Production | 1/12/2013 | 353278 | |
| Passenger cars | Production | 1/01/2014 | 442430 | |
| Passenger cars | Production | 1/02/2014 | 513041 | |
| Passenger cars | Production | 1/03/2014 | 522428 | |
| Passenger cars | Production | 1/04/2014 | 494416 | |
| Passenger cars | Production | 1/05/2014 | 482483 | |
| Passenger cars | Production | 1/06/2014 | 456069 | |
| Passenger cars | Production | 1/07/2014 | 535001 | |
| Passenger cars | Production | 1/08/2014 | 272744 | |
| Passenger cars | Production | 1/09/2014 | 524072 | |
| Passenger cars | Production | 1/10/2014 | 478999 | |
| Passenger cars | Production | 1/11/2014 | 515340 | |
| Passenger cars | Production | 1/12/2014 | 367003 | |
| Passenger cars | Production | 1/01/2015 | 421510 | |
| Passenger cars | Production | 1/02/2015 | 501118 | |
| Passenger cars | Production | 1/03/2015 | 556049 | |
| Passenger cars | Production | 1/04/2015 | 479631 | |
| Passenger cars | Production | 1/05/2015 | 447925 | |
| Passenger cars | Production | 1/06/2015 | 513315 | |
| Passenger cars | Production | 1/07/2015 | 533264 | |
| Passenger cars | Production | 1/08/2015 | 338834 | |
| Passenger cars | Production | 1/09/2015 | 538149 | |
| Passenger cars | Production | 1/10/2015 | 529856 | |
| Passenger cars | Production | 1/11/2015 | 518512 | |
| Passenger cars | Production | 1/12/2015 | 329975 | |
| Passenger cars | Production | 1/01/2016 | 416983 | |
| Passenger cars | Production | 1/02/2016 | 529061 | |
| Passenger cars | Production | 1/03/2016 | 523210 | |
| Passenger cars | Production | 1/04/2016 | 550158 | |
| Passenger cars | Production | 1/05/2016 | 445393 | |
| Passenger cars | Production | 1/06/2016 | 563546 | |
| Passenger cars | Production | 1/07/2016 | 410630 | |
| Passenger cars | Production | 1/08/2016 | 410256 | |
| Passenger cars | Production | 1/09/2016 | 529463 | |
| Passenger cars | Production | 1/10/2016 | 464434 | |
| Passenger cars | Production | 1/11/2016 | 535180 | |
| Passenger cars | Production | 1/12/2016 | 368494 | |
| Trucks | Production | 1/01/2008 | 25412 | |
| Trucks | Production | 1/02/2008 | 28381 | |
| Trucks | Production | 1/03/2008 | 27083 | |
| Trucks | Production | 1/04/2008 | 32175 | |
| Trucks | Production | 1/05/2008 | 25918 | |
| Trucks | Production | 1/06/2008 | 28591 | |
| Trucks | Production | 1/07/2008 | 25323 | |
| Trucks | Production | 1/08/2008 | 24331 | |
| Trucks | Production | 1/09/2008 | 29329 | |
| Trucks | Production | 1/10/2008 | 24756 | |
| Trucks | Production | 1/11/2008 | 18562 | |
| Trucks | Production | 1/12/2008 | 11065 | |
| Trucks | Production | 1/01/2009 | 12277 | |
| Trucks | Production | 1/02/2009 | 9334 | |
| Trucks | Production | 1/03/2009 | 13133 | |
| Trucks | Production | 1/04/2009 | 10349 | |
| Trucks | Production | 1/05/2009 | 12879 | |
| Trucks | Production | 1/06/2009 | 14238 | |
| Trucks | Production | 1/07/2009 | 12826 | |
| Trucks | Production | 1/08/2009 | 12165 | |
| Trucks | Production | 1/09/2009 | 18601 | |
| Trucks | Production | 1/10/2009 | 20106 | |
| Trucks | Production | 1/11/2009 | 17875 | |
| Trucks | Production | 1/12/2009 | 13845 | |
| Trucks | Production | 1/01/2010 | 14349 | |
| Trucks | Production | 1/02/2010 | 17655 | |
| Trucks | Production | 1/03/2010 | 20594 | |
| Trucks | Production | 1/04/2010 | 19105 | |
| Trucks | Production | 1/05/2010 | 18591 | |
| Trucks | Production | 1/06/2010 | 21773 | |
| Trucks | Production | 1/07/2010 | 19710 | |
| Trucks | Production | 1/08/2010 | 10444 | |
| Trucks | Production | 1/09/2010 | 23025 | |
| Trucks | Production | 1/10/2010 | 20866 | |
| Trucks | Production | 1/11/2010 | 27080 | |
| Trucks | Production | 1/12/2010 | 17494 | |
| Trucks | Production | 1/01/2011 | 19977 | |
| Trucks | Production | 1/02/2011 | 21555 | |
| Trucks | Production | 1/03/2011 | 23891 | |
| Trucks | Production | 1/04/2011 | 21584 | |
| Trucks | Production | 1/05/2011 | 25242 | |
| Trucks | Production | 1/06/2011 | 19651 | |
| Trucks | Production | 1/07/2011 | 20441 | |
| Trucks | Production | 1/08/2011 | 19684 | |
| Trucks | Production | 1/09/2011 | 27674 | |
| Trucks | Production | 1/10/2011 | 24456 | |
| Trucks | Production | 1/11/2011 | 29416 | |
| Trucks | Production | 1/12/2011 | 21459 | |
| Trucks | Production | 1/01/2012 | 21618 | |
| Trucks | Production | 1/02/2012 | 21703 | |
| Trucks | Production | 1/03/2012 | 24872 | |
| Trucks | Production | 1/04/2012 | 17889 | |
| Trucks | Production | 1/05/2012 | 22248 | |
| Trucks | Production | 1/06/2012 | 23178 | |
| Trucks | Production | 1/07/2012 | 20078 | |
| Trucks | Production | 1/08/2012 | 19069 | |
| Trucks | Production | 1/09/2012 | 25366 | |
| Trucks | Production | 1/10/2012 | 24434 | |
| Trucks | Production | 1/11/2012 | 24850 | |
| Trucks | Production | 1/12/2012 | 15496 | |
| Trucks | Production | 1/01/2013 | 19743 | |
| Trucks | Production | 1/02/2013 | 21021 | |
| Trucks | Production | 1/03/2013 | 24014 | |
| Trucks | Production | 1/04/2013 | 25375 | |
| Trucks | Production | 1/05/2013 | 23212 | |
| Trucks | Production | 1/06/2013 | 26161 | |
| Trucks | Production | 1/07/2013 | 20857 | |
| Trucks | Production | 1/08/2013 | 21898 | |
| Trucks | Production | 1/09/2013 | 25196 | |
| Trucks | Production | 1/10/2013 | 23137 | |
| Trucks | Production | 1/11/2013 | 29256 | |
| Trucks | Production | 1/12/2013 | 18448 | |
| Trucks | Production | 1/01/2014 | 25329 | |
| Trucks | Production | 1/02/2014 | 23337 | |
| Trucks | Production | 1/03/2014 | 24855 | |
| Trucks | Production | 1/04/2014 | 27804 | |
| Trucks | Production | 1/05/2014 | 27494 | |
| Trucks | Production | 1/06/2014 | 28063 | |
| Trucks | Production | 1/07/2014 | 25655 | |
| Trucks | Production | 1/08/2014 | 20743 | |
| Trucks | Production | 1/09/2014 | 29240 | |
| Trucks | Production | 1/10/2014 | 26320 | |
| Trucks | Production | 1/11/2014 | 25247 | |
| Trucks | Production | 1/12/2014 | 19435 | |
| Trucks | Production | 1/01/2015 | 27071 | |
| Trucks | Production | 1/02/2015 | 26000 | |
| Trucks | Production | 1/03/2015 | 30193 | |
| Trucks | Production | 1/04/2015 | 27932 | |
| Trucks | Production | 1/05/2015 | 24309 | |
| Trucks | Production | 1/06/2015 | 29727 | |
| Trucks | Production | 1/07/2015 | 22937 | |
| Trucks | Production | 1/08/2015 | 19059 | |
| Trucks | Production | 1/09/2015 | 31125 | |
| Trucks | Production | 1/10/2015 | 32696 | |
| Trucks | Production | 1/11/2015 | 31496 | |
| Trucks | Production | 1/12/2015 | 22681 | |
| Trucks | Production | 1/01/2016 | 24202 | |
| Trucks | Production | 1/02/2016 | 28822 | |
| Trucks | Production | 1/03/2016 | 27792 | |
| Trucks | Production | 1/04/2016 | 28587 | |
| Trucks | Production | 1/05/2016 | 26259 | |
| Trucks | Production | 1/06/2016 | 29962 | |
| Trucks | Production | 1/07/2016 | 23481 | |
| Trucks | Production | 1/08/2016 | 16152 | |
| Trucks | Production | 1/09/2016 | 30938 | |
| Trucks | Production | 1/10/2016 | 27109 | |
| Trucks | Production | 1/11/2016 | 28935 | |
| Trucks | Production | 1/12/2016 | 23515 |
| <head> | |
| <meta charset="utf-8"> | |
| <script src="https://d3js.org/d3.v4.min.js"></script> | |
| <script src="https://d3js.org/d3-scale-chromatic.v0.3.min.js"></script> | |
| <script src="d3-spiral-heatmap.js"></script> | |
| <link href="https://fonts.googleapis.com/css?family=Catamaran" rel="stylesheet"> | |
| <style> | |
| body { | |
| font-family: 'Catamaran', sans-serif; | |
| margin: 20px; | |
| top: 20px; | |
| right: 20px; | |
| bottom: 20px; | |
| left: 20px; | |
| } | |
| line { | |
| stroke: #E9E9E9; | |
| } | |
| .coil-label { | |
| fill: #000; | |
| font-size: 12px; | |
| } | |
| .arc path { | |
| stroke: #FFF; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <h1>Spiral heatmap</h1> | |
| <p>An example of a resuable spiral heatmap.</p> | |
| <div id="charts"></div> | |
| <div id="legend"></div> | |
| <script> | |
| //SVG dimensions | |
| const chartWidth = 500 | |
| const chartHeight = chartWidth | |
| const chartRadius = chartWidth / 2 | |
| const margin = { "top": 40, "bottom": 40, "left": 40, "right": 40 } | |
| let dateParse = d3.timeParse("%d/%m/%Y") | |
| let yearFormat = d3.timeFormat("%Y") | |
| let monthFormat = d3.timeFormat("%b"); | |
| //Colour scale | |
| var colour = d3.scaleSequential(d3.interpolateRdYlGn) | |
| //Load the data, nest, sort and draw charts | |
| d3.csv("data.csv", convertTextToNumbers, function (error, data) { | |
| if (error) { throw error; }; | |
| //ENSURE THE DATA IS SORTED CORRECTLY, IN THIS CASE BY YEAR AND MONTH | |
| //THE SPIRAL WILL START IN THE MIDDLE AND WORK OUTWARDS | |
| var nestedData = d3.nest() | |
| .key(function (d) { return d.car_type; }) | |
| .sortValues(function (a, b) { return a.date - b.date; }) | |
| .entries(data); | |
| nestedData.forEach(function (chartData) { | |
| colour.domain(d3.extent(chartData.values, function (d) { return d.value; })); | |
| //set the options for the sprial heatmap | |
| let heatmap = spiralHeatmap() | |
| .radius(chartRadius) | |
| .holeRadiusProportion(0.2) | |
| .arcsPerCoil(12) | |
| .coilPadding(0.1) | |
| .arcLabel("month") | |
| .coilLabel("year") | |
| //CREATE SVG AND A G PLACED IN THE CENTRE OF THE SVG | |
| const div = d3.select("#charts").append("div") | |
| div.append("p") | |
| .text(chartData.key) | |
| const svg = div.append("svg") | |
| .attr("width", chartWidth + margin.left + margin.right) | |
| .attr("height", chartHeight + margin.top + margin.bottom); | |
| const g = svg.append("g") | |
| .attr("transform", "translate(" | |
| + (margin.left + chartRadius) | |
| + "," | |
| + (margin.top + chartRadius) + ")"); | |
| g.datum(chartData.values) | |
| .call(heatmap); | |
| g.selectAll(".arc").selectAll("path") | |
| .style("fill", function (d) { return colour(d.value); }) | |
| }) | |
| }) | |
| function convertTextToNumbers(d) { | |
| d.value = +d.value; | |
| d.date = dateParse(d.date); | |
| d.year = yearFormat(d.date); | |
| d.month = monthFormat(d.date); | |
| return d; | |
| }; | |
| </script> | |
| </body> |