Bring your own geometry

ApexMaps.registerMap(id, geometry, meta) puts anything into the registry under an id, with the same provenance fields the built-in packs carry. The geometry below is hand-written in this page: a schematic six-zone floor plan in a flat coordinate space, drawn with the identity projection. Nothing about the engine assumes the earth, which is why a floor plan, a stadium or a wafer map works the same way a country does.

map: 'demo/floor' · identity projection
registerPalette('demo/heat', …) · a custom ramp on real geometry

Where geometry comes from

setGeoSource() takes a base URL or a loader function, so a self-hosted copy, an air-gapped install and a bundler import are all the same feature. This page uses the function form: every pack request goes through the loader below, which is also how you would pull packs out of the npm dataset with no network at all. The counter proves it ran.

loader has served 0 packs
// A base URL
ApexMaps.setGeoSource('https://cdn.example.com/apexmaps-geo/')

// Or a function: bundler imports, air-gapped paths, an authenticated fetch
ApexMaps.setGeoSource((file) => import(`apexmaps-geo/${file}`).then((m) => m.default))

Projections

Thirteen projections ship in core, and any of them can be configured as a spec object with rotate, center, parallels, clipAngle and angle, which covers nearly every real requirement. For the rest of the d3-geo-projection catalogue, register the factory. That needs a dependency this page does not load, so it is shown as code rather than pretended:

import { geoRobinson } from 'd3-geo-projection'

ApexMaps.registerProjection('robinson', () => geoRobinson())
ApexMaps.listProjections()   // now includes 'robinson'

// geo: { projection: 'robinson' }

A proj4 escape hatch for non-WGS84 coordinate systems is on the phase-1 list and not built yet. It is absent rather than present as an option that does nothing.