Interactive Globe

Graticules, Rotation and an Interactive Globe

projection.rotate([lambda, phi]) spins the sphere before projecting, so a globe is an orthographic projection whose rotation follows d3-drag. A script sets the frame with one drag:

A draggable orthographic globe with a graticule, after one scripted dragHTMLLive
<script src="https://cdn.jsdelivr.net/npm/d3@7.9.0/dist/d3.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/topojson-client@3.1.0"></script>
<svg width="720" height="210"></svg>
<script type="module">
  const url = 'https://cdn.jsdelivr.net/npm/world-atlas@2.0.2/countries-110m.json';
  const topo = await d3.json(url), svg = d3.select('svg');
  const projection = d3.geoOrthographic().fitExtent([[255, 0], [465, 210]], { type: 'Sphere' })
    .rotate([30, -20]);                                          // over the Atlantic
  const path = d3.geoPath(projection);
  const layers = [[{ type: 'Sphere' }, '#e3f2fd'], [d3.geoGraticule10(), 'none', '#b0bec5'],
    [topojson.feature(topo, topo.objects.countries), '#3f7d3a', 'white']]
    .map(([d, fill, stroke]) => svg.append('path').datum(d).attr('fill', fill)
      .attr('stroke', stroke));
  const render = () => layers.forEach(layer => layer.attr('d', path));
  const clamp = phi => Math.max(-90, Math.min(90, phi));         // keep the poles upright
  svg.call(d3.drag().on('drag', event => {
    const [lambda, phi] = projection.rotate(), k = 75 / projection.scale();  // degrees per px
    projection.rotate([lambda + event.dx * k, clamp(phi - event.dy * k)]);
    render();
  }));
  render();
  const box = svg.node().getBoundingClientRect(), at = (x, y) => ({ view: window,
    clientX: box.x + x, clientY: box.y + y });                     // simulate one drag
  svg.node().dispatchEvent(new MouseEvent('mousedown', at(360, 120)));
  window.dispatchEvent(new MouseEvent('mousemove', at(170, 100)));
  window.__done = true;
</script>
Browser output of Listing 6.47
Browser output of 47

Dragging 190 px left and 20 px up turned the rotation from [30, -20] to [-105.7, -5.7] (logged in a test run), bringing India and Australia into view. For finer data, redraw a simplified layer while dragging, or draw with d3.geoPath(projection, context) (Canvas Rendering).