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-copc-layer

v0.3.0

Published

A library for loading and rendering Cloud-Optimized Point Cloud (COPC) data in MapLibre GL JS using Three.js.

Readme

maplibre-copc-layer

npm version license

A MapLibre GL JS custom layer for streaming and rendering Cloud-Optimized Point Cloud (COPC) data, powered by Three.js.

The layer reads COPC hierarchy pages and LAZ chunks with HTTP byte-range requests. A view-frustum test skips off-screen branches, screen-space error selects an octree level of detail, and node requests are limited to six at a time.

Live Demo

Features

  • Streaming LOD — Frustum culling and screen-space error select point density as the camera moves
  • Web Worker — COPC decoding and coordinate reprojection run off the main thread
  • LRU cache — Configurable node and memory budgets, with visible nodes pinned
  • Ambient Occlusion — SSAO post-processing for depth perception
  • Color modes — RGB, height ramp, intensity, classification, and white
  • Custom color expressions — User-defined linear/discrete color ramps for height and intensity
  • Filtering — By classification, intensity range, or bounding box (WGS84)

Install

npm install maplibre-copc-layer maplibre-gl three

Usage

import maplibregl from 'maplibre-gl';
import { CopcLayer } from 'maplibre-copc-layer';

const map = new maplibregl.Map({
  container: 'map',
  style: 'https://demotiles.maplibre.org/style.json',
  center: [139.7, 35.7],
  zoom: 14,
});

const layer = new CopcLayer('https://example.com/pointcloud.copc.laz', {
  colorMode: 'rgb',
  pointSize: 4,
  enableSSAO: true,
  onInitialized: ({ bounds }) => map.flyTo({
    center: [(bounds.minx + bounds.maxx) / 2, (bounds.miny + bounds.maxy) / 2],
    zoom: 16,
  }),
});

map.on('load', () => map.addLayer(layer));

API

new CopcLayer(url, options?, layerId?)

| Option | Type | Default | Description | |---|---|---|---| | pointSize | number | 6 | Point size in pixels | | colorMode | 'rgb' \| 'height' \| 'intensity' \| 'classification' \| 'white' | 'rgb' | Coloring mode | | heightColor | ColorExpression | auto | Color ramp for height mode. Default: blue→yellow→red across data bounds | | intensityColor | ColorExpression | auto | Color ramp for intensity mode. Default: black→white (0–1) | | classificationColors | Record<number, RGBColor> | ASPRS defaults | Classification code colors (0–1 RGB) | | filter | PointFilter | {} | Filter points by classification, intensity range, or bounding box | | alwaysShowRoot | boolean | false | Bypass frustum culling for the root node; visible ancestors are rendered with their descendants regardless of this setting | | sseThreshold | number | 8 | SSE threshold for LOD — lower loads more detail | | depthTest | boolean | true | Enable depth testing | | maxCacheSize | number | Infinity | Optional node-count limit; cached bytes are the default constraint | | maxCacheMemory | number | 268435456 | Byte budget for cached nodes (256 MB), enforced when admitting new nodes; the active view may exceed it | | enableSSAO | boolean | false | Enable Screen Space Ambient Occlusion | | ssaoStrength | number | 1.0 | SSAO effect strength | | ssaoRadius | number | 8.0 | SSAO sampling radius in pixels | | debug | boolean | false | Enable debug logging | | onInitialized | (msg) => void | — | Called with { nodeCount, bounds } after the initial hierarchy page loads. nodeCount counts nodes discovered so far; bounds contains minx/maxx/miny/maxy/minz/maxz in WGS84 | | onError | (message) => void | — | Called when initialization, a hierarchy page, or a point chunk fails to load |

Methods

| Method | Description | |---|---| | setPointSize(size) | Update point size | | setColorMode(mode) | Switch color mode without reloading data | | setHeightColor(expr) | Update height color expression (instant, no reload) | | setIntensityColor(expr) | Update intensity color expression (instant, no reload) | | setClassificationColors(colors) | Update classification colors (instant, no reload) | | setSseThreshold(threshold) | Update SSE threshold | | setDepthTest(enabled) | Toggle depth testing | | setSSAOEnabled(enabled) | Toggle Screen Space Ambient Occlusion | | setSSAOParameters({ strength?, radius? }) | Update SSAO parameters | | setFilter(filter) | Update point filter (classification / intensity / bbox) | | getFilter() | Get current point filter | | setCacheConfig(config) | Update cache limits at runtime | | clearCache() | Clear all cached nodes | | isLoading() | Whether data is currently being fetched | | getNodeStats() | Returns { loaded, visible } node counts |

Examples

Height-based coloring with custom color ramp

ColorExpression uses a MapLibre Style-like syntax: ["linear", stop, color, stop, color, ...] or ["discrete", ...].

const layer = new CopcLayer('https://example.com/pointcloud.copc.laz', {
  colorMode: 'height',
  // Linear interpolation: blue at 0m, green at 50m, red at 100m
  heightColor: ['linear', 0, [0, 0, 1], 50, [0, 1, 0], 100, [1, 0, 0]],
});

Discrete height coloring

const layer = new CopcLayer('https://example.com/pointcloud.copc.laz', {
  colorMode: 'height',
  // Step function: blue below 50m, green 50-100m, red above 100m
  heightColor: ['discrete', 0, [0, 0, 1], 50, [0, 1, 0], 100, [1, 0, 0]],
});

Custom intensity coloring

const layer = new CopcLayer('https://example.com/pointcloud.copc.laz', {
  colorMode: 'intensity',
  // Intensity values are normalized 0-1
  intensityColor: ['linear', 0, [0, 0, 0.2], 0.5, [1, 1, 0], 1, [1, 0, 0]],
});

Updating colors at runtime (no data reload)

// Switch color mode instantly
layer.setColorMode('height');

// Update height color ramp — reflected immediately
layer.setHeightColor(['linear', 0, [1, 1, 1], 200, [1, 0, 0]]);

// Update classification colors
layer.setClassificationColors({
  2: [0.4, 0.2, 0.1],  // Ground: brown
  6: [0.8, 0.1, 0.1],  // Building: red
});

Filtering points

const layer = new CopcLayer('https://example.com/pointcloud.copc.laz', {
  filter: {
    // Show only ground and buildings
    classification: new Set([2, 6]),
    // Intensity range (0-1)
    intensityRange: [0.1, 0.9],
    // Bounding box in WGS84
    bbox: { minx: 139.7, maxx: 139.8, miny: 35.6, maxy: 35.7 },
  },
});

// Update filter at runtime
layer.setFilter({
  classification: new Set([2, 3, 4, 5, 6]),
});

Ambient Occlusion

const layer = new CopcLayer('https://example.com/pointcloud.copc.laz', {
  enableSSAO: true,
  ssaoStrength: 1.0,
  ssaoRadius: 8.0,
});

Development

git submodule update --init --recursive
pnpm install
pnpm dev       # Dev server with demo app
pnpm test      # Run tests
pnpm build     # Build library

The Cloudflare Workers demo uses Static Assets from demo/. In Workers Builds, use pnpm build:demo as the build command. npx wrangler versions upload uploads a preview version; npx wrangler deploy publishes it to production.

The package has no copc or laz-perf npm dependency. COPC metadata and point records are read by the worker; LAZ chunks are decoded by the vendored laz-perf WebAssembly build, which is bundled into dist. Building from source requires the Git submodule above. The COPC server must support HTTP 206 Partial Content and allow cross-origin Range requests. Point formats 6, 7, and 8 are supported.

The optional live integration test reads the public demo file and needs network access:

RUN_LIVE_COPC=1 pnpm exec vitest run tests/live-copc.test.ts

Third-Party Notices

This project bundles laz-perf (Apache License 2.0).

License

Apache-2.0