Last active
December 12, 2015 04:30
-
-
Save GerardoFurtado/b6726833542d1a05a24a to your computer and use it in GitHub Desktop.
Normalize your choropleths
This file contains 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
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<link href='https://fonts.googleapis.com/css?family=Roboto' rel='stylesheet' type='text/css'> | |
<meta charset="utf-8"> | |
<title>Normalize your choropleths</title> | |
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js"></script> | |
<style type="text/css"> | |
body { | |
margin: 0; | |
background-color: white; | |
font-family: 'Roboto', sans-serif; | |
font-weight: 300; | |
} | |
#container { | |
width: 900px; | |
margin-left: auto; | |
margin-right: auto; | |
margin-top: 10px; | |
padding: 5px 50px 10px 50px; | |
background-color: white; | |
box-shadow: 1px 1px 1px 1px #fff; | |
} | |
/* img.displayed { | |
display: block; | |
margin-left: auto; | |
margin-right: auto; | |
}*/ | |
h1 { | |
font-weight: 700; | |
text-align: center; | |
color: #662506; | |
font-size: 60px; | |
margin-bottom: 10px; | |
} | |
h2 { | |
font-weight: 700; | |
color: #4e5a64; | |
font-size: 26px; | |
margin-bottom: 0px; | |
padding-bottom: 0px; | |
} | |
p { | |
font-size: 18px; | |
margin: 15px 0 10px 0; | |
text-align: justify; | |
} | |
img.displayed { | |
display: block; | |
margin-left: auto; | |
margin-right: auto; | |
align: middle; | |
width: 45%; | |
} | |
a { | |
color: #662506; | |
} | |
.button:hover { | |
fill: maroon; | |
stroke: darkslategray; | |
cursor: pointer; | |
} | |
path:hover { | |
stroke-width: 3px; | |
stroke:maroon; | |
} | |
div.tooltip { | |
position: absolute; | |
text-align: center; | |
white-space: normal; | |
padding: 2px; | |
font: 14px sans-serif; | |
background: ivory; | |
border: 1px solid lightgray; | |
border-radius: 4px; | |
pointer-events: none; | |
} | |
svg { | |
background-color: white; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="container"> | |
<h1>Normalize your choropleths!</h1> | |
<br> | |
<p>Many geographic profile maps are really just population maps! Let’s look at an example: the GSP (gross state product, in millions of dollars) of all U.S. states are displayed below, where a pale colour indicates a state with a low GSP and a dark colour a state with a high GSP. Why is the GSP higher in California, Texas, New York and Florida? Here is a hint: Do you know which four states have the highest populations? California, Texas, New York and Florida!</p> | |
<p>This “GSP map” is actually a population density map! You can easily see this by clicking on the square next to "Population". Did you see? Almost no difference! Now, click on the square next to "GSP per capita".</p> | |
<br> | |
<div id="svganchor"></div> | |
<p>Do you see the difference? Now we have a better GSP map, not a population density map. Perhaps the best way to sum it all up is with this joke from <a href="http://xkcd.com">xkcd.com</a>:</p> | |
<br> | |
<img class="displayed" src="https://imgs.xkcd.com/comics/heatmap.png"> | |
<br> | |
<p>PS: I eliminated DC from the map on purpose: there is too much money there, it was an outlier ruining my beautiful colour scale. GSP values for 2009. Source: <a href="https://en.wikipedia.org/wiki/List_of_U.S._states_by_GDP_per_capita">Wikipedia</a> </p> | |
<p>Created by Gerardo Furtado.</p> | |
</div> | |
<script type="text/javascript"> | |
//Width and height | |
var w = 900; | |
var h = 540; | |
//Define map projection | |
var projection = d3.geo.albersUsa() | |
// .center([-97, 35]) | |
.translate([ w/2, (h/2)-20 ]) | |
.scale(1000); | |
//Define path generator | |
var path = d3.geo.path() | |
.projection(projection); | |
//add commas to numbers on tooltips | |
var formatnum = d3.format("0,000"); | |
var color = d3.scale.quantize() | |
.range(['#ffffe5','#fff7bc','#fee391','#fec44f','#fe9929','#ec7014','#cc4c02','#993404','#662506']); | |
var colorscale = ['#ffffe5','#fff7bc','#fee391','#fec44f','#fe9929','#ec7014','#cc4c02','#993404','#662506']; | |
//Tooltips | |
var div = d3.select("#svganchor").append("div") | |
.attr("class", "tooltip") | |
.style("opacity", 0); | |
//Create SVG | |
var svg = d3.select("#svganchor") | |
.append("svg") | |
.attr("width", w) | |
.attr("height", h); | |
//load GSP data | |
d3.csv("usagsp.csv", function(data) { | |
//Set input domain for color scale | |
color.domain([ | |
d3.min(data, function(d) { return +d.totalgsp; }), | |
d3.max(data, function(d) { return +d.totalgsp; }) | |
]); | |
//Load in GeoJSON data | |
d3.json("usa.json", function(json) { | |
//merge GSP and json | |
for (var i = 0; i < data.length; i++) { | |
//Grab state name | |
var datastatename = data[i].state; | |
//Grab GSP and GSP per capita, as well as population | |
var datagsp = +data[i].totalgsp; | |
var datagpc = +data[i].gspcapita; | |
var datapop = +data[i].population; | |
//Find the corresponding state inside the GeoJSON | |
for (var j = 0; j < json.features.length; j++) { | |
//We'll check the official ISO country code | |
var jsonstate = json.features[j].properties.name; | |
if (datastatename == jsonstate) { | |
//Copy the data value into the GeoJSON | |
json.features[j].properties.gsp = datagsp; | |
json.features[j].properties.gpc = datagpc; | |
json.features[j].properties.pop = datapop; | |
//Stop looking through the JSON | |
break; | |
} | |
} | |
} | |
//Bind data and create one path per GeoJSON feature | |
svg.selectAll("path") | |
.data(json.features) | |
.enter() | |
.append("path") | |
.attr("d", path) | |
.attr("stroke", "dimgray") | |
.attr("fill", function(d) { | |
//Get data value | |
var value = d.properties.gsp; | |
if (value) { | |
//If value exists… | |
return color(value); | |
} else { | |
//If value is undefined… | |
return "#ccc"; | |
} | |
}) | |
.on("mouseover", function(d) { | |
div.transition() | |
.duration(300) | |
.style("opacity", .9); | |
div.html("<strong>" + d.properties.name + "</strong> has a GSP of:<br>" + formatnum(d.properties.gsp) + " million of dollars") | |
.style("left", (d3.event.pageX) + "px") | |
.style("top", (d3.event.pageY - 28) + "px"); | |
}) | |
.on("mouseout", function(d) { | |
div.transition() | |
.duration(300) | |
.style("opacity", 0); | |
}); | |
//creating the scale | |
for (var i = 0; i < 9; i++) { | |
svg.append("rect") | |
.attr("y", 520) | |
.attr("x", (i*20)+350) | |
.attr("width", 20) | |
.attr("height", 20) | |
.attr("fill", colorscale[i]) | |
.attr("stroke", "lightgray") | |
.attr("stroke-width", 1) | |
}; | |
svg.append("text") | |
.attr("x", 420) | |
.attr("y", 515) | |
.attr("font-family", "Roboto") | |
.text("Scale"); | |
svg.append("text") | |
.attr("id", "minvalue") | |
.attr("x", 342) | |
.attr("y", 534) | |
.attr("font-family", "Roboto") | |
.attr("font-size", 12) | |
.attr("opacity", 1) | |
.attr("text-anchor", "end") | |
.text(formatnum(d3.min(data, function(d) { return +d.totalgsp; }))); | |
svg.append("text") | |
.attr("id", "maxvalue") | |
.attr("x", 536) | |
.attr("y", 534) | |
.attr("font-family", "Roboto") | |
.attr("font-size", 12) | |
.attr("opacity", 1) | |
.attr("text-anchor", "start") | |
.text(formatnum(d3.max(data, function(d) { return +d.totalgsp; }))); | |
//creating 1st button | |
svg.append("rect") | |
.attr("x", 385) | |
.attr("y", 10) | |
.attr("width", 20) | |
.attr("height", 20) | |
.attr("class", "button") | |
.attr("stroke", "darkgray") | |
.attr("stroke-width", 2) | |
.attr("fill", "#662506") | |
.on("click", function() { | |
color.domain([ | |
d3.min(data, function(d) { return +d.gspcapita; }), | |
d3.max(data, function(d) { return +d.gspcapita; }) | |
]); | |
d3.selectAll("path") | |
.transition() | |
.duration(1000) | |
.attr("fill", function(d) { | |
//Get data value | |
var value = d.properties.gpc; | |
if (value) { | |
//If value exists… | |
return color(value); | |
} else { | |
//If value is undefined… | |
return "#ccc"; | |
} | |
}); | |
d3.selectAll("path") | |
.on("mouseover", function(d) { | |
div.transition() | |
.duration(300) | |
.style("opacity", .9); | |
div.html("<strong>" + d.properties.name + "</strong> has a GSP per capita of:<br>" + formatnum(d.properties.gpc) + " dollars") | |
.style("left", (d3.event.pageX) + "px") | |
.style("top", (d3.event.pageY - 28) + "px"); | |
}) | |
.on("mouseout", function(d) { | |
div.transition() | |
.duration(300) | |
.style("opacity", 0); | |
}); | |
d3.select("#minvalue") | |
.transition() | |
.duration(500) | |
.attr("opacity", 0) | |
.transition() | |
.duration(500) | |
.attr("opacity", 1) | |
.text(formatnum(d3.min(data, function(d) { return +d.gspcapita; }))); | |
d3.select("#maxvalue") | |
.transition() | |
.duration(500) | |
.attr("opacity", 0) | |
.transition() | |
.duration(500) | |
.attr("opacity", 1) | |
.text(formatnum(d3.max(data, function(d) { return +d.gspcapita; }))); | |
}); | |
svg.append("text") | |
.attr("x", 410) | |
.attr("y", 25) | |
.attr("font-family", "Roboto") | |
.text("GSP per capita"); | |
//creating 2nd button | |
svg.append("rect") | |
.attr("x", 560) | |
.attr("y", 10) | |
.attr("width", 20) | |
.attr("height", 20) | |
.attr("class", "button") | |
.attr("stroke", "darkgray") | |
.attr("stroke-width", 2) | |
.attr("fill", "#662506") | |
.on("click", function() { | |
color.domain([ | |
d3.min(data, function(d) { return +d.totalgsp; }), | |
d3.max(data, function(d) { return +d.totalgsp; }) | |
]); | |
d3.selectAll("path") | |
.transition() | |
.duration(1000) | |
.attr("fill", function(d) { | |
//Get data value | |
var value = d.properties.gsp; | |
if (value) { | |
//If value exists… | |
return color(value); | |
} else { | |
//If value is undefined… | |
return "#ccc"; | |
} | |
}); | |
d3.selectAll("path") | |
.on("mouseover", function(d) { | |
div.transition() | |
.duration(300) | |
.style("opacity", .9); | |
div.html("<strong>" + d.properties.name + "</strong> has a GSP of:<br>" + formatnum(d.properties.gsp) + " million of dollars") | |
.style("left", (d3.event.pageX) + "px") | |
.style("top", (d3.event.pageY - 28) + "px"); | |
}) | |
.on("mouseout", function(d) { | |
div.transition() | |
.duration(300) | |
.style("opacity", 0); | |
}); | |
d3.select("#minvalue") | |
.transition() | |
.duration(500) | |
.attr("opacity", 0) | |
.transition() | |
.duration(500) | |
.attr("opacity", 1) | |
.text(formatnum(d3.min(data, function(d) { return +d.totalgsp; }))); | |
d3.select("#maxvalue") | |
.transition() | |
.duration(500) | |
.attr("opacity", 0) | |
.transition() | |
.duration(500) | |
.attr("opacity", 1) | |
.text(formatnum(d3.max(data, function(d) { return +d.totalgsp; }))); | |
svg.selectAll("title") | |
.text( function(d) { | |
return d.properties.name + " has a GSP of " + formatnum(d.properties.gsp) + " millions of dollars"; | |
}); | |
}); | |
svg.append("text") | |
.attr("x", 585) | |
.attr("y", 25) | |
.attr("font-family", "Roboto") | |
.text("Total GSP"); | |
//creating the 3rd button | |
svg.append("rect") | |
.attr("x", 230) | |
.attr("y", 10) | |
.attr("width", 20) | |
.attr("height", 20) | |
.attr("class", "button") | |
.attr("stroke", "darkgray") | |
.attr("stroke-width", 2) | |
.attr("fill", "#662506") | |
.on("click", function() { | |
color.domain([ | |
d3.min(data, function(d) { return +d.population; }), | |
d3.max(data, function(d) { return +d.population; }) | |
]); | |
d3.selectAll("path") | |
.transition() | |
.duration(1000) | |
.attr("fill", function(d) { | |
//Get data value | |
var value = d.properties.pop; | |
if (value) { | |
//If value exists… | |
return color(value); | |
} else { | |
//If value is undefined… | |
return "#ccc"; | |
} | |
}); | |
d3.selectAll("path") | |
.on("mouseover", function(d) { | |
div.transition() | |
.duration(300) | |
.style("opacity", .9); | |
div.html("<strong>" + d.properties.name + "</strong> has a population of:<br>" + formatnum(d.properties.pop) + " people") | |
.style("left", (d3.event.pageX) + "px") | |
.style("top", (d3.event.pageY - 28) + "px"); | |
}) | |
.on("mouseout", function(d) { | |
div.transition() | |
.duration(300) | |
.style("opacity", 0); | |
}); | |
d3.select("#minvalue") | |
.transition() | |
.duration(500) | |
.attr("opacity", 0) | |
.transition() | |
.duration(500) | |
.attr("opacity", 1) | |
.text(formatnum(d3.min(data, function(d) { return +d.population; }))); | |
d3.select("#maxvalue") | |
.transition() | |
.duration(500) | |
.attr("opacity", 0) | |
.transition() | |
.duration(500) | |
.attr("opacity", 1) | |
.text(formatnum(d3.max(data, function(d) { return +d.population; }))); | |
svg.selectAll("title") | |
.text( function(d) { | |
return d.properties.name + " has a population of " + formatnum(d.properties.pop) + " people"; | |
}); | |
}); | |
svg.append("text") | |
.attr("x", 255) | |
.attr("y", 25) | |
.attr("font-family", "Roboto") | |
.text("Population"); | |
}); //End of d3.json() | |
}); //End d3.csv() | |
</script> | |
</body> | |
</html> |
This file contains 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
state | totalgsp | gspcapita | population | |
---|---|---|---|---|
Alabama | 169856 | 31493 | 4849377 | |
Alaska | 45709 | 63264 | 736732 | |
Arizona | 256364 | 34905 | 6731484 | |
Arkansas | 101818 | 30991 | 2966369 | |
California | 1891363 | 45105 | 38802500 | |
Colorado | 252657 | 45450 | 5355866 | |
Connecticut | 227405 | 54814 | 3596677 | |
Delaware | 60588 | 62072 | 935614 | |
Florida | 737038 | 34775 | 19893297 | |
Georgia | 469809 | 36776 | 10097343 | |
Hawaii | 66431 | 42995 | 1419561 | |
Idaho | 54005 | 32133 | 1634464 | |
Illinois | 630398 | 43851 | 12880580 | |
Indiana | 262647 | 35202 | 6596855 | |
Iowa | 142282 | 40141 | 3107126 | |
Kansas | 124921 | 38980 | 2904021 | |
Kentucky | 156553 | 31313 | 4413457 | |
Louisiana | 208377 | 42268 | 4649676 | |
Maine | 51293 | 33672 | 1330089 | |
Maryland | 286797 | 44632 | 5976407 | |
Massachusetts | 365182 | 50285 | 6745408 | |
Michigan | 368401 | 31738 | 9909877 | |
Minnesota | 323912 | 44262 | 5457173 | |
Mississippi | 95905 | 28289 | 2994079 | |
Missouri | 239752 | 35663 | 6063589 | |
Montana | 35954 | 31780 | 1023579 | |
Nebraska | 86439 | 42823 | 1881503 | |
Nevada | 126503 | 40974 | 2839098 | |
New Hampshire | 59400 | 40631 | 1326813 | |
New Jersey | 482967 | 48526 | 8938175 | |
New Mexico | 74801 | 34485 | 2085572 | |
New York | 1093219 | 50452 | 19746227 | |
North Carolina | 496092 | 39390 | 9943964 | |
North Dakota | 31872 | 44359 | 739482 | |
Ohio | 471264 | 35171 | 11594163 | |
Oklahoma | 153778 | 35523 | 3878051 | |
Oregon | 165648 | 43247 | 3970239 | |
Pennsylvania | 554774 | 38105 | 12787209 | |
Rhode Island | 47837 | 40565 | 1055173 | |
South Carolina | 159647 | 30483 | 4832482 | |
South Dakota | 38308 | 42567 | 853175 | |
Tennessee | 244508 | 35189 | 6549352 | |
Texas | 1244695 | 43221 | 26956958 | |
Utah | 112941 | 37770 | 2942902 | |
Vermont | 25438 | 35383 | 626562 | |
Virginia | 408443 | 45891 | 8326289 | |
Washington | 338334 | 45113 | 7061530 | |
West Virginia | 63344 | 28078 | 1850326 | |
Wisconsin | 24437 | 37499 | 5757564 | |
Wyoming | 37544 | 57942 | 563626 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment