Skip to content

Instantly share code, notes, and snippets.

@ayala-usma
Last active September 27, 2017 06:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ayala-usma/e6f5672e0a7abaf7749415140050e308 to your computer and use it in GitHub Desktop.
Save ayala-usma/e6f5672e0a7abaf7749415140050e308 to your computer and use it in GitHub Desktop.
Brushable Scatterplot/Choropleth
license: mit
height: 350

Block-a-Day #2. A brush interaction on a scatterplot filters a choropleth showing the Y axis variable.

Data Sources: State of Obesity and 2014 ACS.

What I Learned: This is kinda a quickie as I ran into an issue with the planned block, but I had never used D3-brush before.

What I'd Do With More Time: I'd probably add a mouseover to the map so the association between the charts is two-way.

Block-a-Day

Just what it sounds like. For fifteen days, I will make a D3.js v4 block every single day. Rules:

  1. Ideas over implementation. Do something novel, don't sweat the details.
  2. No more than two hours can be spent on coding (give or take).
  3. Every. Single. Day.

Previously

forked from cmgiven's block: Brushable Scatterplot/Choropleth

<!DOCTYPE html>
<meta charset="utf-8">
<style>
div { float: left; }
</style>
<body>
<div id="scatterplot"></div>
<div id="choropleth"></div>
<script src="//d3js.org/d3.v4.min.js"></script>
<script>
d3.queue()
.defer(d3.csv, 'paises.csv', function (d) {
return {
name: d.cntry_name,
obesity: +d.obesity,
income: +d.income
}
})
.defer(d3.json, 'latinamerica.geojson')
.awaitAll(initialize)
var color = d3.scaleThreshold()
.domain([0.24, 0.28, 0.32])
.range(['#fbb4b9', '#f768a1', '#c51b8a', '#7a0177'])
function initialize(error, results) {
if (error) { throw error }
var data = results[0]
var features = results[1].features
var components = [
choropleth(features),
scatterplot(onBrush)
]
function update() {
components.forEach(function (component) { component(data) })
}
function onBrush(x0, x1, y0, y1) {
var clear = x0 === x1 || y0 === y1
data.forEach(function (d) {
d.filtered = clear ? false
: d.income < x0 || d.income > x1 || d.obesity < y0 || d.obesity > y1
})
update()
}
update()
}
function scatterplot(onBrush) {
var margin = { top: 10, right: 15, bottom: 40, left: 75 }
var width = 480 - margin.left - margin.right
var height = 350 - margin.top - margin.bottom
var x = d3.scaleLinear()
.range([0, width])
var y = d3.scaleLinear()
.range([height, 0])
var xAxis = d3.axisBottom()
.scale(x)
.tickFormat(d3.format('$.2s'))
var yAxis = d3.axisLeft()
.scale(y)
.tickFormat(d3.format('.0%'))
var brush = d3.brush()
.extent([[0, 0], [width, height]])
.on('start brush', function () {
var selection = d3.event.selection
var x0 = x.invert(selection[0][0])
var x1 = x.invert(selection[1][0])
var y0 = y.invert(selection[1][1])
var y1 = y.invert(selection[0][1])
onBrush(x0, x1, y0, y1)
})
var svg = d3.select('#scatterplot')
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
var bg = svg.append('g')
var gx = svg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0,' + height + ')')
var gy = svg.append('g')
.attr('class', 'y axis')
gx.append('text')
.attr('x', width)
.attr('y', 35)
.style('text-anchor', 'end')
.style('fill', '#000')
.style('font-weight', 'bold')
.text('Median household income')
gy.append('text')
.attr('transform', 'rotate(-90)')
.attr('x', 0)
.attr('y', -40)
.style('text-anchor', 'end')
.style('fill', '#000')
.style('font-weight', 'bold')
.text('Obesity rate')
svg.append('g')
.attr('class', 'brush')
.call(brush)
return function update(data) {
x.domain(d3.extent(data, function (d) { return d.income })).nice()
y.domain(d3.extent(data, function (d) { return d.obesity })).nice()
gx.call(xAxis)
gy.call(yAxis)
var bgRect = bg.selectAll('rect')
.data(d3.pairs(d3.merge([[y.domain()[0]], color.domain(), [y.domain()[1]]])))
bgRect.exit().remove()
bgRect.enter().append('rect')
.attr('x', 0)
.attr('width', width)
.merge(bgRect)
.attr('y', function (d) { return y(d[1]) })
.attr('height', function (d) { return y(d[0]) - y(d[1]) })
.style('fill', function (d) { return color(d[0]) })
var circle = svg.selectAll('circle')
.data(data, function (d) { return d.name })
circle.exit().remove()
circle.enter().append('circle')
.attr('r', 4)
.style('stroke', '#fff')
.merge(circle)
.attr('cx', function (d) { return x(d.income) })
.attr('cy', function (d) { return y(d.obesity) })
.style('fill', function (d) { return color(d.obesity) })
.style('opacity', function (d) { return d.filtered ? 0.5 : 1 })
.style('stroke-width', function (d) { return d.filtered ? 1 : 2 })
}
}
function choropleth(features) {
var width = 480
var height = 400
var projection = d3.geoMercator()
.scale([width * 0.4])
.translate([width + 60, height / 3])
var path = d3.geoPath().projection(projection)
var svg = d3.select('#choropleth')
.append('svg')
.attr('width', width)
.attr('height', height)
svg.selectAll('path')
.data(features)
.enter()
.append('path')
.attr('d', path)
.style('stroke', '#fff')
.style('stroke-width', 1)
return function update(data) {
svg.selectAll('path')
.data(data, function (d) {return d})
.style('fill', function (d) { return d.filtered ? '#ddd' : color(d.obesity) })
}
}
</script>
</body>
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
cntry_name obesity income
Bolivia 0.289 50068
Brazil 0.359 41262
Chile 0.247 61933
Ecuador 0.213 61303
Peru 0.263 70048
Mexico 0.307 59716
Colombia 0.217 71648
French Guiana 0.262 47463
Suriname 0.305 49321
Guyana 0.221 69592
Guatemala 0.289 47861
Costa Rica 0.293 57444
Paraguay 0.327 49446
Uruguay 0.309 53712
Belize 0.313 52504
El Salvador 0.316 42958
Nicaragua 0.349 44555
Honduras 0.282 49462
Panama 0.296 73971
Venezuela 0.335 42830
Argentina 0.297 71583
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment