maplibre-copc-layer 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,124 @@
1
+ # maplibre-copc-layer
2
+
3
+ [![npm version](https://img.shields.io/npm/v/maplibre-copc-layer)](https://www.npmjs.com/package/maplibre-copc-layer)
4
+ [![license](https://img.shields.io/npm/l/maplibre-copc-layer)](https://github.com/spatialty-io/maplibre-copc-layer/blob/main/LICENSE)
5
+
6
+ Render massive point clouds on [MapLibre GL JS](https://maplibre.org/) — powered by [COPC](https://copc.io/) and [Three.js](https://threejs.org/).
7
+
8
+ Stream [Cloud-Optimized Point Cloud (COPC)](https://copc.io/) data directly into MapLibre as a custom layer. Only the tiles visible on screen are fetched and rendered, enabling smooth visualization of billion-point datasets in the browser.
9
+
10
+ ## Highlights
11
+
12
+ - **Streaming LOD** — Screen-space error (SSE) based level-of-detail fetches only what you see
13
+ - **Web Worker parsing** — COPC decoding and coordinate reprojection run off the main thread
14
+ - **LRU cache** — Configurable node count and memory limits keep the GPU lean
15
+ - **Eye-Dome Lighting** — EDL post-processing for depth perception without normals
16
+ - **Color modes** — RGB, height ramp, intensity, and flat white
17
+ - **Zero config** — Drop in a single `CopcLayer` class and go
18
+
19
+ ## Demo
20
+
21
+ **[Live Demo](https://maplibre-copc-layer.spatialty.workers.dev/?copc=https%3A%2F%2Fgsvrg.ipri.aist.go.jp%2F3ddb-pds%2Fcopc%2F114112.copc.laz#17.95/35.657894/139.746455/-83.4/60)**
22
+
23
+ ## Install
24
+
25
+ ```bash
26
+ npm install maplibre-copc-layer
27
+ ```
28
+
29
+ Peer dependencies:
30
+
31
+ ```bash
32
+ npm install maplibre-gl three
33
+ ```
34
+
35
+ ## Quick Start
36
+
37
+ ```ts
38
+ import maplibregl from 'maplibre-gl';
39
+ import { CopcLayer } from 'maplibre-copc-layer';
40
+
41
+ const map = new maplibregl.Map({
42
+ container: 'map',
43
+ style: 'https://demotiles.maplibre.org/style.json',
44
+ center: [139.7, 35.7],
45
+ zoom: 14,
46
+ });
47
+
48
+ const layer = new CopcLayer('https://example.com/pointcloud.copc.laz', {
49
+ colorMode: 'rgb',
50
+ pointSize: 4,
51
+ enableEDL: true,
52
+ onInitialized: ({ center }) => map.flyTo({ center, zoom: 16 }),
53
+ });
54
+
55
+ map.on('load', () => map.addLayer(layer));
56
+ ```
57
+
58
+ ## API
59
+
60
+ ### `CopcLayer`
61
+
62
+ ```ts
63
+ new CopcLayer(url: string, options?: CopcLayerOptions, layerId?: string)
64
+ ```
65
+
66
+ #### Options
67
+
68
+ | Option | Type | Default | Description |
69
+ |---|---|---|---|
70
+ | `pointSize` | `number` | `6` | Point size in pixels |
71
+ | `colorMode` | `'rgb' \| 'height' \| 'intensity' \| 'white'` | `'rgb'` | Coloring mode |
72
+ | `sseThreshold` | `number` | `8` | SSE threshold for LOD — lower values load more detail |
73
+ | `depthTest` | `boolean` | `true` | Enable depth testing |
74
+ | `maxCacheSize` | `number` | `100` | Max cached nodes |
75
+ | `maxCacheMemory` | `number` | `104857600` | Max cache memory in bytes (100 MB) |
76
+ | `enableEDL` | `boolean` | `false` | Enable Eye-Dome Lighting |
77
+ | `edlStrength` | `number` | `0.4` | EDL effect strength |
78
+ | `edlRadius` | `number` | `1.5` | EDL sampling radius |
79
+ | `wasmPath` | `string` | `undefined` | Custom path to `laz-perf.wasm` |
80
+ | `debug` | `boolean` | `false` | Enable debug logging |
81
+ | `onInitialized` | `(msg) => void` | — | Called with `{ nodeCount, center }` after COPC header loads |
82
+
83
+ #### Methods
84
+
85
+ | Method | Description |
86
+ |---|---|
87
+ | `setPointSize(size)` | Update point size |
88
+ | `setSseThreshold(threshold)` | Update SSE threshold |
89
+ | `setDepthTest(enabled)` | Toggle depth testing |
90
+ | `setEDLEnabled(enabled)` | Toggle Eye-Dome Lighting |
91
+ | `updateEDLParameters({ strength?, radius? })` | Update EDL parameters |
92
+ | `updateCacheConfig(config)` | Update cache limits at runtime |
93
+ | `clearCache()` | Clear all cached nodes |
94
+ | `getPointSize()` | Get current point size |
95
+ | `getColorMode()` | Get current color mode |
96
+ | `getSseThreshold()` | Get current SSE threshold |
97
+ | `getDepthTest()` | Get depth test state |
98
+ | `getEDLParameters()` | Get EDL parameters |
99
+ | `getOptions()` | Get all current options |
100
+ | `isLoading()` | Whether data is currently being fetched |
101
+ | `getNodeStats()` | Returns `{ loaded, visible }` node counts |
102
+
103
+ ### `GlobeControl`
104
+
105
+ A MapLibre `IControl` that toggles between Mercator and Globe projections.
106
+
107
+ ```ts
108
+ import { GlobeControl } from 'maplibre-copc-layer';
109
+
110
+ map.addControl(new GlobeControl());
111
+ ```
112
+
113
+ ## Development
114
+
115
+ ```bash
116
+ pnpm install
117
+ pnpm dev # Dev server with demo app
118
+ pnpm test # Run tests
119
+ pnpm build # Build library
120
+ ```
121
+
122
+ ## License
123
+
124
+ MIT
@@ -0,0 +1,176 @@
1
+ import maplibregl from "maplibre-gl";
2
+ import * as THREE from "three";
3
+
4
+ //#region src/cache-manager.d.ts
5
+ interface CachedNodeData {
6
+ nodeId: string;
7
+ positions: Float64Array;
8
+ colors: Float32Array;
9
+ pointCount: number;
10
+ geometry?: THREE.BufferGeometry;
11
+ points?: THREE.Points;
12
+ materialConfig: {
13
+ colorMode: string;
14
+ pointSize: number;
15
+ depthTest: boolean;
16
+ };
17
+ lastAccessed: number;
18
+ sizeBytes: number;
19
+ }
20
+ interface CacheManagerOptions {
21
+ maxNodes?: number;
22
+ maxMemoryBytes?: number;
23
+ debug?: boolean;
24
+ }
25
+ declare class CacheManager {
26
+ private cache;
27
+ private memoryUsage;
28
+ private options;
29
+ constructor(options?: CacheManagerOptions);
30
+ get(nodeId: string): CachedNodeData | null;
31
+ set(nodeData: CachedNodeData, protectedNodes?: Set<string>): void;
32
+ has(nodeId: string): boolean;
33
+ delete(nodeId: string): boolean;
34
+ clear(): void;
35
+ updateOptions(newOptions: Partial<CacheManagerOptions>, protectedNodes?: Set<string>): void;
36
+ getCachedNodeIds(): string[];
37
+ size(): number;
38
+ static estimateNodeSize(positions: Float64Array | Float32Array, colors: Float32Array): number;
39
+ static createNodeData(nodeId: string, positions: Float64Array, colors: Float32Array, materialConfig: CachedNodeData['materialConfig']): CachedNodeData;
40
+ private ensureCacheLimits;
41
+ private disposeNodeResources;
42
+ private formatBytes;
43
+ private log;
44
+ }
45
+ //#endregion
46
+ //#region src/copclayer.d.ts
47
+ type ColorMode = 'rgb' | 'height' | 'intensity' | 'white';
48
+ interface CopcLayerOptions {
49
+ pointSize?: number;
50
+ colorMode?: ColorMode;
51
+ maxCacheSize?: number;
52
+ sseThreshold?: number;
53
+ depthTest?: boolean;
54
+ maxCacheMemory?: number;
55
+ debug?: boolean;
56
+ enableEDL?: boolean;
57
+ edlStrength?: number;
58
+ edlRadius?: number;
59
+ /** Path to laz-perf.wasm file for LAZ decompression */
60
+ wasmPath?: string;
61
+ onInitialized?: (message: {
62
+ nodeCount: number;
63
+ center: [number, number];
64
+ }) => void;
65
+ }
66
+ interface NodeStats {
67
+ loaded: number;
68
+ visible: number;
69
+ }
70
+ declare class CopcLayer implements maplibregl.CustomLayerInterface {
71
+ readonly id: string;
72
+ readonly type: 'custom';
73
+ readonly renderingMode: '3d';
74
+ readonly url: string;
75
+ map?: maplibregl.Map;
76
+ readonly camera: THREE.Camera;
77
+ readonly scene: THREE.Scene;
78
+ renderer?: THREE.WebGLRenderer;
79
+ readonly worker: Worker;
80
+ readonly cacheManager: CacheManager;
81
+ private readonly options;
82
+ private visibleNodes;
83
+ private workerInitialized;
84
+ private pendingRequests;
85
+ private requestQueue;
86
+ private lastCameraPosition;
87
+ private sceneCenter;
88
+ private colorTarget?;
89
+ private depthTarget?;
90
+ private edlMaterial?;
91
+ private edlQuadScene?;
92
+ private edlQuadCamera?;
93
+ private readonly _tempMatrix1;
94
+ private readonly _tempMatrix2;
95
+ private _lastEdlWidth;
96
+ private _lastEdlHeight;
97
+ private _lastUpdatePointsTime;
98
+ constructor(url: string, options?: CopcLayerOptions, layerId?: string);
99
+ private setupWorkerMessageHandlers;
100
+ private handleNodeLoaded;
101
+ private updateVisibleNodes;
102
+ private requestNodeData;
103
+ private needsMaterialUpdate;
104
+ private updateNodeMaterial;
105
+ private rebuildAllMaterials;
106
+ private removeFromRequestQueue;
107
+ private cancelAllPendingRequests;
108
+ private prioritizeNodeRequests;
109
+ onAdd(map: maplibregl.Map, gl: WebGLRenderingContext): Promise<void>;
110
+ updateCacheConfig(config: Partial<CopcLayerOptions>): void;
111
+ setPointSize(size: number): void;
112
+ setSseThreshold(threshold: number): void;
113
+ setDepthTest(enabled: boolean): void;
114
+ /**
115
+ * Compute camera altitude in meters above sea level.
116
+ * MapLibre's getCameraAltitude() returns NaN in Globe mode because
117
+ * the Globe transform's internal _pixelPerMeter is never initialized.
118
+ * We replicate the formula using publicly accessible values.
119
+ */
120
+ private computeCameraAltitude;
121
+ private updatePoints;
122
+ render(_gl: WebGLRenderingContext, options: maplibregl.CustomRenderMethodInput): void;
123
+ private shouldUpdateSceneCenter;
124
+ onRemove(_map: maplibregl.Map, _gl: WebGLRenderingContext): void;
125
+ private setupEDL;
126
+ private updateEDLSize;
127
+ private createPointMaterial;
128
+ getPointSize(): number;
129
+ getColorMode(): ColorMode;
130
+ getSseThreshold(): number;
131
+ getDepthTest(): boolean;
132
+ getOptions(): Readonly<CopcLayerOptions>;
133
+ isLoading(): boolean;
134
+ getNodeStats(): NodeStats;
135
+ clearCache(): void;
136
+ setEDLEnabled(enabled: boolean): void;
137
+ updateEDLParameters(params: {
138
+ strength?: number;
139
+ radius?: number;
140
+ }): void;
141
+ getEDLParameters(): {
142
+ enabled: boolean;
143
+ strength: number;
144
+ radius: number;
145
+ };
146
+ }
147
+ //#endregion
148
+ //#region src/globe-control.d.ts
149
+ /**
150
+ * A MapLibre control that toggles between Mercator and Globe projections.
151
+ */
152
+ declare class GlobeControl implements maplibregl.IControl {
153
+ private map?;
154
+ private container?;
155
+ private button?;
156
+ private isGlobe;
157
+ onAdd(map: maplibregl.Map): HTMLElement;
158
+ onRemove(): void;
159
+ getDefaultPosition(): maplibregl.ControlPosition;
160
+ private toggle;
161
+ private updateIcon;
162
+ }
163
+ //#endregion
164
+ //#region src/worker/sse.d.ts
165
+ type Vec3 = [number, number, number];
166
+ /**
167
+ * Calculates Screen Space Error (SSE) for a point cloud node.
168
+ * Higher SSE = more visible error = should render with higher detail.
169
+ *
170
+ * @param maxDistance - Optional maximum effective distance. When the camera is
171
+ * extremely far away (e.g. Globe View at low zoom), the distance is capped
172
+ * to this value so that SSE does not approach 0.
173
+ */
174
+ declare function computeScreenSpaceError(cameraCenter: Vec3, center: Vec3, fov: number, geometricError: number, screenHeight: number, maxDistance?: number): number;
175
+ //#endregion
176
+ export { CacheManager, type CacheManagerOptions, type CachedNodeData, type ColorMode, CopcLayer, type CopcLayerOptions, GlobeControl, type NodeStats, type Vec3, computeScreenSpaceError };