D3’s geographic projections allow features to be rotated about three axes. The three angles used are called Euler angles, and in D3 they specify the following sequence of rotations:

This sequence is convenient, since if we approximate the globe as a sphere, the first two angles can be used to specify a central meridian λ_{0} and central meridian φ_{0} by simple negation: `[-λ`

, and the third angle allows oblique projections.
_{0}, -φ_{0}]

A naïve implementation simply takes the x- and y-coordinates in 2D screen space and uses them directly as the first two Euler angles mentioned above.

However, the geographic start point of the gesture often moves at a different speed to the cursor, making it difficult to control. Moreover, since only two angles of rotation are being controlled, it is impossible to interactively specify the third angle using this method.

An improved implementation ensures the geographic start point remains under the cursor where possible. If the gesture goes outside the projection range, it adjusts the third Euler angle (the oblique projection angle).

It also incrementally rotates the world about an axis perpendicular to the incremental direction in spherical coordinates. This allows all three angles of rotation to be adjusted by dragging, and it works for arbitrary map projections, such as Eisenlohr’s projection below.

See Animated World Zoom.

I used versors (unit quaternions) to accumulate the 3D rotation during a gesture, as they can be easily converted to and from axis-angle representations.

The code is available as a reusable helper, d3.geo.zoom, which wraps d3.behavior.zoom and updates a projection’s rotation during the gesture.