Published
Edited
Oct 9, 2022
1 fork
Importers
Insert cell
Insert cell
map = {
const container = html`<div style="height:600px;">`;
yield container;
const map = L.map(container).setView([37.8, -96.9], 4);
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
attribution: "&copy; <a href=https://www.openstreetmap.org/copyright>OpenStreetMap</a> contributors"
}).addTo(map);

// Add an SVG element to Leaflet’s overlay pane. Leaflet automatically repositions the overlay pane when the
// map pans. Note that the SVG element is initialized with no width or height; the dimensions must be set
// dynamically because they change on zoom.
const svg = d3.select(map.getPanes().overlayPane).append("svg").style("position", "relative"),
g = svg.append("g").attr("class", "leaflet-zoom-hide");

// Adapt Leaflet’s latLngToLayerPoint and LatLng methods to project a single point,
// passing the resulting coordinates to an output geometry stream (https://github.com/d3/d3-geo#streams)
function projectPoint(x, y) {
var point = map.latLngToLayerPoint(new L.LatLng(y, x));
this.stream.point(point.x, point.y);
}

// Now that we can project points, we can create a d3.geoPath to convert GeoJSON to SVG...
const transform = d3.geoTransform({point: projectPoint}),
path = d3.geoPath().projection(transform);

var feature = g.selectAll("path")
.data(states.features)
.join("path")
.attr("d", path)
.style("fill", "#000")
.style("fill-opacity", .2)
.style("stroke", "#fff")
.style("stroke-width", "1.5px")
.style("pointer-events", "all")
.on("mouseover", function () {
const element = d3.select(this);
const datum = element.data()[0].properties.index;
element.style("fill", colorScale(datum)).style("fill-opacity", .7);
})
.on("mouseout", function () { d3.select(this).style("fill", "#000").style("fill-opacity", .2) });

// It can be difficult to compute a bounding box with arbitrary projections, but fortunately D3 provides a
// convenient mechanism for computing the projected bounding box of our features using our custom transform
// to convert the longitude and latitude to pixels:
var bounds = path.bounds(states),
topLeft = bounds[0], // [x, y]
bottomRight = bounds[1];
svg.attr("width", bottomRight[0] - topLeft[0])
.attr("height", bottomRight[1] - topLeft[1])
.style("left", topLeft[0] + "px")
.style("top", topLeft[1] + "px");

g.attr("transform", "translate(" + -topLeft[0] + "," + -topLeft[1] + ")");

// With Leaflet’s viewreset event, the SVG can be repositioned and rerendered whenever the map zooms
map.on("zoom", reset);
reset();

// Reposition the SVG to cover the features.
function reset() {
const bounds = path.bounds(states),
topLeft = bounds[0],
bottomRight = bounds[1];

svg .attr("width", bottomRight[0] - topLeft[0])
.attr("height", bottomRight[1] - topLeft[1])
.style("left", topLeft[0] + "px")
.style("top", topLeft[1] + "px");

g .attr("transform", "translate(" + -topLeft[0] + "," + -topLeft[1] + ")");

feature.attr("d", path);
}
}
Insert cell
colorScale.domain()
Insert cell
colorScale.range()
Insert cell
colorScale = d3.scaleOrdinal(d3.schemeCategory10)
.domain([...new Set(states.features.map(d => d.properties.index))])
Insert cell
L = {
const L = await require("leaflet@1/dist/leaflet.js");
if (!L._style) {
const href = await require.resolve("leaflet@1/dist/leaflet.css");
document.head.appendChild(L._style = html`<link href=${href} rel=stylesheet>`);
}
return L;
}
Insert cell
states = {
const states = topojson.feature(us, us.objects.states);
states.features = states.features.map((d, i) => {d.properties = { ...d.properties, index: (i%4).toString() }; return d;})
return states
}
Insert cell
import {us, topojson} from "@pbogden/my-u-s-map-svg"
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more