Main class for loading and rendering COPC files in CesiumJS.
new COPCPointCloudProvider(options: COPCProviderOptions)interface COPCProviderOptions {
/** URL to the COPC file (required) */
url: string;
/** Color rendering mode (default: 'rgb') */
colorMode?: 'rgb' | 'intensity' | 'classification' | 'elevation';
/** Point size in pixels (default: 2) */
pointSize?: number;
/**
* Maximum memory usage in MB (default: 512) for the LRU cache of decoded
* data from nodes that have scrolled out of view (so they can reappear
* without a re-fetch/re-decode). Nodes currently visible are never
* evicted regardless of this budget - it only bounds the retained,
* off-screen backlog.
*/
maximumMemoryUsage?: number;
/** Maximum screen space error for LOD (default: 16) */
maximumScreenSpaceError?: number;
/** Skip LOD levels for debugging (default: 0) */
skipLevels?: number;
/**
* URL of the LAZ decoder Web Worker script
* (src/workers/laz-decoder.worker.ts). When set, point data is decoded
* off the main thread. Left unset, decoding runs on the main thread.
*/
workerUrl?: string | URL;
/** Number of decoder workers to spawn when workerUrl is set (default 4) */
workerPoolSize?: number;
}Initialize the COPC provider. Must be called before adding to scene.
const provider = new COPCPointCloudProvider({ url: 'data.copc.laz' });
await provider.initialize();Load and render the root node, and set up the octree traversal state used
by update(). Required before adding the provider's primitive to the scene.
await provider.loadRootNode();Returns the PrimitiveCollection to add to the scene
(viewer.scene.primitives.add(provider.getPrimitive())). Individual node
primitives are added/removed from this collection internally as update()
streams nodes in and out.
Drives camera-based LOD selection and frustum culling for one frame -
streams new nodes in, evicts nodes no longer needed. Not called
automatically: the provider is not itself a Cesium primitive, so you must
call it yourself, e.g. from viewer.scene.postRender:
viewer.scene.postRender.addEventListener(() => {
provider.update(viewer.scene.frameState);
});Update the rendered point size. Already-rendered nodes are rebuilt from their already-decoded data (no re-fetch/re-decode).
provider.setPointSize(4);Update the LOD threshold. Takes effect on the next update() call.
provider.setMaximumScreenSpaceError(8); // more detail, more requestsClean up and release all resources.
provider.destroy();Check if the provider has been destroyed.
Current loading/rendering stats, for HUD/debug display. cachedNodeCount/
cachedMemoryMB reflect the off-screen LRU cache (see maximumMemoryUsage),
not what's currently rendered.
import { Viewer } from 'cesium';
import { COPCPointCloudProvider } from 'copc-cesium';
const viewer = new Viewer('cesiumContainer');
const copcLayer = new COPCPointCloudProvider({
url: 'https://example.com/pointcloud.copc.laz',
colorMode: 'rgb',
pointSize: 2
});
await copcLayer.initialize();
await copcLayer.loadRootNode();
viewer.scene.primitives.add(copcLayer.getPrimitive());
// Drive camera-based LOD/culling every frame
viewer.scene.postRender.addEventListener(() => {
copcLayer.update(viewer.scene.frameState);
});const copcLayer = new COPCPointCloudProvider({
url: 'https://example.com/terrain.copc.laz',
colorMode: 'elevation', // Color by elevation
pointSize: 3
});const copcLayer = new COPCPointCloudProvider({
url: 'https://example.com/large-dataset.copc.laz',
maximumScreenSpaceError: 8 // lower = more detail, more requests
});const copcLayer = new COPCPointCloudProvider({
url: 'https://example.com/large-dataset.copc.laz',
workerUrl: new URL('../src/workers/laz-decoder.worker.ts', import.meta.url),
workerPoolSize: 4
});Display point cloud with original RGB colors from the COPC file.
Display points based on intensity values (grayscale).
Color points based on ASPRS classification codes:
- Ground (brown)
- Vegetation (green shades)
- Buildings (red)
- Water (blue)
- etc.
Color points based on Z coordinate (blue = low, red = high).
maximumMemoryUsagebounds the LRU cache of off-screen nodes' decoded data (kept around so revisiting them skips network/decode). It does not bound what's currently visible - a wide view with many simultaneously-visible nodes can still use more memory than this.- Lower it on memory-constrained devices; raise it to make camera back-and-forth over the same area cheaper.
maximumScreenSpaceErrorcontrols detail level- Lower values = more detail, more requests
- Higher values = less detail, fewer requests
- Default (16) is good balance for most cases
- Adjust
pointSizebased on point density - Dense clouds: smaller points (1-2 pixels)
- Sparse clouds: larger points (3-5 pixels)
- Modern browsers with WebGL support
- HTTP Range Request support required
- Web Workers support required only if you opt into
workerUrl
try {
const provider = new COPCPointCloudProvider({ url: 'data.copc.laz' });
await provider.initialize();
} catch (error) {
console.error('Failed to load COPC:', error);
}Common errors:
- Network errors (404, CORS)
- Invalid COPC file format
- Unsupported coordinate systems
- Memory exhaustion
The library uses HTTP Range Requests for efficient streaming (RangeRequestManager). Requests are:
- Prioritized by node depth (deeper/more-detailed nodes first)
- Limited to 6 concurrent requests by default
- Queued (not dropped) once the concurrency limit is reached
- Cancellable via AbortController, including while still queued
fetchMergedRanges() (merging adjacent byte ranges into one request) exists
on RangeRequestManager but isn't wired into the loading path yet - each
node is currently fetched as its own request.
Supports automatic transformation from:
- Korean coordinate systems (EPSG:5186, 5187, 5185)
- Web Mercator (EPSG:3857)
- Any projection defined in WKT VLR
Transforms to WGS84/ECEF for Cesium rendering.
Off by default. Set workerUrl to move LAZ decoding into Web Worker threads
(4 by default, via workerPoolSize) and keep the main thread free for
camera interaction. See the workerUrl option above and demo/autzen.html
for a working example.
MIT