Core-only / Custom Renderer
@openweather/maps-core has zero MapLibre, React, or WebGL dependency — it's the option for a completely
custom renderer (Canvas 2D, a different map library, a non-map UI, a test harness) or for driving
@openweather/maps-react's hooks/compounds over your own map implementation.
createCoreMapController
import { createCoreMapController } from '@openweather/maps-core';
const controller = createCoreMapController({
apiKey: 'YOUR_KEY',
defaultLayer: 'temperature',
availableLayers: DEFAULT_LAYER_CATALOG, // optional override
wireTimelineToEngine: true, // default; false to drive setDate() yourself
pointQuerySample: async (request) => {
// Your renderer's pixel sampler — see "Point query" below.
return [];
},
});
await controller.engine.initialize();
CreateCoreMapControllerOptions also accepts defaultModelId, defaultRadarModel,
initialActiveLayers, dataBaseUrl, and timelineRange ({ start, end, transitionAt?, initialTime? }) to override the default ±TIMELINE_HOURS_EITHER_SIDE-hour window.
What you get back
CoreMapController extends the base MapController with:
| Member | Purpose |
|---|---|
engine |
The WeatherEngine instance — active layer/date state, resolve, events |
addWeatherLayer(code) / removeWeatherLayer(code) |
Manage the registry's active-layer set |
getWeatherLayer(code) / getLayerMetadata(code) / listLayers() |
Read the layer catalog |
setLayerModel(layerCode, modelId) |
Per-layer model switch (e.g. radar nowcast) |
setActiveLayer(code) |
Activate a layer and resolve its asset |
settings, layers, timeline, search (via base MapController) |
The other stores — see @openweather/maps-core API reference |
wireTimelineToEngine (default true) subscribes the timeline's currentTime to
engine.setDate() automatically, and keeps the timeline's frame step in sync with whichever
layer/model is active (e.g. radar's 10-minute cadence vs. temperature's hourly one). Set it false
if you want to drive engine.setDate() yourself on a different cadence.
Rendering the resolved asset
Subscribe to the engine and hand resolvedAsset to your own renderer:
const unsubscribe = controller.engine.subscribe(() => {
const { resolvedAsset } = controller.engine.getSnapshot();
if (resolvedAsset) renderIntoYourCanvas(resolvedAsset);
});
await controller.setActiveLayer('pressure');
await controller.engine.setDate(new Date());
ResolvedLayerAsset includes url, date, locked?, modelId?, tileGrid?/tilePyramid?, and
scalarConfig? — everything @openweather/maps-maplibre's ScalarFlowRenderer/MultiTileScalarManager
use to paint a MapLibre layer. A custom renderer follows the same contract: load the image at
url, decode/colorize it against scalarConfig, and re-render whenever the store notifies you.
Point query
PointQueryController is renderer-agnostic — core has no pixel access of its own, so you supply a
pointQuerySample function (see the options above) that maps a PointQueryRequest (lng/lat +
layer list) to a value per layer. This is exactly what @openweather/maps-maplibre's PointQuerySampler
does for the MapLibre adapter: texel lookup in the active texture, then decodeScalarValue. See
Architecture — Point query for the full four-step contract.
Driving React hooks/compounds over a custom map
Pass your CoreMapController straight to <MapsProviders> — every hook (useLayers,
useTimeline, usePointQuery, useSearch, useSettings) and compound (Timeline, Layers,
Search) works against it exactly as it would against a WeatherMapHandle's controller, since
<WeatherMap> does nothing more than construct one of these and wrap it in the same provider. See
Providers & Hooks.
Testing
Because @openweather/maps-core has no DOM dependency, createCoreMapController runs unmodified under
plain Node (no jsdom) — this is how the SDK's own core package tests run, and it's a convenient way
to unit-test business logic (layer switching, timeline alignment, entitlement handling) without
spinning up a real map at all.