Published
Edited
Oct 15, 2021
1 fork
Importers
2 stars
Insert cell
Insert cell
Insert cell
// this is a topojson file which encodes the outline of each state, along with its name
// we *could* attach this an load it via the Observable `FileAttachment` helper, but we happen to know it's URL online so we can
// load it directly
pathsForMap = d3.json("https://s3-us-west-2.amazonaws.com/s.cdpn.io/25240/us-states.json")
Insert cell
Insert cell
Insert cell
Insert cell
stateData = FileAttachment("state-data.csv").csv()
Insert cell
Insert cell
// build a lookup table so we can go from state name to state abbreviation (for the labels on our map)
stateLookup = {
const lookup = {}
for (const item of stateData) {
lookup[item.State] = item.Code
}
return lookup
}
Insert cell
Insert cell
stateLookup['New York']
Insert cell
Insert cell
map = {
// a "projection" lets us visualize the 3d Earth surface in 2d (going from latitude/longitutde to x/y pixel positions)
const projectionTranslator = d3
.geoAlbersUsa() // composite projection of the United States
.translate([width / 2, height / 2]) // make sure to center it
.fitSize([width, height], pathsForMap); // and try to fit it into the space nicely

// setup our project to be able to draw our geographic state outlines (aka paths - a series of points connected by lines)
const pathRenderer = d3.geoPath().projection(projectionTranslator);

// setup a wrapper node to hold our map (with SVG, which knows how to draw paths)
const svg = d3
.select(DOM.element('svg'))
.attr('width', width)
.attr('height', height)
.attr('style', "font-family: 'Lato';")

// draw each state
const map = svg.append("g").attr("id", "map") // setup a container <g> node to hold each state
map.selectAll("path")
.data(pathsForMap.features) // the features contain the name of the state and the path to draw its outline
.join('path') // we'll draw an SVG <path> node for each state in the dataset
.attr("d", d => pathRenderer(d)) // this will call the projection function with each state's path
.style('stroke', 'white') // and style it up a little bit
.style('fill', 'darkblue')

// and now draw a label for each state too
const labels = svg.append("g").attr("id", "labels") // setup a container <g> node to hold all the labels
labels.selectAll("text")
.data(pathsForMap.features) // we're still working with the same geographic data
.join('text') // we want to add a <text> SVG node for each state
.attr('text-anchor', 'middle') // style it up a little bit to look reasonable and readable
.attr('fill', 'white')
// Here we combine the two datasets to pull the state name out of the map data, and then use the lookup table we
// built to grab the abbrevation
.text(d => stateLookup[d.properties.name])
// the path.centroid function is a built in helper where d3 will give you the x/y coordinates for the
// center of a path - this does well in most cases (but not all)
.attr('x', d => pathRenderer.centroid(d)[0])
.attr('y', d => pathRenderer.centroid(d)[1])
return svg.node();
}
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more