Created
October 12, 2018 14:48
-
-
Save sammorrisdesign-zz/b6d9b968d13cac80facaaa47c4ecd736 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
| var d3 = Object.assign( | |
| require('d3-selection'), | |
| require('d3-hierarchy'), | |
| require('d3-timer'), | |
| require('d3-ease'), | |
| require('d3-geo'), | |
| require('d3-request'), | |
| require('d3-scale'), | |
| require('d3-array'), | |
| require('d3-axis') | |
| ) | |
| var topojson = require('topojson'); | |
| var _ = require('underscore'); | |
| var data = require('../../../.data/cleanData.json'); | |
| var map = require('../data/map.json'); | |
| var width; | |
| var height; | |
| var radius, nodePadding, groupPadding; | |
| var ctx; | |
| var svgCtx; | |
| var ease = d3.easeCubicOut; | |
| var timer; | |
| module.exports = { | |
| init: function() { | |
| if (window.$) { | |
| this.readyToInit(); | |
| } else { | |
| setTimeout(function() { this.init() }.bind(this), 50); | |
| } | |
| }, | |
| readyToInit: function() { | |
| this.setupCanvas(); | |
| this.setupSVG(); | |
| this.bindings(); | |
| this.setSizing(); | |
| this.createIgnored(); | |
| this.calculatePositions(); | |
| }, | |
| bindings: function() { | |
| $('.uit-canvas').on('shift', function() { | |
| this.clearLabels(); | |
| this.calculatePositions(); | |
| }.bind(this)); | |
| $('.uit-canvas').on('reset', function() { | |
| $('.uit-canvas__labels').empty(); | |
| this.setupCanvas(); | |
| this.setupSVG(); | |
| this.setSizing(); | |
| this.calculatePositions(); | |
| }.bind(this)); | |
| }, | |
| setupCanvas: function() { | |
| width = $(window).width(); | |
| height = $(window).height(); | |
| $('.uit-canvas canvas').remove(); | |
| var canvas = d3.select('.uit-canvas') | |
| .append('canvas') | |
| .attr('width', width) | |
| .attr('height', height); | |
| ctx = canvas.node().getContext('2d'); | |
| }, | |
| setupSVG: function() { | |
| $('.uit-canvas svg').remove(); | |
| var svg = d3.select('.uit-canvas') | |
| .append('svg') | |
| .attr('width', width) | |
| .attr('height', height); | |
| svgCtx = d3.select('.uit-canvas svg'); | |
| }, | |
| setSizing: function() { | |
| if (width > 768) { | |
| radius = 2.5; | |
| nodePadding = 4; | |
| groupPadding = 40; | |
| } else { | |
| radius = 1.5; | |
| nodePadding = 2; | |
| groupPadding = 25; | |
| } | |
| }, | |
| createIgnored: function() { | |
| for (var i = 0; i < 458; i++) { | |
| data.push({ | |
| ignored: true, | |
| id: data.length + i, | |
| }) | |
| } | |
| }, | |
| calculatePositions: function() { | |
| var sortBy = $('.uit-canvas').attr('data-set'); | |
| for (var i in data) { | |
| if (sortBy === 'default') { | |
| data[i][sortBy] = 'Total cases analyzed'; | |
| } else if (sortBy === 'charges') { | |
| if (data[i].ignored) { | |
| data[i][sortBy] = 'Serious offenses'; | |
| } else { | |
| data[i][sortBy] = 'Low-level immigration offenses'; | |
| } | |
| } else { | |
| if (data[i].ignored) { | |
| data[i][sortBy] = 'Ignored'; | |
| } else if (!data[i][sortBy]) { | |
| data[i][sortBy] = 'Unknown'; | |
| } | |
| } | |
| data[i].value = 1; | |
| data[i].parentId = data[i][sortBy]; | |
| } | |
| if (sortBy === 'location' || sortBy === 'nationality') { | |
| var root = this.mapPack(sortBy); | |
| this.animate(root.nodes); | |
| root.labels.forEach(function(d) { | |
| this.createLabel(d.id, d.value, null, d.x, d.y, 0, sortBy === 'location' || d.id === 'Mexico'); | |
| }.bind(this)); | |
| } else if (sortBy === 'previousDeportation' || sortBy === 'sentence-felony' || sortBy === 'sentence-misdemeanor') { | |
| var root = this.linearPack(sortBy); | |
| this.animate(root.nodes); | |
| this.hideMap(); | |
| root.labels.forEach(function(d) { | |
| this.createLabel(d.id.match(/\(([^)]+)\)/)[1], d.value, null, d.x, d.y, 0, null, true); | |
| }.bind(this)); | |
| } else if (sortBy === 'sentence-average-misdemeanour' || sortBy == 'sentence-average-felony') { | |
| this.barChart(sortBy); | |
| this.animate(); | |
| } else { | |
| var root = this.regularPack(sortBy); | |
| var labels = root.descendants().filter(function(d) { return d.depth === 1 }); | |
| var nodes = root.leaves(); | |
| this.animate(nodes); | |
| this.hideMap(); | |
| labels.forEach(function(d) { | |
| var total = sortBy === 'outcome' ? null : root.leaves().length; | |
| this.createLabel(d.id, d.value, total, d.x, d.y, d.r, d.value > 80); | |
| }.bind(this)); | |
| } | |
| }, | |
| linearPack: function(sortBy) { | |
| var timeline = {}; | |
| if (sortBy === 'previousDeportation') { | |
| timeline = { | |
| '1 (≤1 week)': [], | |
| '2 (1-4 weeks)': [], | |
| '3 (1-6 months)': [], | |
| '4 (6 months - 1 year)': [], | |
| '6 (>1 year)': [] | |
| }; | |
| } else if (sortBy.includes('sentence')) { | |
| timeline = { | |
| '1 (1-2 days)': [], | |
| '2 (3-7 days)': [], | |
| '3 (8-14 days)': [], | |
| '4 (15-30 days)': [], | |
| '5 (1-3 months)': [], | |
| '6 (3-6 months)': [], | |
| '7 (6 months - 1 year)': [], | |
| '8 (>1 year)': [] | |
| } | |
| } | |
| var key = sortBy.includes('sentence') ? 'sentence' : sortBy; | |
| data.forEach(function(dataPoint, i) { | |
| if (key === 'sentence' && sortBy.includes('felony') && dataPoint.sentenced !== 'Felony re-entry' || | |
| key === 'sentence' && sortBy.includes('misdemeanor') && dataPoint.sentenced !== 'Misdemeanor illegal entry') { | |
| return; | |
| } else if (timeline[dataPoint[key]]) { | |
| timeline[dataPoint[key]].push(dataPoint.id); | |
| }; | |
| }); | |
| var bandWidth = (nodePadding * 10) + (radius * 10) + groupPadding; | |
| var groups = Object.keys(timeline); | |
| var totalWidth = bandWidth * groups.length - groupPadding; | |
| // this can be easily replaced without d3 scale band | |
| var x = d3.scaleBand() | |
| .range([(width - totalWidth) / 2, totalWidth + ((width - totalWidth) / 2)]) | |
| .padding(0); | |
| x.domain(groups); | |
| var chartStarts = {}; | |
| for (var time in timeline) { | |
| chartStarts[time] = { | |
| x: x(time), | |
| y: height / 2, | |
| positioned: 0, | |
| row: 0 | |
| } | |
| } | |
| var nodes = []; | |
| data.forEach(function(dataPoint, i) { | |
| if (timeline[dataPoint[key]] && timeline[dataPoint[key]].includes(dataPoint.id)) { | |
| nodes.push({ | |
| id: dataPoint.id, | |
| x: chartStarts[dataPoint[key]].x + (chartStarts[dataPoint[key]].positioned * (nodePadding + radius)), | |
| y: chartStarts[dataPoint[key]].y - (chartStarts[dataPoint[key]].row * (nodePadding + radius)) | |
| }); | |
| chartStarts[dataPoint[key]].positioned++; | |
| if (chartStarts[dataPoint[key]].positioned % 10 === 0) { | |
| chartStarts[dataPoint[key]].row++; | |
| chartStarts[dataPoint[key]].positioned = 0; | |
| } | |
| } else { | |
| nodes.push({ | |
| id: dataPoint.id | |
| }) | |
| } | |
| }); | |
| labels = []; | |
| for (var chart in chartStarts) { | |
| labels.push({ | |
| id: chart, | |
| x: chartStarts[chart].x + (nodePadding * 5) + (radius * 5), | |
| y: chartStarts[chart].y + 50, | |
| value: timeline[chart].length | |
| }) | |
| } | |
| return { | |
| nodes: nodes, | |
| labels: labels | |
| } | |
| }, | |
| mapPack: function(sortBy) { | |
| this.drawMap(sortBy); | |
| var nodes = []; | |
| var scrollTop = $(document).scrollTop(); | |
| var pointPositions = {}; | |
| var pointTarget = sortBy === 'nationality' ? '.country' : '.county'; | |
| var stateValues = {}; | |
| $(pointTarget).each(function(i, county) { | |
| var $county = $(county); | |
| var countyName = $county.data('link'); | |
| pointPositions[countyName] = { | |
| x: $county.position().left + ($county.width() / 2), | |
| y: $county.position().top + ($county.height() / 2) - scrollTop | |
| } | |
| }); | |
| var dataSource = sortBy === 'nationality' ? 'nationality' : 'location'; // is this line needed?? | |
| data.forEach(function(dataPoint, i) { | |
| nodes.push({ | |
| id: dataPoint.id, | |
| x: pointPositions[dataPoint[dataSource]] ? pointPositions[dataPoint[dataSource]].x : width / 2, | |
| y: pointPositions[dataPoint[dataSource]] ? pointPositions[dataPoint[dataSource]].y : -200, | |
| hide: true | |
| }); | |
| if (pointPositions[dataPoint[dataSource]]) { | |
| if (!pointPositions[dataPoint[dataSource]].value) { | |
| pointPositions[dataPoint[dataSource]].value = 1; | |
| } else { | |
| pointPositions[dataPoint[dataSource]].value++; | |
| } | |
| } | |
| if (sortBy === 'location') { | |
| var state = dataPoint.location.split(/[-]+/).pop(); | |
| state = state == 'mexico' ? 'new-mexico' : state; | |
| if (!stateValues[state]) { | |
| stateValues[state] = {}; | |
| stateValues[state].value = 1; | |
| } else { | |
| stateValues[state].value++; | |
| } | |
| } | |
| }); | |
| var labelPositions = []; | |
| var labelTarget = sortBy === 'nationality' ? '.country' : '.state'; | |
| $(labelTarget).each(function(i, label) { | |
| var $label = $(label); | |
| if (sortBy === 'nationality') { | |
| var value = pointPositions[$label.data('link')].value; | |
| } else { | |
| if (stateValues[$label.data('link')]) { | |
| var value = stateValues[$label.data('link')].value; | |
| } | |
| } | |
| if (value) { | |
| labelPositions.push({ | |
| id: $label.data('label'), | |
| value: value, | |
| x: $label.data('label') === 'Brazil' ? $label.position().left + ($label.width() * 0.3) : $label.position().left + ($label.width() / 2), | |
| y: $label.data('label') === 'Brazil' ? $label.position().top - scrollTop + ($label.height() * 0.1) : $label.position().top + ($label.height() / 2) - scrollTop, | |
| }) | |
| }; | |
| }); | |
| this.colourMap(pointPositions); | |
| this.showMap(); | |
| return { | |
| nodes: nodes, | |
| labels: labelPositions | |
| } | |
| }, | |
| drawMap: function(sortBy) { | |
| $('.uit-canvas svg').empty(); | |
| $('.uit-canvas svg').removeClass().addClass(sortBy); | |
| var counties = topojson.feature(map, map.objects.counties); | |
| var states = topojson.feature(map, map.objects.states); | |
| var countries = topojson.feature(map, map.objects.countries); | |
| var rivers = topojson.feature(map, map.objects.river); | |
| var border = topojson.feature(map, map.objects.border); | |
| if (sortBy === 'nationality') { | |
| var cropArea = topojson.feature(map, { | |
| type: "GeometryCollection", | |
| geometries: map.objects.countries.geometries.filter(function(d) { | |
| return d.properties.GEOUNIT != 'Canada' | |
| && d.properties.GEOUNIT != 'United States of America' | |
| && d.properties.GEOUNIT != 'Chile' | |
| && d.properties.GEOUNIT != 'Uruguay' | |
| && d.properties.GEOUNIT != 'Brazil' | |
| && d.properties.GEOUNIT != 'Peru' | |
| && d.properties.GEOUNIT != 'Paraguay' | |
| && d.properties.GEOUNIT != 'Bolivia' | |
| && d.properties.GEOUNIT != 'Argentina'; | |
| }) | |
| }); | |
| var projection = d3.geoMercator().fitExtent([[width * 0.05, 0], [width * 0.95, height]], cropArea); | |
| } else { | |
| var projection = d3.geoMercator().fitExtent([[width * 0.05, 0], [width * 0.95, height]], counties); | |
| } | |
| var path = d3.geoPath().projection(projection); | |
| svgCtx.append('g') | |
| .attr('class', 'countries') | |
| .selectAll('path') | |
| .data(countries.features) | |
| .enter().append('path') | |
| .attr('d', path) | |
| .attr('class', function(d) { return 'country' }) | |
| .attr('data-label', function(d) { | |
| return d.properties.GEOUNIT; | |
| }) | |
| .attr('data-link', function(d) { | |
| return d.properties.GEOUNIT.toLowerCase().replace(/ /g, '-'); | |
| }); | |
| if (sortBy === 'location') { | |
| svgCtx.append('g') | |
| .attr('class', 'states') | |
| .selectAll('path') | |
| .data(states.features) | |
| .enter().append('path') | |
| .attr('d', path) | |
| .attr('class', 'state') | |
| .attr('data-label', function(d) { | |
| return d.properties.NAME; | |
| }) | |
| .attr('data-link', function(d) { | |
| return d.properties.NAME.toLowerCase().replace(/ /g, '-'); | |
| }); | |
| svgCtx.append('g') | |
| .attr('class', 'counties') | |
| .selectAll('path') | |
| .data(counties.features) | |
| .enter().append('path') | |
| .attr('d', path) | |
| .attr('class', 'county') | |
| .attr('data-link', function(d) { | |
| return d.properties.NAME.toLowerCase().replace(/ /g, '-') + '-' + this.getState(d.properties.STATEFP); | |
| }.bind(this)); | |
| svgCtx.append('g') | |
| .attr('class', 'border') | |
| .selectAll('path') | |
| .data(border.features) | |
| .enter().append('path') | |
| .attr('d', path) | |
| .attr('class', 'border-section'); | |
| svgCtx.append('g') | |
| .attr('class', 'rivers') | |
| .selectAll('path') | |
| .data(rivers.features) | |
| .enter().append('path') | |
| .attr('d', path) | |
| .attr('class', 'river'); | |
| var labelData = [ | |
| { | |
| label: 'El Paso', | |
| long: -106.4850, | |
| lat: 31.7619 | |
| }, | |
| { | |
| label: 'Brownsville', | |
| long: -97.4975, | |
| lat: 25.9017, | |
| below: true | |
| }, | |
| { | |
| label: 'Nogales', | |
| long: -110.9381, | |
| lat: 31.3012 | |
| }, | |
| { | |
| label: 'San Diego', | |
| long: -117.1611, | |
| lat: 32.7157 | |
| }, | |
| { | |
| label: 'Tijuana', | |
| long: -117.0382, | |
| lat: 32.5149, | |
| below: true | |
| }, | |
| { | |
| label: 'Ciudad Juarez', | |
| long: -106.4245, | |
| lat: 31.6904, | |
| below: true | |
| } | |
| ] | |
| var labels = svgCtx.append('g') | |
| .attr('class', 'labels') | |
| .selectAll('g') | |
| .data(labelData) | |
| .enter() | |
| .append('g') | |
| .attr('transform', function(d) { return 'translate(' + projection([d.long, d.lat]) + ')' }) | |
| .attr('class', function(d) { return 'label' + (d.below ? ' label--below' : '') }); | |
| labels.append('circle') | |
| .attr('class', 'label__point') | |
| .attr('r', 4); | |
| labels.append('text') | |
| .attr('class', 'label__text') | |
| .text(function(d) { return d.label }); | |
| } | |
| }, | |
| colourMap: function(countiesForMap) { | |
| var dataArray = []; | |
| for (var county in countiesForMap) { | |
| var d = countiesForMap[county]; | |
| if (d.value) { | |
| dataArray.push(d.value); | |
| } | |
| } | |
| var minVal = d3.min(dataArray); | |
| var maxVal = d3.max(dataArray); | |
| var ramp = d3.scaleLinear().domain([minVal, maxVal]).range(['#ffbac8', '#c70000']); | |
| for (var county in countiesForMap) { | |
| var d = countiesForMap[county] | |
| if (d.value) { | |
| $('[data-link=\'' + county + '\']').attr('style', 'fill: ' + ramp(d.value)); | |
| } | |
| } | |
| }, | |
| showMap: function() { | |
| $('.uit-canvas svg').addClass('is-current'); | |
| }, | |
| hideMap: function() { | |
| $('.uit-canvas svg').removeClass('is-current'); | |
| }, | |
| getState: function(stateID) { | |
| switch(stateID) { | |
| case '06': | |
| return 'california' | |
| case '04': | |
| return 'arizona' | |
| case '35': | |
| return 'new-mexico' | |
| case '48': | |
| return 'texas' | |
| } | |
| }, | |
| regularPack: function(sortBy) { | |
| var upperLevels = [{ | |
| id: 'cases', | |
| parentId: null | |
| }]; | |
| var middleLevels = _.map(_.countBy(data, sortBy), function (value, key) { | |
| if (key !== 'Ignored') { | |
| return level = { | |
| id: key, | |
| parentId: 'cases' | |
| } | |
| } | |
| }); | |
| var levels = upperLevels.concat(middleLevels.filter(Boolean)); | |
| levels = levels.concat(data.filter(function(d) { return d.parentId !== 'Ignored' })); | |
| var root = this.packNodes(levels); | |
| return root; | |
| }, | |
| packNodes: function(levels) { | |
| var root = d3.stratify() | |
| (levels) | |
| .sum(function(d) { return d.value; }) | |
| .sort(function(a, b) { return b.value - a.value }); | |
| var pack = d3.pack() | |
| .size([width, height / 10 * 8]) | |
| .radius(function(){ return radius }) | |
| .padding(function(d) { | |
| return d.depth == 1 ? nodePadding : groupPadding; | |
| }); | |
| return pack(root); | |
| }, | |
| animate: function(positionedData = []) { | |
| positionedData.sort(function(a,b){ | |
| return a.id - b.id; | |
| }); | |
| data.forEach(function(dataPoint, i) { | |
| dataPoint.sx = data[i].x || width / 2; | |
| dataPoint.sy = data[i].y || height / 2; | |
| dataPoint.so = data[i].o || 1; | |
| dataPoint.tx = positionedData[i] && positionedData[i].x ? positionedData[i].x : width / 2; | |
| dataPoint.ty = positionedData[i] && positionedData[i].y ? positionedData[i].y : -200; | |
| dataPoint.to = positionedData[i] && positionedData[i].hide ? 0 : 1; | |
| }.bind(this)); | |
| if (timer !== undefined) { | |
| timer.stop(); | |
| } | |
| timer = d3.timer(function(elapsed) { | |
| var t = Math.min(1, ease(elapsed / 800)); | |
| data.forEach(function(dataPoint, i) { | |
| dataPoint.x = dataPoint.sx * (1 - t) + dataPoint.tx * t; | |
| dataPoint.y = dataPoint.sy * (1 - t) + dataPoint.ty * t; | |
| dataPoint.o = dataPoint.so * (1 - t) + dataPoint.to * t; | |
| }); | |
| this.draw(); | |
| if (t === 1) { | |
| timer.stop(); | |
| } | |
| }.bind(this), 0); | |
| }, | |
| draw: function() { | |
| ctx.clearRect(0, 0, width, height); | |
| ctx.save(); | |
| data.forEach(function(d) { | |
| ctx.fillStyle = 'rgba(199, 0, 0, ' + d.o + ')'; | |
| ctx.beginPath(); | |
| ctx.moveTo(d.x + radius, d.y); | |
| ctx.arc(d.x, d.y, radius, 0, 2 * Math.PI); | |
| ctx.fill(); | |
| }.bind(this)); | |
| ctx.restore(); | |
| }, | |
| clearLabels: function() { | |
| $('.uit-canvas__labels').empty(); | |
| }, | |
| createLabel: function(title, value, total, x, y, r, large = false, alwaysStack = false) { | |
| // get y position | |
| var top; | |
| if (title === 'Nicaragua') { | |
| top = y + (height / 100 * 2); | |
| } else if (title === 'Belize' || title === 'Guatemala' || title === 'Honduras' ) { | |
| top = y - (height / 100); | |
| } else if (large || title === 'Dominican Republic' || title === 'El Salvador') { | |
| top = y; | |
| } else if (y > height * 0.55) { | |
| top = y + r + 14 | |
| } else { | |
| top = Math.floor(y - r - 14); | |
| } | |
| // get x position | |
| var left = Math.floor(x); | |
| // get number | |
| var number; | |
| if (!total || value == data.length) { | |
| number = value.toLocaleString(); | |
| } else if (total) { | |
| number = parseFloat((100 / total * value).toFixed(1)) + '%'; | |
| } else { | |
| number = 'XXXX' | |
| } | |
| $('.uit-canvas__labels').append('<h3 class=\'uit-canvas__label' + (alwaysStack ? ' uit-canvas__label--stacked' : ' ') + (large ? ' uit-canvas__label--large' : ' ') + '\' style=\'top: ' + top + 'px; left: ' + left + 'px; \'><span class=\'uit-canvas__label-descriptor\'>' + title + '</span><span class=\'uit-canvas__label-value\'>' + number + '</span></h3>'); | |
| }, | |
| barChart: function(sortBy) { | |
| $('.uit-canvas svg').empty(); | |
| var dataSet = sortBy === 'sentence-average-misdemeanour' ? 'misdemeanor' : 'felony'; | |
| var barData = [ | |
| { | |
| district: 'California Southern', | |
| felony: 60, | |
| misdemeanor: 16 | |
| }, | |
| { | |
| district: 'Arizona', | |
| felony: 60, | |
| misdemeanor: 2 | |
| }, | |
| { | |
| district: 'New Mexico', | |
| felony: 43, | |
| misdemeanor: 8 | |
| }, | |
| { | |
| district: 'Texas Western', | |
| felony: 105, | |
| misdemeanor: 10 | |
| }, | |
| { | |
| district: 'Texas Southern', | |
| felony: 130, | |
| misdemeanor: 3 | |
| } | |
| ]; | |
| var isMobile = width < 620; | |
| var chartWidth = isMobile ? width - 40 : 620; | |
| var chartHeight = isMobile ? 250: 250; | |
| var xOffset = (width - chartWidth) / 2; | |
| var yOffset = (height - chartHeight) / 2; | |
| if (isMobile) { | |
| $('.uit-canvas svg').addClass('is-mobile'); | |
| } else { | |
| $('.uit-canvas svg').removeClass('is-mobile'); | |
| } | |
| var y = d3.scaleBand() | |
| .range([yOffset, yOffset + chartHeight]) | |
| .padding(isMobile ? 0.7 : 0.3); | |
| var x = d3.scaleLinear() | |
| .range([xOffset, xOffset + chartWidth]); | |
| y.domain(barData.map(function(d) { return d.district })); | |
| x.domain([0, dataSet == 'misdemeanor' ? 20 : 160]); | |
| var ticks = 8; | |
| svgCtx.append('g') | |
| .attr('class', 'grid-lines') | |
| .attr('transform', 'translate(0, ' + (yOffset - 12) + ')') | |
| .call(d3.axisTop(x) | |
| .ticks(ticks) | |
| .tickSize(-(chartHeight)) | |
| .tickFormat(function(d) { return d == 0 ? d + ' days' : d}) | |
| ) | |
| .selectAll('.tick text') | |
| .attr('y', 12) | |
| .attr('x', 0) | |
| var graph = svgCtx.append('g') | |
| .attr('transform', 'translate(' + xOffset + ',' + (isMobile ? 12 : 0) + ')'); | |
| var district = graph.selectAll('g.district') | |
| .data(barData) | |
| .enter() | |
| .append('g') | |
| .attr('class', 'district'); | |
| district.append('text') | |
| .attr('y', function(d) { return (isMobile ? -16 : 0) + y(d.district) }) | |
| .attr('x', isMobile ? 0 : -6) | |
| .attr('class', 'district-name') | |
| .text(function(d) { return d.district }); | |
| district.append('text') | |
| .attr('y', function(d) { return y(d.district) }) | |
| .attr('x', function(d) { return x(d[dataSet]) - xOffset }) | |
| .attr('class', 'district-percentage') | |
| .text(function(d) { return d[dataSet] + ' days' }); | |
| district.append('rect') | |
| .attr('y', function(d) { return y(d.district) }) | |
| .attr('x', 0) | |
| .attr('class', 'felony') | |
| .attr('width', function(d) { return x(d[dataSet]) - xOffset }) | |
| .attr('height', y.bandwidth()); | |
| this.showMap(); | |
| } | |
| }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment