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

maplibre-gl-raster

v0.5.0

Published

A MapLibre GL JS plugin for visualizing local and remote raster datasets (GeoTIFF/COG)

Readme

maplibre-gl-raster

A MapLibre GL JS plugin for visualizing local and remote raster datasets (GeoTIFF / Cloud Optimized GeoTIFF) directly in the browser. No tile server required: COGs are read with HTTP range requests and rendered on the GPU through a deck.gl pipeline.

npm version License: MIT Open in CodeSandbox Open in StackBlitz

Features

  • Local and remote rasters - Load Cloud Optimized GeoTIFFs from any CORS-enabled URL, or drag-and-drop local GeoTIFF files
  • Multiple layers - Layer list with visibility toggles, reordering, zoom-to, and per-layer settings
  • GPU rendering pipeline - Band compositing, per-band rescale, 90+ colormaps, nodata filtering, linear/sqrt/log stretch, and gamma correction as deck.gl shader modules; parameter changes re-render without re-fetching tiles
  • Auto statistics - Per-band min/max and histograms sampled from COG overviews (or GDAL metadata), with draggable histogram handles for the rescale range
  • Pixel inspector - Toggle inspect mode and click the map to read the raw source values of every band of the selected layer at that location, shown in a popup (works for COGs in any CRS)
  • Colorbar legend - A standalone Colorbar control: gradient + tick labels for a named colormap (or custom colors), with configurable min/max, title, units, orientation, and position
  • Collapsible control - A compact 29x29 map button that expands into a floating panel
  • TypeScript + React - Full type definitions, a React wrapper component, and hooks
  • GeoLibre bundle output - Builds a zip with root plugin.json, bundled ESM, and CSS for GeoLibre Desktop

Installation

npm install maplibre-gl-raster

The plugin declares maplibre-gl, @deck.gl/*, and @luma.gl/* as peer dependencies (npm 7+ installs them automatically). This package is ESM-only.

Quick Start

Vanilla JavaScript/TypeScript

import maplibregl from "maplibre-gl";
import { RasterControl } from "maplibre-gl-raster";
import "maplibre-gl-raster/style.css";

const map = new maplibregl.Map({
  container: "map",
  style: "https://basemaps.cartocdn.com/gl/positron-gl-style/style.json",
  center: [0, 0],
  zoom: 2,
});

map.on("load", () => {
  const control = new RasterControl({ collapsed: false });
  map.addControl(control, "top-right");

  // Optionally add a raster programmatically (users can also paste a URL
  // or drop a local GeoTIFF file in the panel).
  control.addRaster("https://example.com/data/cog.tif");
});

React

import { useEffect, useRef, useState } from "react";
import maplibregl, { Map } from "maplibre-gl";
import { RasterControlReact, useRasterState } from "maplibre-gl-raster/react";
import "maplibre-gl-raster/style.css";

function App() {
  const mapContainer = useRef<HTMLDivElement>(null);
  const [map, setMap] = useState<Map | null>(null);
  const { state, toggle } = useRasterState();

  useEffect(() => {
    if (!mapContainer.current) return;

    const mapInstance = new maplibregl.Map({
      container: mapContainer.current,
      style: "https://basemaps.cartocdn.com/gl/positron-gl-style/style.json",
      center: [0, 0],
      zoom: 2,
    });

    mapInstance.on("load", () => setMap(mapInstance));

    return () => mapInstance.remove();
  }, []);

  return (
    <div style={{ width: "100%", height: "100vh" }}>
      <div ref={mapContainer} style={{ width: "100%", height: "100%" }} />
      {map && (
        <RasterControlReact
          map={map}
          collapsed={state.collapsed}
          onReady={(control) => control.addRaster("https://example.com/cog.tif")}
        />
      )}
    </div>
  );
}

API

RasterControl

The main control class implementing MapLibre's IControl interface.

Constructor Options

| Option | Type | Default | Description | | ------------- | --------- | ------------- | ------------------------------------------------------------------------- | | collapsed | boolean | true | Whether the panel starts collapsed (showing only the 29x29 toggle button) | | position | string | 'top-right' | Control position on the map | | title | string | 'Raster' | Title displayed in the header | | panelWidth | number | 360 | Width of the dropdown panel in pixels | | className | string | '' | Custom CSS class name | | interleaved | boolean | true | Render the deck.gl overlay interleaved with the basemap layers | | defaultUrl | string | '' | Prefills the Add data URL input (not loaded until the user clicks Load) | | autoLoad | boolean | false | Load defaultUrl automatically when the control is added to the map | | engine | RenderEngine | 'maplibre-gl-raster' | Initial rendering backend; switchable at runtime from the panel |

Raster Methods

  • addRaster(source, options?) - Add a raster from a COG URL (string) or a local GeoTIFF File; resolves with the layer id
  • removeRaster(id) - Remove a raster layer
  • getRaster(id) / getRasters() - Get layer snapshots (RasterLayerInfo)
  • setRasterState(id, patch) - Update visualization state (mode, bands, rescale, colormap, reversed, nodata, opacity, gamma, stretch, visible)
  • setVisible(id, visible) - Show / hide a layer
  • selectRaster(id | null) - Choose which layer the panel's settings edit
  • zoomToRaster(id) - Fit the map to a layer's bounds
  • reorderRaster(id, toIndex) - Move a layer in the draw order (0 = bottom)
  • getEngine() / setEngine(engine) - Read / switch the rendering backend (see Rendering engines)

addRaster options (AddRasterOptions): id, name, state (initial Partial<RasterLayerState> overrides), zoomTo (default true), and beforeId (insert the raster beneath an existing style layer, e.g. a label layer; also available as an input in the panel's Add data section).

Panel Methods

  • toggle() / expand() / collapse() - Control the panel
  • getState() / setState(state) - Control-level state (collapsed, panelWidth)
  • on(event, handler) / off(event, handler) - Event handlers
  • getMap() / getContainer() - Access the map / container

Events

  • collapse / expand / statechange - Panel state events
  • rasteradd / rasterremove / rasterchange / rasterselect - Layer lifecycle events (payload includes layerId)
  • error - Loading or rendering errors (payload includes error)

Rendering engines

The panel has a Rendering engine selector (and a matching engine option / getEngine() / setEngine() API) that switches the backend used for every layer:

  • maplibre-gl-raster (default) - the GPU pipeline described above: a deck.gl COGLayer on a shared MapboxOverlay. Parameter changes re-render without re-fetching tiles.
  • cog-tiler-wasm - a serverless CPU/WASM XYZ tiler (cog-tiler-wasm) wired to a MapLibre custom protocol. Tiles are rendered on the CPU and served as native MapLibre raster layers. The panel's settings (bands, rescale, colormap, curve, gamma, nodata, opacity) map directly onto its render parameters.

cog-tiler-wasm is an optional peer dependency, loaded lazily the first time the engine is selected, so it never enters the default bundle. To use it, install it alongside its own peers:

npm install cog-tiler-wasm whitebox-wasm proj4 "geotiff@^2.1" geotiff-geokeys-to-proj4

Pin geotiff to the 2.x line. cog-tiler-wasm reads a GeoTIFF's embedded color table from fileDirectory.ColorMap, which geotiff@3 resolves lazily (so paletted rasters like NLCD would otherwise render through a continuous colormap instead of their categorical colors).

// Start on the WASM engine, or switch at runtime:
const control = new RasterControl({ engine: "cog-tiler-wasm" });
// ...
control.setEngine("maplibre-gl-raster");

If the package is not installed, selecting the engine surfaces a load error via the error event; the default engine keeps working.

RasterLayerState

Per-layer visualization state, editable via the panel or setRasterState:

interface RasterLayerState {
  mode: "rgb" | "single"; // RGB composite or single band + colormap
  bands: number[]; // 1-indexed band selection
  rescale: [number, number][] | null; // per-channel min/max; null = auto (2-98%)
  colormap: string; // colormap name; "palette" = embedded color table
  reversed: boolean; // sample the named colormap back-to-front
  nodata: number | "off" | "auto"; // nodata handling
  opacity: number; // 0..1
  gamma: number; // power-law correction (1 = off)
  stretch: "linear" | "log" | "sqrt"; // curve applied after rescale
  visible: boolean;
  colorbar?: {
    // optional on-map legend for this single-band layer
    visible: boolean;
    title?: string;
    units?: string;
    orientation?: "horizontal" | "vertical";
    position?: "top-left" | "top-right" | "bottom-left" | "bottom-right";
  };
}

When a raster loads, the mode and bands are picked automatically (3+ bands → RGB [1, 2, 3]; otherwise single-band), and the rescale range defaults to the 2-98% percentile of sampled statistics. Single-band rasters use the image's embedded color table when it carries one (colormap: "palette") and grayscale otherwise. The first four bands are fetched as GPU textures, so band combinations among them re-render instantly without re-downloading tiles.

RasterControlReact

React wrapper component for RasterControl.

Props

All RasterControl options plus:

| Prop | Type | Description | | --------------- | ---------- | ------------------------------------------------------ | | map | Map | MapLibre GL map instance (required) | | onStateChange | function | Callback fired when the control state changes | | onReady | function | Receives the RasterControl instance after map attach |

useRasterState

Custom React hook for managing control state.

const {
  state, // Current state
  setState, // Update entire state
  setCollapsed, // Set collapsed state
  setPanelWidth, // Set panel width
  setData, // Set custom data
  reset, // Reset to initial state
  toggle, // Toggle collapsed state
} = useRasterState(initialState);

Colorbar

The settings panel has a "Show colorbar" toggle for single-band layers (with title, units, orientation, and position controls). Enabling it shows a legend on the map driven by that layer's colormap, reversed flag, and effective value range, and it follows rescale / colormap changes live. This is persisted per layer in RasterLayerState.colorbar.

You can also use the legend directly as a standalone control. Add it like any MapLibre control; it docks into a map corner and renders a gradient with tick labels. The ramp is sampled from the same colormap sprite the renderer uses, so a named colormap (and the reversed flag) matches the map exactly — or supply your own colors.

import { Colorbar } from "maplibre-gl-raster";

const colorbar = new Colorbar({
  colormap: "viridis", // or colors: ["#000", "#f00", "#ff0"]
  min: 0,
  max: 3000,
  title: "Elevation",
  units: "m",
  orientation: "horizontal", // or "vertical"
  position: "bottom-right",
  ticks: 5,
});
map.addControl(colorbar);

Keep it in sync with a single-band raster by updating it from the control's rasterchange event:

control.on("rasterchange", ({ layerId }) => {
  const info = layerId ? control.getRaster(layerId) : undefined;
  const range = info?.state.rescale?.[0]; // [min, max] when set explicitly
  // 'palette' uses the image's embedded table, not a named colormap.
  if (info && range && info.state.colormap !== "palette") {
    colorbar.update({
      colormap: info.state.colormap,
      reversed: info.state.reversed,
      min: range[0],
      max: range[1],
    });
  }
});

ColorbarOptions: colormap? (default "viridis"), colors? (custom ramp, overrides colormap), reversed?, min? / max? (default 0 / 1), title?, titleAlign? ("left" | "center" | "right"), units?, stretch? ("linear" | "log" | "sqrt" — spaces tick values to match the layer's stretch), orientation? ("horizontal" | "vertical", default "horizontal"), position? (map corner, default "bottom-right"), ticks? (count, default 5), tickValues? (explicit ticks), decimals? (fixed decimal places; omit for a compact auto format), barLength? / barThickness? (px), className?. Reconfigure live with colorbar.update(partial).

Utilities

The package also exports lower-level building blocks for advanced use:

  • loadGeoTIFF(url) - Open a (CORS-safe) GeoTIFF from a URL or blob URL
  • computeAutoStats(tiff, signal, onProgress?) - Per-band min/max + histograms
  • summarizeGeoTIFF(tiff) - Image / CRS / band / GDAL metadata summary
  • readBandNames(tiff) / percentileFromHistogram(stats, p)
  • COLORMAP_NAMES / COLORMAP_OPTIONS / colormapsPngUrl
  • sampleColormapStops(name, steps, reversed?) / loadColormapSprite() / isKnownColormap(name) - sample a colormap's colors in plain JS
  • autoRangeFor(stats) / statsForBand(autoStats, band) - resolve a band's effective rescale range
  • clamp, formatNumericValue, generateId, debounce, throttle, classNames

CORS requirements for remote COGs

Remote COGs must be served with CORS enabled (Access-Control-Allow-Origin). The loader includes a workaround for buckets that do not expose Content-Range via Access-Control-Expose-Headers, so most public S3/R2 buckets work out of the box.

Build a GeoLibre plugin zip

GeoLibre Desktop loads external plugins from an app data plugins/ directory. The zip must contain plugin.json at the root, plus a bundled ESM entry and optional CSS file.

npm install
npm run package:geolibre

This creates:

geolibre-plugin/maplibre-gl-raster-0.1.0.zip

The generated zip contains:

plugin.json
dist/index.js
dist/style.css

Copy the zip into GeoLibre Desktop's app data plugins/ directory and restart GeoLibre. On Linux with the default app identifier, that directory is usually:

~/.local/share/org.geolibre.desktop/plugins/

For the GeoLibre web app, serve the unpacked plugin with CORS enabled:

npm run package:geolibre
npm run serve:geolibre -- 8000

Then add this manifest URL in GeoLibre Settings > Plugins:

http://localhost:8000/plugin.json

Using python -m http.server for this cross-origin web app case is not enough because it does not send Access-Control-Allow-Origin.

Development

Setup

# Clone the repository
git clone https://github.com/opengeos/maplibre-gl-raster.git
cd maplibre-gl-raster

# Install dependencies
npm install

# Start development server
npm run dev

Scripts

| Script | Description | | -------------------------- | ---------------------------------------- | | npm run dev | Start development server | | npm run build | Build the library and GeoLibre bundle | | npm run build:lib | Build the standalone MapLibre library | | npm run build:geolibre | Build the GeoLibre ESM and CSS bundle | | npm run package:geolibre | Build and zip the GeoLibre plugin bundle | | npm run build:examples | Build examples for deployment | | npm run test | Run tests | | npm run test:ui | Run tests with UI | | npm run test:coverage | Run tests with coverage | | npm run lint | Lint the code | | npm run format | Format the code |

Project Structure

maplibre-gl-raster/
├── geolibre-plugin/
│   └── plugin.json          # GeoLibre external plugin manifest
├── scripts/
│   └── package-geolibre-plugin.mjs
├── src/
│   ├── index.ts              # Main entry point
│   ├── geolibre.ts           # GeoLibre plugin wrapper entry point
│   ├── react.ts              # React entry point
│   ├── index.css             # Root styles
│   └── lib/
│       ├── core/             # RasterControl, React wrapper, types
│       ├── raster/           # GeoTIFF loading, stats, GPU render pipeline
│       ├── state/            # RasterLayer model + LayerManager
│       ├── ui/               # Vanilla DOM panel components
│       ├── hooks/            # React hooks
│       ├── utils/            # Utility functions
│       └── styles/           # Component styles
├── tests/                    # Test files
├── examples/                 # Example applications
│   ├── basic/               # Vanilla JS example
│   └── react/               # React example
└── .github/workflows/        # CI/CD workflows

Docker

The examples can be run using Docker. The image is automatically built and published to GitHub Container Registry.

Pull and Run

# Pull the latest image
docker pull ghcr.io/opengeos/maplibre-gl-raster:latest

# Run the container
docker run -p 8080:80 ghcr.io/opengeos/maplibre-gl-raster:latest

Then open http://localhost:8080/maplibre-gl-raster/ in your browser to view the examples.

Build Locally

# Build the image
docker build -t maplibre-gl-raster .

# Run the container
docker run -p 8080:80 maplibre-gl-raster

Available Tags

| Tag | Description | | -------- | -------------------------------- | | latest | Latest release | | x.y.z | Specific version (e.g., 1.0.0) | | x.y | Minor version (e.g., 1.0) |

Publish to npm

npm login
npm whoami
npm publish --access public

Set up Trusted Publisher on npmjs.com

Credits

The rendering pipeline, GeoTIFF loading strategy, statistics sampling, and much of the visualization UX are ported from Source Cooperative's cog-viewer, built on their excellent @developmentseed/deck.gl-geotiff and @developmentseed/deck.gl-raster libraries.

License

MIT License - see LICENSE for details.