Cartofreako WebAssembly quick start

Documentation index · Runtime reference · Forward/reverse API · Runnable examples · Stage 10 notes

This is the short path for a web developer who wants an interrupted world map without learning the native SVG generation system. The API accepts ordinary GeoJSON, supports all six projections, and returns one typed-array geometry format for SVG, Canvas, D3, workers, and custom renderers.

1. Build

From the repository root:

make wasm-projections
make check-forward-reverse-projection-api
make check-wasm-projections

For the real-browser and worker check:

make check-wasm-projections-browser

The build creates these two untracked artifacts beside the checked-in browser helpers:

src.wasm/cartofreako-projections.mjs
src.wasm/cartofreako-projections.wasm

2. Deploy

The smallest normal deployment keeps these three files together:

cartofreako-web.mjs
cartofreako-web.d.ts
cartofreako-projections.mjs
cartofreako-projections.wasm

Add whichever output adapters you use:

cartofreako-canvas.mjs
cartofreako-screen.mjs
cartofreako-screen.d.ts
cartofreako-three.mjs
cartofreako-three.d.ts
cartofreako-svg.mjs
cartofreako-d3.mjs
cartofreako-projections-worker.mjs
cartofreako-worker-client.mjs
cartofreako-worker-client.d.ts

Serve .wasm as application/wasm and .mjs as JavaScript. Use HTTP(S), not file://. For a local trial from the repository root:

python3 -m http.server 8000

Then open http://localhost:8000/src.wasm/examples/slices.html, the exact-1080p Canvas canary at /src.wasm/examples/screen-1080p.html, or the checked Three.js flat-plane example at /src.wasm/examples/screen-1080p-three.html.

3. Project GeoJSON to Canvas

<canvas id="map" width="1200" height="600"></canvas>
<script type="module">
  import createCartofreako from './cartofreako-web.mjs';
  import {drawBaseMap} from './cartofreako-canvas.mjs';

  const runtime = await createCartofreako();
  const projection = runtime.createProjection({
    id: 'cahill-keyes',
    width: 1200
  });

  const land = await fetch('./land.geojson').then(r => r.json());
  const ocean = projection.carrierGeometry();
  const features = projection.projectGeometry(land, {
    tolerancePx: 0.35
  });

  const canvas = document.querySelector('#map');
  canvas.width = features.frame.width;
  canvas.height = features.frame.height;
  drawBaseMap(canvas.getContext('2d'), ocean, features);

  projection.dispose();
</script>

Only width is needed; the runtime derives the exact native height. If you provide both dimensions, an incorrect ratio is rejected. The high-level API uses GeoJSON order: projection.project(longitude, latitude).

Switch projection models by changing id:

cahill-keyes  authagraph  dymaxion  myriahedral  star-x  voronoi

Forward and reverse points

Runtime API 3 returns topology with every forward point and never assumes that an interrupted reverse is globally unique:

const projection = runtime.projection({
  name: 'myriahedral-pacific',
  frame: [1920, 1080]
});

const forward = projection.forward([171.2, 7.1]);
// {x, y, nativeCell, component}

const reverse = projection.inverse([forward.x, forward.y]);
// {status, candidates, tolerancePx, truncated}

const oneFace = projection.inverse([forward.x, forward.y], {
  nativeCell: forward.nativeCell,
  component: forward.component
});

All Myriahedral layouts, Cahill–Keyes, AuthaGraph, Dymaxion, and Voronoi advertise inverseMode: "face-qualified". Star-X advertises "candidates" because its ordinary carrier and unified Antarctic cap can overlap. Preserve every candidate when the status is ambiguous, or pass known nativeCell and component values; never silently select the first result.

Star-X structured points select the current visible composition automatically:

const star = runtime.createProjection({id: 'star-x', width: 1080});
const cap = star.forward([171.2, -75]);
// cap.component === 1
const geographic = star.inverse([cap.x, cap.y], {
  nativeCell: cap.nativeCell,
  component: cap.component
});

At the South Pole, omit nativeCell to receive the four southern quadrant representatives, or supply it to retain one known source cell.

Use forwardMany() and inverseMany() with packed Float64Array pairs for agent, game, or data-tool batches. The point API is version 3 while geometry buffers remain ABI 1:

console.log(runtime.apiVersion); // 3
console.log(runtime.abiVersion); // 1
console.log(projection.metadata().inverseMode);

The runtime is fully headless. It performs no network access, opens no prompt, and persists no consent or authorization state. Calling workflows remain responsible for recording source licenses, data consent, and publication authority. See the complete API contract.

Map projected coordinates into an exact 1080p product

Build the complete checked standard derivative set and catalog:

make generate-screen-1080p
make check-screen-1080p

The output uses a 1920 × 1080 contain fit with #f4f5f5 padding; nothing is cropped or stretched. The current manifest contains 211 products: 32 standard whole-map pass IDs across the applicable projections, five alternate Myriahedral layouts, twelve Cahill–Keyes slices, and two Myriahedral slices. Convert pointer coordinates through the catalog matrix before calling the reverse API:

import {
  projectedToScreen,
  screenToGeographic
} from './cartofreako-screen.mjs';

const forward = projection.forward([171.2, 7.1]);
const screen = projectedToScreen([forward.x, forward.y], artifact.screen);
const inverse = screenToGeographic(projection, screen, artifact, {
  nativeCell: forward.nativeCell,
  component: forward.component
});

Padding clicks throw RangeError; handle that as “outside map,” not as a geographic point. The catalog separately identifies the authoritative SVG, print PDF, full PNG, screen PNG, and lossless WebP with hashes and authority classes.

For a Three.js plane and raycast pick, import createThreeFlatMap() and threeIntersectionToGeographic() from cartofreako-three.mjs. The checked example uses the exact vendored MIT-licensed three@0.185.1 browser module. The 1920 × 1080 image—including any contain padding—belongs on a flat 16:9 plane, never an equirectangular sphere.

Cancel long worker requests

Every worker method accepts a final request-options object with an AbortSignal. Methods that already have projection options retain those as the preceding argument:

const controller = new AbortController();
const work = worker.projectGeometry(
  {id: 'myriahedral-pacific', width: 1920},
  largeGeoJson,
  {tolerancePx: 0.25},
  {signal: controller.signal}
);
controller.abort();
await work; // rejects with AbortError

Cancellation is immediate at the client, suppresses late results, and sends a cooperative cancel message before queued work enters WebAssembly. JavaScript cannot preempt a synchronous WASM function already executing in one worker; terminate that worker when hard preemption is required.

Return SVG instead

import {renderBaseMapSvg} from './cartofreako-svg.mjs';

const svg = renderBaseMapSvg(
  projection.carrierGeometry(),
  projection.projectGeometry(land),
  {title: 'Myriahedral world map'}
);
document.querySelector('#map').innerHTML = svg;

renderBaseMapSvg() emits separate ocean and land groups. For arbitrary marks or lines, use renderSvg(projectedBuffer).

Use a slice

Create the projection on its complete ratio-correct carrier, then pass a slice to both the carrier and feature calls:

const projection = runtime.createProjection({
  id: 'cahill-keyes',
  width: 1200
});

const options = {slice: 'ck-octant-7'};
const ocean = projection.carrierGeometry(options);
const features = projection.projectGeometry(land, options);

console.log(features.frame);
// Local arbitrary-ratio frame. Coordinates already have source x/y removed.

Discover named slices:

for (const slice of projection.listSlices()) {
  console.log(slice.id, slice.kind, slice.sourceView);
}

Built-in IDs are ck-strip-1 through ck-strip-4, ck-octant-1 through ck-octant-8, and myria-group-1/myria-group-2 on the reference Myriahedral layout.

Use a source-region preclip when the desired region is geographic:

const eastAsia = projection.projectGeometry(land, {
  slice: {
    kind: 'geographic-preclip',
    bounds: [100, 0, 150, 50] // west, south, east, north
  }
});

Use a finite-carrier tile or viewport for pan/zoom delivery:

const tile = projection.projectGeometry(land, {
  slice: {
    id: 'z2-x1-y0',
    kind: 'planar-tile',
    view: [300, 0, 300, 300] // carrier x, y, width, height
  }
});

An advanced custom face mask names native cells explicitly:

const faces = projection.projectGeometry(land, {
  slice: {
    kind: 'native-cell-mask',
    selectedCells: [4, 5, 6, 18, 19]
  }
});

The key rule is that none of these output dimensions reconstructs the projection. A slice filters or views the complete carrier and then translates retained coordinates locally.

Run the checked examples from Node:

node src.wasm/examples/slice-node.mjs ck-octant-7
node src.wasm/examples/slice-node.mjs myria-group-1

Keep projection work off the main thread

import CartofreakoWorkerClient from './cartofreako-worker-client.mjs';
import {drawCommandBuffer} from './cartofreako-canvas.mjs';

const worker = new CartofreakoWorkerClient();
const projected = await worker.projectGeometry(
  {id: 'voronoi', width: 1200},
  geojson,
  {slice: null, tolerancePx: 0.35}
);

drawCommandBuffer(context, projected);
worker.terminate();

The worker parses GeoJSON, owns its WASM instance, and transfers output array buffers back to the page. It uses an ordinary module worker and does not require shared memory or cross-origin isolation.

Use with D3

Load d3-geo however your application normally does, then use the supplied stream adapter as the projection passed to geoPath:

import {geoPath} from 'd3-geo';
import {cartofreakoD3Projection} from './cartofreako-d3.mjs';

const adapter = cartofreakoD3Projection(projection, {
  slice: 'ck-strip-2',
  tolerancePx: 0.35
});
const path = geoPath(adapter);
svgPath.setAttribute('d', path(geojson));

const candidates = adapter.invertCandidates([x, y]);
const unique = adapter.invert([x, y]); // [longitude, latitude] or null

The adapter buffers each source line or polygon, calls WASM in a batch, then replays topology-safe events to D3’s sink. It does not call into WASM once per vertex.

Leaflet and OpenLayers

Treat the result as a finite planar carrier:

Do not register the forward transform as if it had one continuous global inverse. Runtime API 3 exposes face/component-qualified candidates, but interrupted cuts can still map one planar boundary to several geographic results. For feature clicks and hover, retain featureIds and index the projected parts in planar space; use reverse candidates for coordinate readout and editing only after handling their status.

Typed-array input for high-volume data

Skip GeoJSON parsing when data is already flat:

const projected = projection.projectGeometry({
  coordinates: new Float64Array([lon0, lat0, lon1, lat1]),
  partOffsets: new Uint32Array([0, 2]),
  partTypes: new Uint8Array([1]),       // line
  featureIds: new Uint32Array([42]),
  ringRoles: new Uint8Array([0])
});

The returned arrays are owned JavaScript copies, so later WASM memory growth does not invalidate them.

Common mistakes

Symptom Fix
WASM fetch or CORS error Serve through HTTP(S), keep loader and binary together, and send application/wasm
Frame-ratio exception Supply only width, or use the exact ratio from runtime.manifest
Lines jump across the page Do not project raw vertices yourself; pass complete lines/rings to projectGeometry()
A slice is distorted Keep the full carrier; use {slice: ...} rather than constructing a projection from slice dimensions
Main thread stalls on a large file Use CartofreakoWorkerClient
Reverse returns unsupported The current six-family registry should not; verify that the caller and WASM binary both use runtime API 3
Reverse returns several candidates Preserve all candidates or repeat with the intended nativeCell and component; do not choose index zero implicitly

Inspect runtime.manifest and runtime.licenses before publishing. The Cahill-Keyes and Star-X implementations carry an additional attribution and commercial-use notice beyond the repository’s GPL license.


Documentation index · Runtime reference · Forward/reverse API · Runnable examples · Stage 10 notes