var terrorData; var colorScale = []; var begDate = 1970; var endDate = 1980; var currentRange = [moment(begDate, "YYYY"), moment(endDate, "YYYY")]; var currDecade = 0; var currAccessor = currentRange[0].year().toString().substring(2) + 's' var pie; var arc; var colorScale = ['#433a3f', '#3d5a6c', '#72a98f', '#8de969', '#cbef43', '#141414', '#141414']; var width = 1000; var height = 700; fireUp() function fireUp() { var data = `[{"70s":[{"Explosives":1284},{"Firearms":299},{"Fire or Firebomb":246},{"Unknown":291},{"Chemical Agent":12},{"Knives & sharp objects":12},{"Remote-detonated explosive":4}]},{"80s":[{"Unknown":560},{"Explosives":2035},{"Firearms":474},{"Knives & sharp objects":29},{"Fire or Firebomb":270},{"Chemical Agent":9},{"Remote-detonated explosive":29}]},{"90s":[{"Firearms":716},{"Fire or Firebomb":699},{"Explosives":2339},{"Unknown":754},{"Knives & sharp objects":93},{"Chemical Agent":5},{"Remote-detonated explosive":99}]},{"00s":[{"Explosives":14717},{"Fire or Firebomb":1537},{"Remote-detonated explosive":1461},{"Firearms":9717},{"Unknown":1896},{"Knives & sharp objects":283},{"Chemical Agent":33},{"Biological Agent":13}]}]` terrorData = JSON.parse(data); var margin = { left : 20, right : 20, bottom : 20, top : 0 }; var radius = (Math.min(width, height / 2)) - 100; var horizontalMargin = margin.right + margin.left; var verticalMargin = margin.bottom + margin.top; var svg = d3.select('body').append('svg') .attr('id', 'mainSVG') .attr('width', width + horizontalMargin) .attr('height', height + verticalMargin) var transformGroup = svg.append('g') .attr('id', 'transformGroup') .attr('transform', 'translate (' + width / 2 + ',' + height / 2 + ')') //Tally incidents by weapon var weaponsTallyTable = terrorData[currDecade][[currAccessor]]; //Make a dummy data for big cirlce var reduced = []; reduced.push(weaponsTallyTable[0]) //'free' obj weaponsTallyObj = []; arc = d3.arc() .innerRadius(radius - 70) .outerRadius(radius) var fullArc = d3.arc() .innerRadius(radius - 70) .outerRadius(radius) pie = d3.pie() .sort(null) .value(function (d) { return (Object.values(d)[0]); }) var startLoc = /M(.*?)A/; var gSpot = transformGroup.selectAll('fullArc') .data(pie(reduced)) .enter() .append('g') .attr('id', 'fullArc') gSpot.append('path') .attr('d', fullArc) .attr('fill', 'transparent') .attr('id', 'transparentArc') drawArcs(weaponsTallyTable) /* Event listeners - update beg/end date - refilter data - redraw map */ d3.select("#prevButton") .on('click', function () { if (begDate != 1970) { begDate -= 10; endDate -= 10; currDecade -= 1; currentRange = [moment(begDate, "YYYY"), moment(endDate, "YYYY")]; currAccessor = currentRange[0].year().toString().substring(2) + 's' weaponsTallyTable = terrorData[currDecade][[currAccessor]]; drawArcs(weaponsTallyTable) } }) d3.select("#nextButton") .on('click', function () { if (endDate != 2010) { begDate += 10; endDate += 10; currDecade += 1; currentRange = [moment(begDate, "YYYY"), moment(endDate, "YYYY")]; currAccessor = currentRange[0].year().toString().substring(2) + 's' weaponsTallyTable = terrorData[currDecade][[currAccessor]]; drawArcs(weaponsTallyTable) } }) } function drawArcs(weaponsTallyTable) { //Genuine Pie /***********************************************************************/ var svg = d3.select('#transformGroup') var gSpot = d3.select('#fullArc') /*repeal and replace style*/ svg.selectAll('.piArcs').remove() svg.selectAll('.pieslices').remove() svg.selectAll('.textPaths').remove() var g = svg.selectAll('.arc') .data(pie(weaponsTallyTable)) .enter() .append('g') .attr('class', 'piArcs') g.append('text') .attr('class', 'decadeLabel') .text(function (d, i) { if (i === 0) return (moment(currentRange[0]).year().toString().substring(2) + 's') }) .attr('fill', 'white') .attr('stroke', 'black') .attr('stroke-width', '2x') .attr('font-size', '90px') .attr('text-anchor', 'middle') g.append('path') .attr('d', arc) .attr('class', 'pieslices') .attr('id', function (d, i) { return ('pieChunks_' + i) }) .attr('fill', function (d, i) { return (colorScale[i]); }) .attr('stroke', 'black'); //Update Text Position /***********************************************************************/ var arcStartPositions = []; var startLoc = /([^,]*,[^,]*,[^,]*,[^,]*,[^,]*)/ middleLoc = /A(.*?)0 0 1/, endLoc = /0 0 0 (.*?)$/, everythingAfterA = /([^,]*,[^,]*,[^,]*,[^,]*,[^,]*)(.*)/ var fullPathString = gSpot.selectAll('path').attr('d') everythingAfterA = everythingAfterA.exec(fullPathString); //Collect the start positions of each arc g.selectAll('path') .each( function () { arcStartPositions.push(startLoc.exec(d3.select(this).attr('d'))) }) //Make new paths ('d')s combing the start position of the arcs, with the angle //data of the full arc var newPaths = []; arcStartPositions.forEach(function (pos, i) { //the arc's sweep flag has to be set to the opposite of the slice's var s = pos[1]; var n = Number(!Number(s.slice(-1))); s = (s.slice(0, -1) + n) + everythingAfterA[2]; var res = s.split(','); if ((res[4] == "1" && Math.abs(Number(res[0].substr(1)) > 180)) || (i === 1 && begDate === 2000)) { //extra special corner case res[4] = "0"; } s = res.join() newPaths.push(s) }) // generate new paths var gTextPaths = svg.selectAll('textPaths') .data(pie(weaponsTallyTable)) .enter() .append('g') .attr('class', 'textPaths') gTextPaths.append('path') .attr('d', function (d, i) { return (newPaths[i]) }) .attr('id', function (d, i) { return ('textChunks_' + i) }) .attr('fill', 'transparent') .attr('stroke', 'black'); gSpot.selectAll('text') .data(weaponsTallyTable) .enter() .append('text') .append('textPath') .attr('xlink:href', function (d, i) { return ('#textChunks_'+ i); }) .text(function (d, i) { return(Object.keys(d)[0]) }) var labels = gSpot.selectAll('text') .attr('class', 'labels') relax(); labels.each(function (d, i) { var l = d3.select(this) l.attr('dy', function (d, i) { var s = +l.attr('y') > 0 ? -1 : 1; return (-40 + s * 80 * l.attr('y')) }) }) } function relax() { var alpha = 0.05; var spacing = 9; var again = false; labels = d3.selectAll('.labels') labels.each(function (d, i) { var a = this; var da = d3.select(a); var y1 = da.attr('dy'); labels.each(function (d, j) { var b = this; if (a == b) return ; var db = d3.select(b); var y2 = db.attr('dy'); var deltaY = y1 - y2; // console.log(deltaY) if (Math.abs(deltaY) > spacing) return ; again = true; var sign = deltaY > 0 ? 1 : -1; var adjust = sign * alpha; da.attr('dy', +y1 + adjust); db.attr('dy', +y2 - adjust); if (again) setTimeout(relax, 100) }) }) }