Public
Edited
Mar 30, 2023
Importers
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function map_it(objects, opts = {}) {
let {
w = 600,
h = w,
pad = 20,
fontSize = 14,
projection = d3.geoEquirectangular()
} = opts;

let div = d3
.create("div")
.style("width", `${w}px`)
.style("height", `${h}px`)
.style("position", "relative")
.style("background-color", "lightblue");
let svg = div
.append("svg")
.attr("viewBox", [0, 0, w, h])
.style("max-width", `${w}px`);

projection.fitExtent(
[
[pad, pad],
[w - pad, h - pad]
],
objects.boundary
);

let path = d3.geoPath().projection(projection);

let graticule = d3
.geoGraticule()
.extent([
[-180, -80],
[180, 80]
])
.step([1, 1])();

if (objects.background) {
let background = svg.append("g");
background
.selectAll("path")
.data(objects.background.features)
.join("path")
.attr("class", "land")
.attr("d", path)
.attr("fill", "#f3f3f3")
.attr("stroke", "gray")
.attr("stroke-width", 0.5);
}

// Draw the boundary
let boundary = svg.append("g");
boundary
.selectAll("path")
.data(objects.boundary.features)
.join("path")
.attr("class", "land")
.attr("d", path)
.attr("fill", "lightgray")
.attr("stroke", "black")
.attr("stroke-width", 3);

if (objects.roads) {
let road_group = svg.append("g");
road_group
.append("g")
.selectAll("path")
.data(objects.roads.features)
.join("path")
.attr("fill", "none")
.attr("stroke", "black")
.attr("stroke-width", 4)
.attr("stroke-linejoin", "round")
.attr("d", path)
.style("pointer-events", "none");
road_group
.append("g")
.selectAll("path")
.data(objects.roads.features)
.join("path")
.attr("fill", "none")
.attr("stroke", "white")
.attr("stroke-width", 2)
.attr("stroke-linejoin", "round")
.attr("d", path)
.style("pointer-events", "none");
}

if (objects.cities) {
objects.cities.features.forEach(function (o) {
let [x, y] = projection(o.geometry.coordinates);
o.properties.x = x;
o.properties.y = y;
});
let city_group = svg.append("g");
city_group
.selectAll("circle")
.data(objects.cities.features)
.join("circle")
.attr("cx", (o) => o.properties.x)
.attr("cy", (o) => o.properties.y)
.attr("r", 4)
.attr("fill", "red")
.attr("stroke", "black");
div
.append("div")
.selectAll("div")
.data(objects.cities.features)
.join("div")
.style("position", "absolute")
.style("font-size", `${fontSize}px`)
.style("left", (o) => `${o.properties.x}px`)
.style("top", (o) => `${o.properties.y}px`)
.text((o) => o.properties.Name)
.style("border", "solid 1px black")
.style("background-color", "rgba(256,256,256,0.7)");
}

return div.node();
}
Insert cell
// I've got friends from Greensboro but it's causing some troublesome overlap
nc.cities.features.filter((o) => o.properties.NAME != "Greensboro")
Insert cell
nc = {
let data = await FileAttachment("nc_in_context@1.json").json();
let boundary = topojson.feature(data, data.objects.boundary);
let roads = topojson.feature(data, data.objects.roads);
let cities = topojson.feature(data, data.objects.cities);
cities.features = cities.features.filter(
(o) => o.properties.NAME != "Greensboro"
);
cities.features.forEach((o) => (o.properties.Name = o.properties.NAME));
let background = topojson.feature(data, data.objects.background);

return { boundary, roads, cities, background };
}
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