Block-a-Day #11. Clicking on a state zooms the map to that state and shows county-level data.
Data Sources: BLS.
What I Learned: Trying out the new projection.fitExtent(). In the process, learned about adaptive resampling, which can break interpolation by changing the number of coordinates in a path at different zoom levels. Fixed the resulting bug by setting projection.precision() to 0, effectively turning off resampling.
What I'd Do With More Time: Finding a way to include Alaska and Hawaii is an obvious next step (albersUSA didn't immediately work with this technique). Could probably use more detailed county shapefiles.
Just what it sounds like. For fifteen days, I will make a D3.js v4 block every single day. Rules:
forked from cmgiven's block: Zoomable Choropleth
forked from anonymous's block: Zoomable Choropleth
forked from anonymous's block: Zoomable Choropleth D3 V3
forked from ejfox's block: Zoomable Choropleth D3 V3 - Queue removed
forked from ejfox's block: Zoomable Choropleth D3 V3 - Queue removed
forked from ejfox's block: Zoomable Choropleth D3 V3 - Queue removed
forked from ejfox's block: Zoomable Choropleth D3 V3 - Queue removed
forked from ejfox's block: Zoomable Choropleth D3 V3 - Queue removed
forked from ejfox's block: Zoomable Choropleth D3 V3 - Queue removed
forked from ejfox's block: Zoomable Choropleth D3 V3 - Queue removed
xxxxxxxxxx
<meta charset="utf-8">
<style>
body { background: transparent; }
path { stroke: #333; stroke-width: 1; }
path:hover { stroke-width: 3; }
#console {
font-size: 11px;
font-family: "Courier", "fixed-width";
width: 400px;
height: 150px;
position: fixed;
top: 0;
left: 0;
padding: 4px;
overflow: auto;
background-color: white;
border: 2px solid black;
}
#console p {
margin-top: 1px;
margin-bottom: 0
}
svg {
border: 5px solid red;
}
path {
stroke: white;
}
</style>
<body>
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<!-- <script src="https://d3js.org/queue.v1.min.js"></script> -->
<script src="//d3js.org/topojson.v1.min.js"></script>
<div id="console"></div>
<svg id="zoomy-map" width="400" height="300"></svg>
<script>
d3.select('#console').append('p').text('v0.2')
var width = 960
var height = 600
var padding = 20
var allData = []
var color = d3.scale.threshold()
.domain([0.028, 0.038, 0.048, 0.058, 0.068, 0.078])
.range(['#4d9221', '#a1d76a', '#e6f5d0', '#f7f7f7', '#fde0ef', '#e9a3c9', '#c51b7d'])
var projection = d3.geo.albers()
.precision(0)
.scale(height * 2).translate([width / 2, height / 2])
var path = d3.geo.path().projection(projection)
var svg = d3.select('#zoomy-map')
//.append('svg')
.attr('width', width)
.attr('height', height)
d3.select('#console').append('p').text('width '+width)
d3.select('#console').append('p').text('height '+height)
/*
queue()
.defer(d3.csv, 'data.csv', function (d) {
return {
id: +(d.state + d.county),
state: d.state,
county: d.county,
unemployment: +d.unemployment
}
})
.defer(d3.json, 'us-states.json')
.defer(d3.json, 'us-counties.json')
.awaitAll(initialize)
*/
d3.csv('data.csv', function(error, data){
d3.select('#console').append('p').text('data.csv loaded')
allData.push(data)
d3.json('us-states.json', function(error, data){
d3.select('#console').append('p').text('us-states.json loaded')
allData.push(data)
d3.json('us-counties.json', function(error, data){
d3.select('#console').append('p').text('us-counties.json loaded')
allData.push(data)
allData[0].forEach(function(d,i){
if(i<10){ console.log('foreach d', d)}
allData[0][i] = {
'id': +(d.state + d.county),
'county': d.county,
'state': d.state,
'unemployment': +d.unemployment
}
})
d3.select('#console').append('p').text('allData[0] length: '+allData[0].length)
d3.select('#console').append('p').text('allData[1] length: '+allData[1].arcs.length)
d3.select('#console').append('p').text('allData[2] length: '+allData[2].arcs.length)
console.log('======> alldata', allData)
initialize({},allData)
})
})
})
function initialize(error, results) {
//if (error) { throw error }
svg.append('rect')
.attr('width', 100)
.attr('height', 100)
.attr('fill','red')
.attr('transform', 'translate(200,200)')
d3.select('#console').append('p').text('running initialize()')
var data = results[0]
var states = topojson.feature(results[1], results[1].objects.states).features
var counties = topojson.feature(results[2], results[2].objects.counties).features
console.log('states -->', states)
states.forEach(function (f,i) {
if(i<5){
//d3.select('#console').append('p').text('state data '+JSON.stringify(f))
//console.log('--> f ',f, 'f.id', f.id)
//console.log('--> data find', data.find(function (d,i) {
//if(i<10){ console.log('--> find d', d)}
//return d.id === f.id
//}))
}
f.properties = data.find(function (d) { return d.id === f.id })
})
console.log('states after foreach ==>', states)
counties.forEach(function (f) {
f.properties = data.find(function (d) { return d.id === f.id }) || {}
})
var statePaths = svg.selectAll('path')
.data(states)
.enter().append('path')
.attr('class', 'state')
.attr('d', path)
.style('fill', function (d) {
//console.log(d)
return color(d.properties.unemployment)
})
.on('click', function (d) { stateZoom(d.id) })
function usZoom() {
alert("US Zoom");
var t = d3.transition().duration(800)
projection.scale(height * 2).translate([width / 2, height / 2])
statePaths.transition(t)
.attr('d', path)
.style('fill', function (d) { return color(d.properties.unemployment) })
svg.selectAll('.county')
.data([])
.exit().transition(t)
.attr('d', path)
.style('opacity', 0)
.remove()
}
function stateZoom(id) {
alert("State Zoom");
var state = states.find(function (d) { return d.id === id })
var stateCounties = counties.filter(function (d) {
return d.id > id && d.id < id + 1000
})
var t = d3.transition().duration(800)
var countyPaths = svg.selectAll('.county')
.data(stateCounties, function (d) { return d.id })
var enterCountyPaths = countyPaths.enter().append('path')
.attr('class', 'county')
.attr('d', path)
.style('fill', function (d) { return color(d.properties.unemployment) })
.style('opacity', 0)
.on('click', function () { usZoom() })
projection.fitExtent(
[[padding, padding], [width - padding, height - padding]],
state
)
statePaths.transition(t)
.attr('d', path)
.style('fill', '#444')
enterCountyPaths.transition(t)
.attr('d', path)
.style('opacity', 1)
countyPaths.exit().transition(t)
.attr('d', path)
.style('opacity', 0)
.remove()
}
}
</script>
</body>
Modified http://d3js.org/queue.v1.min.js to a secure url
https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js
https://d3js.org/queue.v1.min.js
https://d3js.org/topojson.v1.min.js