Public
Edited
Mar 31, 2024
Insert cell
Insert cell
{
const width = 960;
const height = 500;
const svg = d3.create("svg");
svg.attr("width", width).attr("height", height);

const projection = d3
.geoOrthographic()
.scale(250)
.translate([width / 2, height / 2])
.clipAngle(90); // Clip the sphere to a hemisphere

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

svg
.append("path")
.datum({ type: "Sphere" })
.attr("class", "sphere")
.attr("d", path);
//.attr("fill", "#ADD8E6");

const graticule = d3.geoGraticule();

svg
.append("path")
.datum(graticule())
.attr("class", "graticule")
.attr("d", path)
.attr("fill", "none")
.attr("stroke", "#ccc");

// Load and draw the world
const url =
"https://cdn.jsdelivr.net/npm/visionscarto-world-atlas@0.1.0/world/110m.json";

d3.json(url).then((world) => {
const countries = topojson.feature(world, world.objects.countries);

svg
.selectAll(".country")
.data(countries.features)
.enter()
.append("path")
.attr("class", "country")
.attr("d", path)
.attr("fill", "#69b3a2");
});

// Add rotation
function rotateGlobe() {
d3.timer(function (elapsed) {
projection.rotate([elapsed / 200, -15]);
svg.selectAll("path").attr("d", path);
});
}

//rotateGlobe();
let rotation = [0, -20],
velocity = [0, 0],
lastTime = Date.now();
const sensitivity = 0.2,
deceleration = 0.95;

function dragstarted() {
velocity = [0, 0]; // Reset velocity at the start of dragging
}

function dragged(event) {
const now = Date.now();
const time = now - lastTime;
lastTime = now;

// Update the globe's rotation based on the drag
const dx = event.dx * sensitivity;
const dy = event.dy * sensitivity;

rotation[0] += dx;
rotation[1] -= dy;
rotation[1] = Math.max(-90, Math.min(90, rotation[1])); // Constrain the vertical rotation

// Update velocity based on the drag speed and time
velocity = [dx / time, dy / time];

projection.rotate(rotation);
svg.selectAll("path").attr("d", path);
}

function dragended() {
const now = Date.now();
applyInertia(now);
}

function applyInertia(now) {
const delta = now - lastTime;
rotation[0] += velocity[0] * delta;
rotation[1] -= velocity[1] * delta;

// Apply deceleration to the velocity
velocity[0] *= deceleration;
velocity[1] *= deceleration;

// Update the projection and redraw
projection.rotate(rotation);
svg.selectAll("path").attr("d", path);

// Continue applying inertia if velocity is above a minimal threshold
if (Math.abs(velocity[0]) > 0.01 || Math.abs(velocity[1]) > 0.01) {
requestAnimationFrame(() => applyInertia(Date.now()));
}
}
svg.call(
d3.drag().on("start", dragstarted).on("drag", dragged).on("end", dragended)
);

return svg.node();
}
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