npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@dclimate/zarr-map

v0.1.5

Published

Interactive MapLibre visualizations for dClimate-compatible Zarr and Jaxray gridded climate datasets.

Readme

@dclimate/zarr-map

@dclimate/zarr-map is a TypeScript library for rendering dClimate-compatible Zarr datasets on interactive maps. The package exists so a website can pass a dataset request, variable, optional map config, time range, spatial bounds, and visual options, then get a working MapLibre visualization without solving Zarr metadata, IPFS/gateway access, selector normalization, or renderer compatibility itself.

The package should feel like a light map-rendering wrapper over @dclimate/dclimate-client-js, Jaxray-compatible dataset metadata/stores, MapLibre, and the selected Zarr renderer.

Product Direction

The primary use case is no-hassle dClimate map rendering:

<DClimateZarrMap
  dataset={{
    collection: "ecmwf_era5",
    dataset: "temperature_2m",
    variant: "finalized"
  }}
  gatewayUrl="https://ipfs-gateway.dclimate.net"
  variable="2m_temperature"
  bounds={[-12, 35, 16, 60]}
  timeRange={{
    start: "2024-01-01T00:00:00Z",
    end: "2024-01-07T23:00:00Z"
  }}
  colorScale={{
    palette: "viridis",
    domain: [260, 320],
    unit: "K",
    displayUnit: "C",
    quantity: "temperature"
  }}
/>

The package should support two usage modes:

  1. dClimate mode

    • Accept a dClimate dataset query/config.
    • Use @dclimate/dclimate-client-js to resolve and load datasets, including spatial bounds and time-window selection when provided.
    • Render selected variables on a MapLibre map with light controls such as a legend, time selector, loading/error status, and inspect callback.
    • Available datasets: dClimate STAC Browser
  2. Generic grid-source mode

    • Accept a normalized GridDataSource object.
    • Support Jaxray-compatible datasets through an adapter.
    • Keep the renderer reusable outside dClimate datasets.

The preferred architecture is a generic core with first-class dClimate and Jaxray adapters, but package features should stay close to rendering and request-scoped data loading.

Product Boundaries

Include functionality that removes friction from map rendering:

  • dClimate dataset resolution and bounded/time-window source loading.
  • Normalized metadata contracts for Zarr/Jaxray sources.
  • Selector normalization for time, band, and other non-spatial dimensions.
  • MapLibre layer creation and updates.
  • Renderer loading/error state.
  • Light visual customization: palette/gradient, color domain, opacity, display unit, legend, time selector, and inspect callbacks.
  • Request preflight checks that estimate cells and bytes before oversized map requests begin expensive bounded loading.

Avoid functionality that turns this package into an application:

  • Marketplace purchase flow, wallet/order state, or decryption UX.
  • CSV/JSON export.
  • 2D chart rendering or chart aggregation.
  • Dashboard layouts, modal workflows, generic form builders, or rich explorer pages.
  • Heavy map drawing/editing tools as core behavior.

If a feature is mostly about discovering, resolving, slicing, or decrypting dClimate data rather than rendering it, prefer implementing it upstream in dclimate-client-js or another dClimate data package and consuming it here.

Initial Technical Direction

  • Base map: MapLibre GL JS with a packaged Mapbox-dark-inspired OpenFreeMap default.
  • Optional compatibility: allow users to provide existing MapLibre or Mapbox map instances where practical.
  • First renderer candidate: @carbonplan/zarr-layer.
  • Avoid a custom WebGL raster renderer unless the research spike proves existing renderers cannot support target datasets.
  • Keep @dclimate/dclimate-client-js optional for users who only need generic grid-source mode.
  • Keep React UI primitives small. Prefer prop-driven rendering over bundled explorer workflows.

Target Package Shape

@dclimate/zarr-map
@dclimate/zarr-map/core
@dclimate/zarr-map/react
@dclimate/zarr-map/dclimate

Planned responsibilities:

  • src/core: framework-agnostic contracts, selectors, validation, color scales, and query helpers.
  • src/renderers: renderer bridges, starting with MapLibre plus @carbonplan/zarr-layer.
  • src/dclimate: dClimate client adapter and dataset discovery/loading helpers.
  • src/react: React components such as ZarrMap, DClimateZarrMap, TimeSlider, and Legend.
  • docs: architecture notes and decision records.
  • tests: future unit and integration tests.
  • fixtures: small fixture datasets or mocks for tests.
  • todo: implementation tickets with acceptance criteria and dependencies.

MVP API Target

import { DClimateZarrMap } from "@dclimate/zarr-map/react";

export function App() {
  return (
    <DClimateZarrMap
      dataset={{
        collection: "ecmwf_era5",
        dataset: "temperature_2m",
        variant: "finalized"
      }}
      gatewayUrl="https://ipfs-gateway.dclimate.net"
      variable="2m_temperature"
      timeDimension="time"
      initialSelector={{ time: "2024-01-01T00:00:00Z" }}
      bounds={[-12, 35, 16, 60]}
      timeRange={{
        start: "2024-01-01T00:00:00Z",
        end: "2024-01-07T23:00:00Z"
      }}
      colorScale={{
        palette: "viridis",
        domain: [260, 320],
        unit: "K"
      }}
      controls={{
        timeSlider: true,
        legend: true,
        inspect: true
      }}
    />
  );
}

Generic mode should use the same renderer through a normalized source:

import { createJaxraySource } from "@dclimate/zarr-map/core";
import { ZarrMap } from "@dclimate/zarr-map/react";

const source = createJaxraySource(dataset);

<ZarrMap
  source={source}
  variable="precipitation"
  selector={{ time: 0 }}
  controls={{ timeSlider: true, legend: true }}
/>;

Install

npm install @dclimate/zarr-map maplibre-gl @carbonplan/zarr-layer

React users also install react and react-dom. dClimate mode additionally uses the optional peers @dclimate/dclimate-client-js and @dclimate/jaxray.

Demo

  • Demo URL after Vercel deployment: https://zarr-map-demo.vercel.app
  • Demo source: https://github.com/dClimate/zarr-map-demo

The standalone Vite demo app consumes this package like an external user. For local development before the first NPM publish, the demo can depend on the package with a local file:../zarr-map-visualizer link.

Package Entrypoints

import { createJaxraySource, queryPoint } from "@dclimate/zarr-map/core";
import {
  createDefaultMapLibreMapConfig,
  createMapLibreGridLayer
} from "@dclimate/zarr-map/renderer";
import { createDClimateSource } from "@dclimate/zarr-map/dclimate";
import { DClimateZarrMap, ZarrMap } from "@dclimate/zarr-map/react";

Generic Grid Mode

import { createJaxraySource } from "@dclimate/zarr-map/core";
import { ZarrMap } from "@dclimate/zarr-map/react";

const source = createJaxraySource(dataset, {
  bounds: [-10, 35, 5, 45],
  spatialDimensions: { x: "lon", y: "lat" }
});

<ZarrMap
  colorScale={{ palette: "viridis", domain: [0, 50], unit: "mm" }}
  controls={{ legend: true, timeSlider: true, inspect: true }}
  source={source}
  variable="precipitation"
/>;

dClimate Mode

import { DClimateZarrMap } from "@dclimate/zarr-map/react";

<DClimateZarrMap
  colorScale={{
    palette: "viridis",
    domain: [260, 320],
    unit: "K",
    displayUnit: "C",
    quantity: "temperature"
  }}
  dataset={{
    collection: "ecmwf_era5",
    dataset: "temperature_2m",
    variant: "finalized"
  }}
  gatewayUrl="https://ipfs-gateway.dclimate.net"
  initialSelector={{ time: "2024-01-01T00:00:00.000Z" }}
  bounds={[-12, 35, 16, 60]}
  timeRange={{
    start: "2024-01-01T00:00:00Z",
    end: "2024-01-07T23:00:00Z"
  }}
  variable="2m_temperature"
/>;

bounds and timeRange are convenience aliases for sourceOptions.selection.bounds and sourceOptions.selection.timeRange. If a caller needs lower-level dClimate selection options, it can pass sourceOptions.selection directly. The aliases normalize into that same selection object and do not create a separate selection model.

map is optional for React components. When omitted, the package uses a Mapbox-dark-inspired OpenFreeMap basemap centered at [10, 50], zoom 2.25, with MapLibre globe projection enabled in the style object. map.bounds and map.maxBounds are forwarded to MapLibre for viewport fitting and pan limits when callers provide a custom map config.

Default Basemap

ZarrMap and DClimateZarrMap use the packaged default basemap when the map prop is omitted:

<ZarrMap
  colorScale={{ palette: "viridis", domain: [0, 50], unit: "mm" }}
  controls={{ legend: true, timeSlider: true, inspect: true }}
  source={source}
  variable="precipitation"
/>

Callers that need an explicit MapLibre config can reuse the same default:

import { createDefaultMapLibreMapConfig } from "@dclimate/zarr-map/renderer";

const portugalMap = createDefaultMapLibreMapConfig({
  attributionPosition: "top-left",
  center: [-8.6, 39.5],
  projection: "globe",
  zoom: 5
});

The helper returns a fresh map config and style object on every call. The default style uses OpenFreeMap/OpenMapTiles vector data with a Mapbox-dark-inspired palette, and keeps projection in the style JSON so globe mode works when the package owns the MapLibre instance.

The default map config shows expanded attribution for OpenFreeMap, OpenMapTiles, OpenStreetMap, and MapLibre in the bottom-right corner. Use attributionPosition to move it to "top-left", "top-right", "bottom-left", or "bottom-right".

Map Position And Style

The map prop accepts the MapLibre options this package owns, without container. Use it to set the initial camera or replace the basemap style:

import { createDefaultMapLibreMapConfig } from "@dclimate/zarr-map/renderer";

<ZarrMap
  map={createDefaultMapLibreMapConfig({
    center: [-8.6, 39.5],
    zoom: 5
  })}
  source={source}
  variable="precipitation"
/>;

center is [longitude, latitude], not [latitude, longitude]. For example, Portugal is near [-8.6, 39.5]. zoom uses MapLibre's normal scale: lower numbers show more of the world, while higher numbers move closer to the data.

For a known region, prefer map.bounds when you want MapLibre to fit the initial camera to an extent instead of hand-picking a center and zoom:

import { createDefaultMapLibreMapConfig } from "@dclimate/zarr-map/renderer";

<DClimateZarrMap
  bounds={[-12, 35, 16, 60]}
  dataset={{
    collection: "ecmwf_era5",
    dataset: "temperature_2m",
    variant: "finalized"
  }}
  map={{
    ...createDefaultMapLibreMapConfig(),
    bounds: [
      [-12, 35],
      [16, 60]
    ],
    maxBounds: [
      [-20, 30],
      [25, 65]
    ]
  }}
  timeRange={{
    start: "2024-01-01T00:00:00Z",
    end: "2024-01-07T23:00:00Z"
  }}
  variable="2m_temperature"
/>;

bounds on DClimateZarrMap selects the data to load. map.bounds controls the initial MapLibre viewport, and map.maxBounds limits how far the user can pan. dClimate selection bounds use [west, south, east, north]; MapLibre camera bounds use [[west, south], [east, north]]. When map.bounds is present, MapLibre fits the initial camera to those bounds, so center and zoom are usually unnecessary.

To replace the basemap entirely, pass any MapLibre style URL or style object:

<ZarrMap
  map={{
    center: [0, 20],
    zoom: 1.5,
    style: "https://demotiles.maplibre.org/style.json"
  }}
  source={source}
  variable="precipitation"
/>

When you provide a custom style object and want globe mode, include projection: { type: "globe" } in that style object.

Globe Projection

The package default uses MapLibre's globe projection. To force a flat map while keeping the packaged basemap, pass projection: "mercator":

import { createDefaultMapLibreMapConfig } from "@dclimate/zarr-map/renderer";

<ZarrMap
  map={createDefaultMapLibreMapConfig({ projection: "mercator" })}
  source={source}
  variable="precipitation"
/>;

For custom styles, put projection in the MapLibre style object:

<ZarrMap
  colorScale={{ palette: "viridis", domain: [0, 50], unit: "mm" }}
  controls={{ legend: true, timeSlider: true, inspect: true }}
  map={{
    center: [0, 20],
    zoom: 1.5,
    style: {
      version: 8,
      projection: { type: "globe" },
      sources: {
        "base-raster": {
          type: "raster",
          tiles: ["https://tile.openstreetmap.org/{z}/{x}/{y}.png"],
          tileSize: 256
        }
      },
      layers: [
        {
          id: "base-raster",
          type: "raster",
          source: "base-raster"
        }
      ]
    }
  }}
  source={source}
  variable="precipitation"
/>

If a custom basemap is loaded from a style URL, create the map yourself with renderer.mapFactory and switch the projection after MapLibre loads the style:

<ZarrMap
  map={{
    center: [0, 20],
    zoom: 1.5,
    style: "https://demotiles.maplibre.org/style.json"
  }}
  renderer={{
    mapFactory: async (config) => {
      const maplibre = await import("maplibre-gl");
      const map = new maplibre.Map(config as ConstructorParameters<typeof maplibre.Map>[0]);
      map.on("style.load", () => map.setProjection({ type: "globe" }));
      return map;
    }
  }}
  source={source}
  variable="precipitation"
/>

Globe mode is provided by MapLibre and the selected Zarr renderer. dClimate dataset selection, bounds, time controls, legends, opacity, and inspect callbacks continue to use the same package API.

Visual Customization

colorScale.palette accepts a built-in palette name or a custom color-stop array such as ["#1d4ed8", "#f8fafc", "#b91c1c"]. Renderer domains stay in the source unit. Built-in names include viridis, inferno, magma, plasma, cividis, turbo, greys, temperature, precipitation, humidity, vegetation, and wind. The magma, precipitation, and vegetation palettes start transparent at the lower bound for overlaying low, dry, or no-vegetation cells on top of the basemap. To show a converted display unit in legends and inspect results, include unit, displayUnit, and quantity:

colorScale={{
  palette: ["#1d4ed8", "#f8fafc", "#b91c1c"],
  domain: [260, 320],
  unit: "K",
  displayUnit: "C",
  quantity: "temperature"
}}

Supported display conversions are temperature (K, C, F) and precipitation (m, mm, in).

To override the variable name or unit text shown in the legend, including when using DClimateZarrMap with a dataset request, pass a legend control object:

<DClimateZarrMap
  dataset={{ collection: "ecmwf_era5", dataset: "temperature_2m", variant: "finalized" }}
  variable="2m_temperature"
  colorScale={{ palette: "viridis", domain: [260, 320], unit: "K" }}
  controls={{ legend: { variableName: "Air temperature", unit: "custom units" } }}
/>

These fields change the legend text only. Use displayUnit and quantity when values should be converted before display.

Inspect controls default to click-to-inspect with controls={{ inspect: true }}. Use hover mode for a pointer-following tooltip:

<ZarrMap
  ...
  controls={{ inspect: { mode: "hover", debounceMs: 75 } }}
/>

Request Preflight

dClimate mode can estimate request size before bounded raster chunks are materialized:

<DClimateZarrMap
  ...
  preflight={{ limits: { maxCells: 100_000, maxBytes: 256 * 1024 * 1024 } }}
  onPreflight={(result) => {
    console.log(result.cells, result.bytes, result.warnings);
  }}
/>

The framework-independent helper is also exported from core as preflightGridRequest.

The dClimate gateway is not generally down. On June 9, 2026, ../dclimate-client-js-autoresearch still opened the current STAC ERA5 dataset through @dclimate/dclimate-client-js plus @dclimate/jaxray, and its smoke benchmark succeeded against https://ipfs-gateway.dclimate.net.

Current validated fixture:

  • collection: ecmwf_era5
  • dataset: temperature_2m
  • variant: finalized
  • variable: 2m_temperature
  • CID: bafyr4ifr5jtjeommsulg7srirdkskybj5pay3n4qyehecyzsesas5k2kd4

The earlier blocked static CIDs should be treated as stale or unretrievable fixtures, not evidence that the gateway or Jaxray are broken. dClimate loading should call the real DClimateClient.loadDataset({ request, options }) API and unwrap the returned [Dataset, metadata] tuple.

Query Helpers

import { queryGrid, queryPoint } from "@dclimate/zarr-map/core";

const clicked = await queryPoint(source, "temperature", [-8.6, 39.5], {
  time: "2024-01-01T00:00:00.000Z"
});

const bounded = await queryGrid({
  source,
  variable: "temperature",
  geometry: { type: "BoundingBox", bounds: [-9, 38, -8, 40] },
  maxCells: 5000
});

Development

npm run typecheck
npm run format:check
npm run lint
npm run test
npm run build

CI runs the same package checks on pushes and pull requests. The demo app has its own build and deployment workflow in the separate zarr-map-demo repository. Release expectations and known renderer limitations are documented in docs/release-checklist.md.