Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
<!DOCTYPE html>
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
flex-direction: column;
.state {
stroke-width: 1;
stroke: #ffffff;
div.tooltip {
position: absolute;
text-align: center;
width: 60px;
min-height: 28px;
padding: 8px 12px;
font: 12px sans-serif;
background: lightgray;
border: 0px;
border-radius: 8px;
pointer-events: none;
<script src=""></script>
<script src=""></script>
<script src=""></script>
<script src=""></script>
<h2>Unemployment rates in the United States, 2020</h2>
<div class="home"></div>
Number.prototype.round = function (decimals) {
return Number((Math.round(this + "e" + decimals) + "e-" + decimals));
function selectDivisionNumber(array) {
let arraySize = array.length,
halfArray = Math.round(arraySize / 2);
let newArr = [];
//Take first and last item and push them to the array
newArr.push(array[arraySize - 1]);
//Don't mind the order, they will be sorted later.
//Divide the array in two
let firstHalf = array.slice(0, halfArray);
let firstHalfSelection = firstHalf[Math.round(firstHalf.length / 2)];
let secondHalf = array.slice(halfArray, arraySize);
let secondHalfSelection = secondHalf[Math.round(secondHalf.length / 2)];
return newArr;
let fetchData = async () => {
//I've handily uploaded the data to this site for easy reference.
let url = "";
//'fetch()' returns a promise
let response= await fetch(url);
//'json()' also returns a promise
return response.json();
const width = 900;
const height = 600;
const svg =".home").append("svg")
.attr("width", width)
.attr("height", height);
const projection = d3.geoAlbersUsa()
.translate([width / 2, height / 2]) // translate to center of screen
.scale([1000]); // scale things down so see entire US
const path = d3.geoPath().projection(projection);
fetchData().then(response => {
const tooltip =".home").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
let sampleMap = => {
return Number(item.unemploymentRate);
let domain = selectDivisionNumber(sampleMap).sort();
//To help generate the scale,
const colorScale = d3.scaleLinear()
.range(["#00806D", "#00BC4C", "#00F200", "#85FB44"].reverse());
//This loads our topojson
d3.json("", function (error, uState) {
if (error) throw error;
.merge(_.keyBy(, 'State'))
.attr("d", path)
.style('transition', "all 0.2s ease-in-out")
.attr('class', 'state')
.style('fill', function (d, i) {
let uRate = d.unemploymentRate;
return uRate ? colorScale(uRate) : "#ccc";
.on('mousemove', function (d) {
.style("opacity", .9);"left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY) + "px")
.text(()=> `${d.State}: ${(d.unemploymentRate*100).round(2).toFixed(1)}%`)
.on("mouseover", function (d) {
.style("fill", tinycolor(colorScale(d.unemploymentRate)).darken(15).toString())
.style("cursor", "pointer");
.on("mouseout", function (d, i) {"fill", function () {
let uRate = d.unemploymentRate;
return uRate ? colorScale(uRate) : "#ccc";
.style("opacity", 0);
//create a new SVG in the body
const legend =".home").append('svg')
//add it with the '.legend' class
.attr('class', 'legend')
//it should be 14px wide
.attr('width', 148)
//and 148px high
.attr('height', 148)
//then either select all the 'g's inside the svg
//or create placeholders
//Fill the data into our placeholders in reverse order
//This arranges our legend in descending order.
//The 'data' here is the items we entered in the 'domain',
//in this case [min, max]
//Every node in teh data should have a 'g' appended
//the 'g' should have this attribute
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
//Inside every 'legend', insert a rect
//that's 18px wide
.attr("width", 18)
//and 18px high
.attr("height", 18)
//then fill it will the color assigned by the scale
.style("fill", colorScale);
.attr("x", 24)
.attr("y", 9)
.attr("dy", ".35em")
.text(function(d) { return `${(d*100).round(2).toFixed(1)}%`});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.