A 3D and higher-dimensional extension of GeoJSON.

Install

pnpm add -D @types/hyper-geojson@github:kamataryo/types__hyper-geojson#main

Usage

A solid is a list of faces, each a polygon turned to face outward.
cube.ts
import type { Polyhedron } from 'hyper-geojson'

// A unit cube: six square faces, each turned to face outward
export const cube: Polyhedron = {
  type: 'Polyhedron',
  coordinates: [
    [[[0, 0, 0], [0, 0, 1], [0, 1, 1], [0, 1, 0], [0, 0, 0]]],
    [[[0, 0, 0], [1, 0, 0], [1, 0, 1], [0, 0, 1], [0, 0, 0]]],
    [[[0, 0, 0], [0, 1, 0], [1, 1, 0], [1, 0, 0], [0, 0, 0]]],
    [[[1, 1, 1], [1, 0, 1], [1, 0, 0], [1, 1, 0], [1, 1, 1]]],
    [[[1, 1, 1], [1, 1, 0], [0, 1, 0], [0, 1, 1], [1, 1, 1]]],
    [[[1, 1, 1], [0, 1, 1], [0, 0, 1], [1, 0, 1], [1, 1, 1]]],
  ],
}
Features carry polyhedra, polychora and higher polytopes, and the nesting tells TypeScript how deep to look.
corners.ts
import type { HyperFeature, HyperPosition, Polychoron } from 'hyper-geojson'

// Every corner of a polychoron: cells, then faces, then rings, then positions
export function corners(feature: HyperFeature<Polychoron>): HyperPosition[] {
  return feature.geometry.coordinates.flat(3)
}
Get the nesting wrong and TypeScript tells you.
depth.ts
import type { Polytope } from 'hyper-geojson'

// A 5-polytope nests its positions five levels deep
export const fine: Polytope<5> = {
  type: 'Polytope5',
  coordinates: [[[[[[0, 0, 0, 0, 0]]]]]],
}

export const tooShallow: Polytope<5> = {
  type: 'Polytope5',
  // @ts-expect-error four levels are one too few
  coordinates: [[[[[0, 0, 0, 0, 0]]]]],
}

Viewer

Sample
Petrie view
Coordinate views

cells

Live editor

    Orientation

    Every boundary faces away from what it bounds. This is GeoJSON's right-hand rule, carried to every dimension:

    So the two neighbours of every shared boundary traverse it in opposite directions: each edge of a polyhedron by two faces, each face of a polychoron by two cells. A point's winding number is then 1 inside the solid, and 0 outside it or in a void.

    A void needs no nesting level of its own: it is a boundary that faces the other way. The types check only the nesting depth, so a twisted or open boundary still type-checks, just as an open ring does in GeoJSON.