Public
Edited
Feb 21, 2023
Insert cell
Insert cell
Insert cell
chart = {
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height])
.attr("fill", "none")
.attr("stroke", "currentColor");

applyPencilFilterTextures(svg);
const outline = svg.append("path")
.attr("fill","mintcream");

const feature = svg.append("path")
.attr("stroke-width", "3px")
.attr("fill","white")
.attr("filter", "url(#pencilTexture3");

function render() {
outline.attr("d", path(sphere));
feature.attr("d", path(land));
}

return svg
.call(zoom(projection).on("zoom.render end.render", render))
.call(render)
.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
sphere = ({type: "Sphere"})
Insert cell
Insert cell
world = FileAttachment("land-110m.json").json()
Insert cell
topojson = require("topojson@3")
Insert cell
d3 = require("d3@6")
Insert cell
zoom = function(projection, {
// Capture the projection’s original scale, before any zooming.
scale = projection._scale === undefined
? (projection._scale = projection.scale())
: projection._scale,
scaleExtent = [0.8, 8]
} = {}) {
let v0, q0, r0, a0, tl;

const zoom = d3.zoom()
.scaleExtent(scaleExtent.map(x => x * scale))
.on("start", zoomstarted)
.on("zoom", zoomed);

function point(event, that) {
const t = d3.pointers(event, that);

if (t.length !== tl) {
tl = t.length;
if (tl > 1) a0 = Math.atan2(t[1][1] - t[0][1], t[1][0] - t[0][0]);
zoomstarted.call(that, event);
}

return tl > 1
? [
d3.mean(t, p => p[0]),
d3.mean(t, p => p[1]),
Math.atan2(t[1][1] - t[0][1], t[1][0] - t[0][0])
]
: t[0];
}

function zoomstarted(event) {
v0 = versor.cartesian(projection.invert(point(event, this)));
q0 = versor((r0 = projection.rotate()));
}

function zoomed(event) {
projection.scale(event.transform.k);
const pt = point(event, this);
const v1 = versor.cartesian(projection.rotate(r0).invert(pt));
const delta = versor.delta(v0, v1);
let q1 = versor.multiply(q0, delta);

// For multitouch, compose with a rotation around the axis.
if (pt[2]) {
const d = (pt[2] - a0) / 2;
const s = -Math.sin(d);
const c = Math.sign(Math.cos(d));
q1 = versor.multiply([Math.sqrt(1 - s * s), 0, 0, c * s], q1);
}
projection.rotate(versor.rotation(q1));
console.clear()
// console.log(q1)
// console.log(versor.rotation(q1))
console.log(projection.scale() + "," + -versor.rotation(q1)[0] + "," + -versor.rotation(q1)[1])

// In vicinity of the antipode (unstable) of q0, restart.
if (delta[0] < 0.7) zoomstarted.call(this, event);
}

return Object.assign(selection => selection
.property("__zoom", d3.zoomIdentity.scale(projection.scale()))
.call(zoom), {
on(type, ...options) {
return options.length
? (zoom.on(type, ...options), this)
: zoom.on(type);
}
});
}
Insert cell
versor = require("versor@0.0.4")
Insert cell
import {geoCurvePath} from "@d3/context-to-curve"
Insert cell
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