Cahill-Keyes geometric context

Documentation index · Implementation notes · Bibliography

What kind of projection this is

The Cahill-Keyes is a polyhedral world map. Conceptually, the globe is divided among the eight triangular faces of an octahedron: four faces meet at the North Pole and four at the South Pole. Each spherical face covers 90 degrees of longitude and one hemisphere. Unfolding and arranging those faces produces the characteristic Cahill-Keyes M-shaped map.

This construction is unlike a cylindrical projection with one formula applied continuously across a rectangular longitude-latitude grid. It deliberately has cuts between selected octants. Within an octant, meridians and parallels are constructed piecewise to control the relative shape and scale of geographic cells. The implementation therefore follows a geometric construction rather than a single closed-form equation.

Cahill-Keyes world map in its M-shaped eight-octant layout

The illustration is the checked-in 44-by-22-inch Visionscarto SVG. Its outer frame has the required 2:1 ratio. The graticule exposes the triangular octants, the repeated polar vertices, and the intentional gaps at the map’s cuts.

Quadrants, octants, and half-octants

“Quadrant” is useful as a general orientation term, but the construction’s algorithmic unit is an octant. A hemisphere has four 90-degree longitude sectors; adding north and south makes eight spherical octants. The seams are rotated relative to the prime meridian, so the ordinary northeast, northwest, southeast, and southwest geographic quadrants do not coincide with projection faces.

The canonical numbering inherited from MegamapMaker-prep9.pl is:

Longitude sector North South Broad geographic context
160° E through 180° and 180° through 110° W 1 6 Pacific, western North America, New Zealand
110° W to 20° W 2 7 North America and North Atlantic; South America
20° W to 70° E 3 8 Europe, Africa, and the Middle East
70° E to 160° E 4 5 Asia; Australia
flowchart TB
  NP((North Pole))
  SP((South Pole))

  subgraph N[Four northern octants]
    N1["1<br/>Pacific"]
    N2["2<br/>Americas / Atlantic"]
    N3["3<br/>Europe / Africa"]
    N4["4<br/>Asia"]
  end

  subgraph S[Four southern octants]
    S6["6<br/>Pacific"]
    S7["7<br/>South America"]
    S8["8<br/>South Africa"]
    S5["5<br/>Australia"]
  end

  NP --- N1
  NP --- N2
  NP --- N3
  NP --- N4
  N1 --- S6
  N2 --- S7
  N3 --- S8
  N4 --- S5
  S6 --- SP
  S7 --- SP
  S8 --- SP
  S5 --- SP

This diagram shows geographic pairing, not the final planar positions. The rotations and translations that form the M-layout are described below.

Each octant is itself split down its central meridian. All coordinate construction can then happen in one reference half-octant:

Thus eight octants become sixteen mirrored half-octants, but the difficult geometry is calculated only once.

Geometry inside the reference half-octant

The template starts with the 10,000-unit scaffold MG. Important points on the center line, equator, outer boundary, and transition curves establish the graticule. Rays and line intersections form each meridian’s torrid, middle, and frigid segments. Circular arcs control the polar region. A specially constructed circle supplies the 15° transition through the “supple” zones near the outer third of the octant.

The original construction names twelve regions A through L. Dispatch order matters where their boundary conditions overlap:

flowchart TD
  IN["Reference coordinate<br/>0° ≤ m ≤ 45°, 0° ≤ p ≤ 90°"] --> M0{"m = 0°?"}
  M0 -- yes --> AB["A / B<br/>octant center line"]
  M0 -- no --> P75{"p ≥ 75°?"}
  P75 -- yes --> C["C<br/>polar circular arc"]
  P75 -- no --> P0{"p = 0°?"}
  P0 -- yes --> D["D<br/>equator"]
  P0 -- no --> ETEST{"p ≥ 73° and m ≤ 30°?"}
  ETEST -- yes --> E["E<br/>polar transition"]
  ETEST -- no --> M45{"m = 45°?"}
  M45 -- yes --> FGH["F / G / H<br/>outer boundary"]
  M45 -- no --> M29{"m ≤ 29°?"}
  M29 -- yes --> I["I<br/>central two-thirds"]
  M29 -- no --> JTEST{"p ≥ 73°?"}
  JTEST -- yes --> J["J<br/>frigid supple zone"]
  JTEST -- no --> P15{"p ≤ 15°?"}
  P15 -- yes --> K["K<br/>torrid supple zone"]
  P15 -- no --> L["L<br/>middle supple zone"]

The purpose of the piecewise construction is continuity through the intended joins while preserving Keyes’s chosen proportions. A point may be computed by linear interpolation along a constructed meridian, by a polar circular arc, or by a line-circle intersection, depending on its zone. The exact formulas are in the implementation notes.

From one template to the M-layout

After constructing (x, y) in the reference half-octant, the longitude-side sign mirrors y. The result is rotated and translated according to its octant. Southern octants first reflect the template horizontally about x = MG.

Octant Southern reflection Rotation Horizontal translation
1 no -120° -MG
2 no -60° -MG
3 no -120° +MG
4 no -60° +MG
5 yes -60° +MG
6 yes -120° -MG
7 yes -60° -MG
8 yes -120° +MG

Every octant then receives the same vertical translation, MG sin(60°). These rigid transformations form the standard M-profile without changing any local half-octant geometry.

flowchart LR
  LL["latitude, longitude"] --> OCT["choose octant<br/>and half-side"]
  OCT --> MP["normalize to<br/>(m, p, sign)"]
  MP --> XY["A–L reference<br/>construction"]
  XY --> MIRROR["mirror half and<br/>southern octants"]
  MIRROR --> ASSEMBLE["rotate + translate<br/>into M-layout"]
  ASSEMBLE --> FRAME["center and invert y<br/>in a 2:1 frame"]

The 2:1 rendering frame

The canonical scaffold has MG = 10,000 and an M-layout span of 40,000 units. For a frame of width W and height H, this implementation requires W = 2H and chooses:

MG = H / 2 = W / 4
frame center = (W / 2, H / 2)

The native construction uses Cartesian coordinates with positive y upward. Rendering uses screen coordinates with positive y downward, so the final conversion is:

X = W / 2 + x
Y = H / 2 - y

Scaling only changes lengths, not angles, zone boundaries, octant selection, or relative positions. The same code therefore fits a 44-by-22 logical frame, the 4224-by-2112 SVG coordinate space, the 13200-by-6600 300-DPI raster, and arbitrary fractional 2:1 frames.

Cuts, seams, and paths

A polyhedral projection is discontinuous at its cut edges. The point API makes a deterministic octant choice at every longitude boundary, but a line or polygon crossing a cut must be split instead of connected through the blank space between octants. Projection-specific path handling remains in cart0freak0-cahill-keyes-functions.h.

The helper views the placed projection as the rectangle bounded by L, R, T, and B. A large jump between opposite outer quarters or opposite vertical halves is interpreted as a short wrapped continuation. It terminates the current path at one edge and begins a new path at the paired edge:

Projected movement Current segment exits Next segment enters
left outer quarter → right outer quarter L R
right outer quarter → left outer quarter R L
north half → south half T B
south half → north half B T

The edge point is not copied from either endpoint. It is the intersection of the selected unwrapped line with the frame edge, so a diagonal path remains directionally continuous. A link that crosses both horizontal and vertical edges is split in intersection order; a link aimed exactly through a corner exits and re-enters through the opposite corner.

flowchart LR
  PAIR["adjacent projected points"] --> CLASSIFY["classify opposite<br/>quarters / halves"]
  CLASSIFY --> UNWRAP["shift endpoint by<br/>frame width / height"]
  UNWRAP --> INTERSECT["intersect selected line<br/>with frame edge"]
  INTERSECT --> SPLIT["close at exit<br/>restart at paired entry"]

This is projected-path repair, not spherical clipping. It assumes ordered, reasonably sampled points already produced by the same placed Cahill-Keyes cartography. It deliberately leaves a large jump unchanged when the endpoints do not identify a supported opposite-frame-edge transition. See the implementation notes for the API contract, thresholds, formulas, and example.

The native class provides the authoritative forward construction and an octant-qualified reverse. The reverse first undoes a selected M-layout octant and then solves the bounded piecewise half-octant by forward residual. It does not pretend the interrupted carrier has one globally unique inverse: the runtime returns explicit candidates and unique, ambiguous, cut, or outside status. See the forward/reverse API and the implementation plan and issues.

Documentation index · Implementation notes · Bibliography