Public
Edited
Jul 8, 2024
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
asheville_projection.proj([-82.5, 35.5])
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
global = ({ projections: [{ projection: d3.geoEquirectangularRaw }] })
Insert cell
process = {
global.projections.push(projection);
}
Insert cell
function plain_pic(proj, opts = {}) {
let { w = 600, h = 400 } = opts;

let svg = d3
.create("svg")
.attr("viewBox", [0, 0, w, h])
.style("max-width", `${w}px`);
// .style("border", "solid 1px black");

proj.fitSize([w, h], land);
if (opts.scale) {
proj.scale(opts.scale);
}
if (opts.translate) {
proj.translate(opts.translate);
}

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

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

// Draw the graticule
let graticule_path = svg
.append("g")
.selectAll("path.graticule")
.data([graticule])
.join("path")
.attr("d", path)
.attr("fill", "none")
.attr("stroke", "#333")
.attr("stroke-width", 0.4);

// Draw the countries
let land_group = svg.append("g");
land_group
.selectAll("path.land")
.data(land.features)
.join("path")
.attr("class", "land")
.attr("d", path)
.attr("fill", "#eee")
.attr("stroke", "black")
.attr("stroke-width", 1)
.attr("opacity", 1);

return svg.node();
}
Insert cell
function projection_transition_pic(p0, p1, t) {
let w = 800;
let h = 0.625 * w;

let proj = interpolateProjection(p0, p1)(t);

let svg = d3
.create("svg")
.attr("viewBox", [0, 0, w, h])
.style("max-width", `${w}px`)
// .style("border", "solid 1px black");

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

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

// Draw the graticule
let graticule_path = svg
.append("g")
.selectAll("path.graticule")
.data([graticule])
.join("path")
.attr("d", path)
.attr("fill", "none")
.attr("stroke", "#333")
.attr("stroke-width", 0.4);

// Draw the countries
let land_group = svg.append("g");
land_group
.selectAll("path.land")
.data(land.features)
.join("path")
.attr("class", "land")
.attr("d", path)
.attr("fill", "#eee")
.attr("stroke", "black")
.attr("stroke-width", 1)
.attr("opacity", 1);

return svg.node();

function interpolateProjection(p0, p1) {
const {
scale: scale0,
translate: translate0,
rotate: rotate0
} = fit(p0.projection, p0.polar);
const {
scale: scale1,
translate: translate1,
rotate: rotate1
} = fit(p1.projection, p1.polar);
return (t) =>
d3
.geoProjection((x, y) =>
lerp2(p0.projection(x, y), p1.projection(x, y), t)
)
.scale(lerp1(scale0, scale1, t))
.translate(lerp2(translate0, translate1, t))
.rotate(lerp2(rotate0, rotate1, t));

function lerp1(x0, x1, t) {
return (1 - t) * x0 + t * x1;
}
function lerp2([x0, y0], [x1, y1], t) {
return [(1 - t) * x0 + t * x1, (1 - t) * y0 + t * y1];
}
function fit(raw, polar) {
let p = d3.geoProjection(raw);
p.fitExtent(
[
[0.5, 0.5],
[w - 0.5, h - 0.5]
],
land
);
if (polar) {
p.rotate([0, -90]).scale(200);
}
return { scale: p.scale(), translate: p.translate(), rotate: p.rotate() };
}
}
}
Insert cell
Insert cell
nh = {
let nh = await FileAttachment("nh@1.json").json();
let boundary = topojson.feature(nh, nh.objects.nh);
let roads = topojson.feature(nh, nh.objects.roads);
return { boundary, roads };
}
Insert cell
nc = {
let nc = await FileAttachment("nc@2.json").json();
let boundary = topojson.feature(nc, nc.objects.nc);
let roads = topojson.feature(nc, nc.objects.roads);
return { boundary, roads };
}
Insert cell
land = {
let land = await FileAttachment("ne_110m_land.json").json();
return topojson.feature(land, land.objects.ne_110m_land);
}
Insert cell
contiguous_us = {
let contiguous_us = await FileAttachment(
"contiguous_us.json"
).json();
let cities = topojson.feature(
contiguous_us,
contiguous_us.objects.cities
);
let states = topojson.feature(
contiguous_us,
contiguous_us.objects.states
);

return { cities, states };
}
Insert cell
Insert cell
tippy_style = html`<link style="display: none" rel="stylesheet" href="${await require.resolve(
`tippy.js/themes/light-border.css`
)}">`
Insert cell
tippy = require("tippy.js@6")
Insert cell
d3 = require("d3@7", "d3-geo@3", "d3-geo-projection@4")
Insert cell
Insert cell
<style>
blockquote {
background: #e9e9e9;
border: solid 1px #aaa;
border-radius: 9px;
padding-left: 12px;
}
.aside {
font-size: 0.8em;
background: #efefef;
margin: 10px;
border: solid 1px #aaa;
border-radius: 9px;
padding: 12px;
max-width: 600px;
}
</style>
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