Published
Edited
Apr 22, 2021
Insert cell
md`# Poughkeepsie debtor map

This is based on examples from [here](https://observablehq.com/@spattana/geovisualization/2) and [here](https://observablehq.com/@d3/zoomable-raster-vector?collection=@d3/d3-geo).`
Insert cell
map = {
// make the map with the defined width and height
const svg = d3.create("svg").attr("viewBox", [0, 0, width, height]);

//use a mercator projection map with some adjustment for curvature of the earth
const projection = d3
.geoMercator()
.scale(1 / (2 * Math.PI))
.translate([0, 0]);

// reference the mercator projection to pixels
const render = d3.geoPath(projection);

// use the tiles sublibrary to draw the image tiles of the map
const tile = d3
.tile()
.extent([[0, 0], [width, height]])
.tileSize(512);

// define how much the viewer can zoom in and out
const zoom = d3
.zoom()
.scaleExtent([1 << 10, 1 << 150])
.extent([[0, 0], [width, height]])
.on("zoom", ({ transform }) => zoomed(transform));

// let d3 draw on the map tiles
let image = svg
.append("g")
.attr("pointer-events", "none")
.selectAll("image");

// use the d3 geoPath function to draw the network lines in reference to the mercator projection
const geoGenerator = d3.geoPath().projection(projection);

// style the network path
const path = svg
.append("g")
.selectAll("path")
.data(data) // use the data file to get the start and end points of the line
.join("path")
.style("fill", d => "none")
.style("stroke", "red");

// start the zoom level at the initial zoom level and centered on Poughkeepsie
svg.call(zoom).call(
zoom.transform,
d3.zoomIdentity
.translate(width / 2, height / 2)
.scale(-initialScale)
.translate(...projection(initialCenter))
.scale(-1)
);

// handle zooming and panning
function zoomed(transform) {
const tiles = tile(transform);
// zoom and pan the image tiles of the map
image = image
.data(tiles, d => d)
.join("image")
.attr("xlink:href", d => url(...d))
.attr("x", ([x]) => (x + tiles.translate[0]) * tiles.scale)
.attr("y", ([, y]) => (y + tiles.translate[1]) * tiles.scale)
.attr("width", tiles.scale)
.attr("height", tiles.scale);

projection
.scale(transform.k / (2 * Math.PI))
.translate([transform.x, transform.y]);

// draw the actual network lines using the column names source_lon, source_lat, etc
path.attr("d", d =>
geoGenerator({
type: "Feature",
geometry: {
type: "LineString",
coordinates: [
[d.source_lon, d.source_lat],
[d.target_lon, d.target_lat]
]
}
})
);
}

// draw the whole network
return svg.node();
}
Insert cell
// import the data file
data = FileAttachment("poughkeepsie@2.csv").csv()
Insert cell
// you can keep this as is if the map style is ok, otherwise see the Module 8 Maps Lesson on making your own mapbox style
// using mapbox third party url fulcrum
url = (x, y, z) =>
`https://api.mapbox.com/styles/v1/maevekane/cknt3v7j10icq17pbml5cje7z/tiles/256/${z}/${x}/${y}${
devicePixelRatio > 1 ? "@2x" : ""
}?access_token=pk.eyJ1IjoibWFldmVrYW5lIiwiYSI6ImNra29hZG4yeDJieTUyb29kb3djdng1NjgifQ.qPMhwmvnHtErpzfZjU2x-g`
Insert cell
// this sets how zoomed in the map will be. Leave the first number as 1 and adjust the second number; the larger the second number is, the more zoomed in it will be to start
initialScale = 1 << 16
Insert cell
// the longitude and latitude you want the map centered on. The is the lon/lat of Poughkeepsie
initialCenter = [-73.93, 41.7]
Insert cell
height = 600
Insert cell
//import d3 and a map tile sublibrary to handle the mapbox map layer
d3 = require("d3@6", "d3-tile@1")
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