@canetoad/mrd
v0.3.1
Published
Multi Raster Decoder (MRD): browser-native raster decoding for ECW, JPEG 2000, TIFF/BigTIFF, and large range-streamed imagery.
Downloads
606
Maintainers
Readme
@canetoad/mrd
Summary
@canetoad/mrd is the public Multi Raster Decoder package for opening large
geospatial rasters from local files, byte buffers, or strict HTTP range
sources. It provides a single openRaster entry point plus direct
format-specific entries backed by clean-room Rust/WebAssembly decoders.
@canetoad/mrd is the only public npm package from this repository; the
format implementations are exposed as root namespaces and subpath exports.
Features
- Auto-detects supported raster families from file signatures.
- Opens ECW, JPEG 2000, TIFF, and BigTIFF through format-specific WASM cores.
- Supports streamable JP2 lower-LOD sparse region reconstruction in the Rust/WASM path for reversible 5/3 and irreversible 9/7 codestreams, with small-source full-object overview fallback when sparse plans are too costly.
- Exposes
ecw,jp2, andtiffnamespaces from the package root. - Keeps unused format engines unloaded when importing the root package.
- Enforces strict HTTP Range responses for sparse remote reads.
- Exposes byte-range planning helpers for custom progressive loading.
- Exposes a browser tile session runtime for queueing, cancellation, worker orchestration, MapLibre protocol integration, and bounded tile caches.
- Ships ESM JavaScript, TypeScript declarations, WASM assets, README, provenance notes, changelog, and PolyForm Noncommercial license text.
Quick Start
npm install @canetoad/mrdimport { openRaster } from "@canetoad/mrd";
const result = await openRaster(fileOrUrl);
const metadata = result.handle.metadata();Using The Library
Auto-detect and open
import { openRaster } from "@canetoad/mrd";
const result = await openRaster("https://example.com/scene.tif");
console.log(result.format);
console.log(result.handle.metadata());openRaster accepts a URL string, a File, a Blob, a Uint8Array, or a
custom RasterByteSource with size() and readRange(offset, size). The
package probes the first bytes, detects the raster family, then loads the
matching format subpath.
Source handling is format-aware:
| Source kind | ECW | JP2/J2K | TIFF/BigTIFF |
| --- | --- | --- | --- |
| URL string | Sparse HTTP range reads | Sparse header/packet reads with bounded fallback | Sparse HTTP range reads |
| File / Blob | Sparse worker reads | Sparse local reads where supported | Sparse local reads |
| Custom RasterByteSource | Sparse worker reads | Sparse header/packet reads where supported | Sparse custom reads |
| Uint8Array | In-memory full-buffer open | In-memory/header open | In-memory sparse wrapper |
Import a known format directly
import { jp2 } from "@canetoad/mrd";
import { openEcw } from "@canetoad/mrd/ecw";
import { openJp2 } from "@canetoad/mrd/jp2";
import { openTiff } from "@canetoad/mrd/tiff";
const image = await jp2.openJp2(source);Use direct imports when the caller already knows the input format or wants to keep bundle analysis explicit. The ECW entry uses browser Worker-backed WASM; JP2 and TIFF can also run in Node-style environments that support the package runtime requirements.
Read remote rasters safely
import { httpByteSource, openRaster } from "@canetoad/mrd";
const source = httpByteSource("https://example.com/scene.tif");
const { handle } = await openRaster(source);httpByteSource, ECW URL streaming, and the private helper packages share the
same strict range implementation. They reject range requests when a server
ignores the Range header, returns a mismatched Content-Range, or reports an
inconsistent Content-Length, and they throw InvalidRangeResponseError with a
stable reason field for UI branching. Strict range fetches default to
cache: "no-store" and cancel invalid response bodies before throwing, so a
200 OK full-object response is not accepted as a fallback for sparse reads.
For direct browser access to cross-origin buckets, the bucket CORS policy must
allow GET, HEAD, and request header Range, and expose response headers
Content-Range, Accept-Ranges, Content-Length, ETag, and
Last-Modified.
Handle decoder errors
import {
FormatUnsupportedError,
InvalidRangeResponseError,
MissingBytesError,
openRaster,
} from "@canetoad/mrd";
try {
await openRaster(source);
} catch (error) {
if (error instanceof MissingBytesError) {
await fetchAndFeed(error.offset, error.size);
} else if (error instanceof InvalidRangeResponseError) {
console.warn(error.reason);
} else if (error instanceof FormatUnsupportedError) {
console.warn(error.headerBytes);
} else {
throw error;
}
}Plan byte ranges
import {
coalesceByteRanges,
mapByteRangesWithConcurrency,
planMissingByteRanges,
} from "@canetoad/mrd";The byte-range planning helpers are public so applications can build custom byte sources, preload strategies, and progressive fetch queues around the same range model used internally by the decoders.
Render MapLibre tiles
import {
installMrdMapLibreRasterLayer,
openPrimaryLocalRasterSession,
openRasterSession,
} from "@canetoad/mrd";
const openSession =
sourceDescriptor.kind === "file"
? openPrimaryLocalRasterSession
: openRasterSession;
const session = await openSession({
sourceDescriptor,
raster,
getRenderOptions,
});
const rasterLayer = installMrdMapLibreRasterLayer(maplibregl, map, session, {
sourceId: "raster",
layerId: "raster",
bounds,
maxzoom,
});The session owns viewport-priority queueing, stale-tile cancellation, bounded
memory and IndexedDB tile caches, overview sharing, and activity snapshots. The
default package worker owns the hard-won JP2 and GeoTIFF streaming heuristics,
overview fallback limits, projection handling, and worker preemption policy.
Applications can still pass a custom workerFactory or renderTileOnMain for
special renderers, but the standard MapLibre path does not require copying demo
worker code.
GeoTIFF sampling policy is source-aware. Local File, Blob, and byte sources
can use refined sampled TIFF tiles in Full quality mode when a broad source
window is too large for exact decode, because the cost stays in local
worker/disk reads and should remain off the main thread. Remote no-overview
GeoTIFFs should remain bounded: integrations should keep those broad sampled
tiles in draft quality unless the source has COG/internal overview structure or
the application deliberately accepts the larger remote range plan.
Use openPrimaryLocalRasterSession for local GeoTIFF/JP2 application layers
that should follow the demo-proven primary local runtime contract. It wraps the
package session API, requires a local replayable source descriptor, and reports
refined local TIFF sampling as geotiff-sampled for compatibility with the
demo proof surface.
The session contract exports named TypeScript shapes for
RasterSourceDescriptor, RasterRuntimeMetadata, RasterLngLatBounds,
RasterProjection, RasterRenderOptions, and RasterSourceInvalidatedEvent.
Use those types when bridging an application importer into
openRasterSession; they describe the replayable source, placement metadata,
render state, and invalidation callbacks the package runtime expects.
installMrdMapLibreRasterLayer installs the MRD protocol, source, and raster
layer, then forwards map viewport changes into the session. That viewport bridge
lets the package cancel queued tiles and preempt workers that are still fetching
tiles no longer needed after the user pans or zooms.
For JP2 sources, the runtime policy is intentionally hybrid. Sparse region and
lower-LOD reconstruction stay on the Rust/WASM path for streamable reversible
5/3 and irreversible 9/7 codestreams. Broad lower-LOD JP2 tiles try the Rust
packet-index streamer first, fetching only the planned packet ranges needed for
the requested region when the codestream exposes enough packet information.
Full-object decoded overview fallback remains available for small sources by
default, currently capped at 64 MB, or for integrations that explicitly raise
jp2OverviewMaxSourceBytes / jp2OverviewSourceByteCap. This keeps
interactive opens stable without asking application callers to tune JP2 packet
budgets, while avoiding surprise full-source downloads for streamable JP2s.
Runtime And Publishing
@canetoad/mrd is ESM-only and targets modern browsers, Node 20+, and edge
runtimes with fetch and WebAssembly. Browser-only engines require Worker
support. The source repository is private. The npm package is published publicly
with provenance disabled for the private repository and contains only the built
runtime artifacts, TypeScript declarations, license, changelog, README, and
provenance notes listed in package.json. Source maps, source directories, Rust
sources, tests, and examples are not part of the published package.
License
This software is licensed under the PolyForm Noncommercial License 1.0.0.
You may use, copy, modify, and distribute it for non-commercial purposes only, subject to the license terms and required attribution notices.
Commercial use requires a separate written license from Toad AI Pty Ltd. See
LICENSE and PROVENANCE.md for license terms and clean-room provenance
notes.
