Skip to content

Instantly share code, notes, and snippets.

@GerardoFurtado
Last active December 12, 2015 04:30
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 GerardoFurtado/b6726833542d1a05a24a to your computer and use it in GitHub Desktop.
Save GerardoFurtado/b6726833542d1a05a24a to your computer and use it in GitHub Desktop.
Normalize your choropleths
<!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>
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.
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