@ifc-lite/pointcloud 0.11.0 → 0.12.0

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.
Files changed (74) hide show
  1. package/README.md +72 -0
  2. package/dist/copc/copc-hierarchy.d.ts +102 -0
  3. package/dist/copc/copc-hierarchy.d.ts.map +1 -0
  4. package/dist/copc/copc-hierarchy.js +210 -0
  5. package/dist/copc/copc-hierarchy.js.map +1 -0
  6. package/dist/copc/copc-info.d.ts +52 -0
  7. package/dist/copc/copc-info.d.ts.map +1 -0
  8. package/dist/copc/copc-info.js +147 -0
  9. package/dist/copc/copc-info.js.map +1 -0
  10. package/dist/copc/copc-reader.d.ts +60 -0
  11. package/dist/copc/copc-reader.d.ts.map +1 -0
  12. package/dist/copc/copc-reader.js +138 -0
  13. package/dist/copc/copc-reader.js.map +1 -0
  14. package/dist/copc/copc-worker-client.d.ts +37 -0
  15. package/dist/copc/copc-worker-client.d.ts.map +1 -0
  16. package/dist/copc/copc-worker-client.js +154 -0
  17. package/dist/copc/copc-worker-client.js.map +1 -0
  18. package/dist/copc/copc-worker-host.d.ts +11 -0
  19. package/dist/copc/copc-worker-host.d.ts.map +1 -0
  20. package/dist/copc/copc-worker-host.js +73 -0
  21. package/dist/copc/copc-worker-host.js.map +1 -0
  22. package/dist/formats/las.d.ts +1 -0
  23. package/dist/formats/las.d.ts.map +1 -1
  24. package/dist/formats/las.js +1 -1
  25. package/dist/formats/las.js.map +1 -1
  26. package/dist/index.d.ts +11 -1
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +10 -1
  29. package/dist/index.js.map +1 -1
  30. package/dist/lod/copc-lod-tree.d.ts +15 -0
  31. package/dist/lod/copc-lod-tree.d.ts.map +1 -0
  32. package/dist/lod/copc-lod-tree.js +67 -0
  33. package/dist/lod/copc-lod-tree.js.map +1 -0
  34. package/dist/lod/frustum.d.ts +13 -0
  35. package/dist/lod/frustum.d.ts.map +1 -0
  36. package/dist/lod/frustum.js +38 -0
  37. package/dist/lod/frustum.js.map +1 -0
  38. package/dist/lod/pacer.d.ts +62 -0
  39. package/dist/lod/pacer.d.ts.map +1 -0
  40. package/dist/lod/pacer.js +56 -0
  41. package/dist/lod/pacer.js.map +1 -0
  42. package/dist/lod/select.d.ts +80 -0
  43. package/dist/lod/select.d.ts.map +1 -0
  44. package/dist/lod/select.js +155 -0
  45. package/dist/lod/select.js.map +1 -0
  46. package/dist/streaming/blob-source.d.ts +3 -2
  47. package/dist/streaming/blob-source.d.ts.map +1 -1
  48. package/dist/streaming/blob-source.js +2 -9
  49. package/dist/streaming/blob-source.js.map +1 -1
  50. package/dist/streaming/decode-worker.js +22 -0
  51. package/dist/streaming/decode-worker.js.map +1 -1
  52. package/dist/streaming/http-range-source.d.ts +47 -0
  53. package/dist/streaming/http-range-source.d.ts.map +1 -0
  54. package/dist/streaming/http-range-source.js +102 -0
  55. package/dist/streaming/http-range-source.js.map +1 -0
  56. package/dist/streaming/inline-worker.js +1 -1
  57. package/dist/streaming/laz-perf-loader.d.ts +58 -0
  58. package/dist/streaming/laz-perf-loader.d.ts.map +1 -0
  59. package/dist/streaming/laz-perf-loader.js +107 -0
  60. package/dist/streaming/laz-perf-loader.js.map +1 -0
  61. package/dist/streaming/laz-source.d.ts +0 -40
  62. package/dist/streaming/laz-source.d.ts.map +1 -1
  63. package/dist/streaming/laz-source.js +1 -94
  64. package/dist/streaming/laz-source.js.map +1 -1
  65. package/dist/streaming/protocol.d.ts +45 -0
  66. package/dist/streaming/protocol.d.ts.map +1 -1
  67. package/dist/streaming/protocol.js.map +1 -1
  68. package/dist/streaming/types.d.ts +14 -0
  69. package/dist/streaming/types.d.ts.map +1 -1
  70. package/dist/streaming/worker-client.d.ts +25 -0
  71. package/dist/streaming/worker-client.d.ts.map +1 -1
  72. package/dist/streaming/worker-client.js +12 -5
  73. package/dist/streaming/worker-client.js.map +1 -1
  74. package/package.json +2 -2
package/README.md CHANGED
@@ -81,6 +81,78 @@ Notes:
81
81
  - Strict CSPs need `script-src 'self' blob:` to allow the `Blob`-URL
82
82
  worker.
83
83
 
84
+ ## COPC: range-read octree point clouds
85
+
86
+ A [COPC](https://copc.io) file is a LAZ 1.4 file organised as an octree, so a
87
+ viewer can read only the nodes it needs, from a local `File` or over HTTP
88
+ Range requests. `openCopcWorkerReader` opens one in the decode worker, loads
89
+ the root hierarchy page, and decodes nodes on demand. Detection is by the
90
+ `copc`/`info` VLR (`isCopcHeader` on the first `COPC_PROBE_BYTES` bytes), not
91
+ by the `.copc.laz` file name.
92
+
93
+ ```ts
94
+ import { openCopcWorkerReader } from '@ifc-lite/pointcloud';
95
+
96
+ declare const file: File;
97
+ const reader = await openCopcWorkerReader({ source: { kind: 'blob', blob: file } });
98
+ const root = reader.hierarchy.nodes.get('0-0-0-0');
99
+ if (root) {
100
+ const chunk = await reader.readNode(root, { stride: 1 });
101
+ console.log(`${chunk.pointCount} points in the root node`);
102
+ }
103
+ // Deeper hierarchy pages load on demand: reader.loadPage(ref) for any
104
+ // ref in reader.hierarchy.pendingPages.
105
+ reader.close();
106
+ ```
107
+
108
+ For a remote file, pass `{ kind: 'http', url }`. The server must answer
109
+ Range requests with `206 Partial Content`; a server that ignores `Range` is
110
+ refused rather than downloading the whole file. When CORS hides
111
+ `Content-Range`, the size comes from a `HEAD` request and each response is
112
+ checked by its exact length. Hierarchy pages come from the file, so the
113
+ page walk is iterative with a visited set and page, node and byte budgets;
114
+ exceeding a budget is an error, never a silently truncated octree.
115
+
116
+ ## View-dependent level of detail
117
+
118
+ `selectLod(root, camera, { pointBudget })` picks which octree nodes to show.
119
+ It is pure (no renderer, no I/O) and works on any additive octree through the
120
+ small `LodNode` interface; `createCopcLodTree` adapts a COPC hierarchy.
121
+
122
+ - Nodes outside the view frustum are never selected.
123
+ - The selected node with the largest projected screen span refines first,
124
+ while its span exceeds `pixelThreshold` (default 96 px) and the selection
125
+ stays within `maxNodes` (default `clamp(pointBudget / 128, 8, 1024)`).
126
+ Parents always precede their children.
127
+ - The point budget is water-filled across the selection: each node gets an
128
+ equal share capped by its own point count, and every node carries a
129
+ `stride` hint whose decoded count never exceeds its share.
130
+ - Nodes that should refine but whose hierarchy page is not loaded yet are
131
+ listed in `needsChildren`.
132
+
133
+ `LodPacer` sizes a fast first pass to a time budget (default 200 ms) from a
134
+ learned read rate, an asymmetric moving average that drops fast and rises
135
+ slowly, with outlier clipping. `shouldReplacePass` swaps what is on screen
136
+ only for something better: more points for the same view, or a complete
137
+ pass for a newer view.
138
+
139
+ ```ts
140
+ import { LodPacer, createCopcLodTree, selectLod, type CopcWorkerReader, type LodCamera } from '@ifc-lite/pointcloud';
141
+
142
+ declare const reader: CopcWorkerReader;
143
+ declare const camera: LodCamera;
144
+ // Bounds in the decoded frame; move the camera into it (view-projection x placement).
145
+ const tree = createCopcLodTree(reader.hierarchy, reader.file.info, reader.originOffset);
146
+ const pacer = new LodPacer();
147
+ const [firstPass] = pacer.passBudgets(10_000_000);
148
+ if (tree.root) {
149
+ const selection = selectLod(tree.root, camera, { pointBudget: firstPass });
150
+ for (const node of selection.needsChildren) {
151
+ for (const ref of tree.pendingPagesUnder(node)) void reader.loadPage(ref);
152
+ }
153
+ }
154
+ ```
155
+
84
156
  ## API
85
157
 
86
158
  See the [docs site](https://ifclite.dev/docs/) for guides and the full API reference.
@@ -0,0 +1,102 @@
1
+ /**
2
+ * COPC hierarchy pages (#6869).
3
+ *
4
+ * A page is a packed array of 32-byte entries:
5
+ * VoxelKey (int32 level, x, y, z) | uint64 offset | int32 byteSize | int32 pointCount
6
+ * `pointCount >= 0` describes a node whose LAZ chunk sits at `offset`;
7
+ * `pointCount === -1` points at a CHILD PAGE of `byteSize` bytes at `offset`.
8
+ *
9
+ * The page tree comes from the file, so its shape is attacker-controlled.
10
+ * Following AGENTS.md "Bounding walks over file-supplied references", the
11
+ * walk is ITERATIVE (no stack to overflow), keeps a global visited set of
12
+ * page offsets (a page is a pure function of its offset, so memoising is
13
+ * safe and kills cycles and fan-out), and carries work budgets for pages,
14
+ * nodes and page bytes. Exceeding a budget THROWS: a truncated hierarchy
15
+ * would render as silently missing geometry.
16
+ */
17
+ import { type VoxelKey } from './copc-info.js';
18
+ /** Deepest level accepted: keeps `2^d` cell indices inside int32. */
19
+ export declare const MAX_COPC_LEVEL = 30;
20
+ /** Largest single node accepted (points). Real writers stay far below. */
21
+ export declare const MAX_COPC_NODE_POINTS: number;
22
+ export interface CopcNodeEntry {
23
+ key: VoxelKey;
24
+ /** Absolute file offset of the node's LAZ chunk. */
25
+ offset: number;
26
+ byteSize: number;
27
+ pointCount: number;
28
+ }
29
+ export interface CopcPageRef {
30
+ /** Root of the subtree the page describes. */
31
+ key: VoxelKey;
32
+ offset: number;
33
+ byteSize: number;
34
+ }
35
+ export interface CopcHierarchyPage {
36
+ nodes: CopcNodeEntry[];
37
+ pages: CopcPageRef[];
38
+ }
39
+ export interface CopcHierarchyLimits {
40
+ maxPages: number;
41
+ maxNodes: number;
42
+ maxPageBytes: number;
43
+ }
44
+ export declare const DEFAULT_COPC_HIERARCHY_LIMITS: CopcHierarchyLimits;
45
+ /** Parse and validate one page. Every entry is checked against the file size. */
46
+ export declare function parseCopcHierarchyPage(bytes: Uint8Array, fileSize: number): CopcHierarchyPage;
47
+ export type CopcChildState = 'node' | 'page' | 'absent';
48
+ /** The eight children of `key`. */
49
+ export declare function copcChildKeys(key: VoxelKey): VoxelKey[];
50
+ /**
51
+ * The hierarchy known so far: nodes from loaded pages, plus pointers to
52
+ * pages not yet loaded. Pages load on demand, so a huge file's hierarchy is
53
+ * only read where the camera looks.
54
+ */
55
+ export declare class CopcHierarchy {
56
+ readonly limits: CopcHierarchyLimits;
57
+ readonly nodes: Map<string, CopcNodeEntry>;
58
+ /** Unloaded child pages, keyed by the id of their subtree root. */
59
+ readonly pendingPages: Map<string, CopcPageRef>;
60
+ private readonly seenPageOffsets;
61
+ private pageBytes;
62
+ private pageCount;
63
+ constructor(limits?: CopcHierarchyLimits);
64
+ get loadedPageCount(): number;
65
+ /** Page bytes still allowed by `limits.maxPageBytes`. */
66
+ get remainingPageBytes(): number;
67
+ /**
68
+ * Throw unless `ref` fits the page and byte budgets. Call BEFORE reading a
69
+ * page: a hostile pointer can name a page as large as the file, and a
70
+ * budget checked only after parsing has already paid for it.
71
+ */
72
+ assertCanLoad(ref: {
73
+ byteSize: number;
74
+ }): void;
75
+ /**
76
+ * Admit the page read from `ref`: the root page first, then only pages
77
+ * that are pending. Throws when a budget is exceeded or a node key
78
+ * repeats; pointers to already-seen pages are dropped.
79
+ */
80
+ addPage(ref: {
81
+ offset: number;
82
+ byteSize: number;
83
+ key?: VoxelKey;
84
+ }, page: CopcHierarchyPage): void;
85
+ private isPending;
86
+ /** Whether `key` is a known node, the root of an unloaded page, or neither. */
87
+ stateOf(key: VoxelKey): CopcChildState;
88
+ }
89
+ /** Reads one page's bytes and parses it; the reader owns the I/O. */
90
+ export type CopcPageFetcher = (ref: CopcPageRef, signal?: AbortSignal) => Promise<CopcHierarchyPage>;
91
+ /**
92
+ * Load every pending page, iteratively (breadth-first, `concurrency` reads
93
+ * in flight), optionally only those whose subtree root is at most
94
+ * `maxLevel` deep. The visited set and the hierarchy's budgets bound the
95
+ * work; there is no recursion.
96
+ */
97
+ export declare function loadPendingCopcPages(hierarchy: CopcHierarchy, fetchPage: CopcPageFetcher, options?: {
98
+ maxLevel?: number;
99
+ signal?: AbortSignal;
100
+ concurrency?: number;
101
+ }): Promise<void>;
102
+ //# sourceMappingURL=copc-hierarchy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copc-hierarchy.d.ts","sourceRoot":"","sources":["../../src/copc/copc-hierarchy.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAc,KAAK,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAG3D,qEAAqE;AACrE,eAAO,MAAM,cAAc,KAAK,CAAC;AACjC,0EAA0E;AAC1E,eAAO,MAAM,oBAAoB,QAAU,CAAC;AAE5C,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,QAAQ,CAAC;IACd,oDAAoD;IACpD,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,8CAA8C;IAC9C,GAAG,EAAE,QAAQ,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,eAAO,MAAM,6BAA6B,EAAE,mBAI3C,CAAC;AAQF,iFAAiF;AACjF,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,iBAAiB,CAyC7F;AAED,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC;AAExD,mCAAmC;AACnC,wBAAgB,aAAa,CAAC,GAAG,EAAE,QAAQ,GAAG,QAAQ,EAAE,CAMvD;AAED;;;;GAIG;AACH,qBAAa,aAAa;IAQZ,QAAQ,CAAC,MAAM,EAAE,mBAAmB;IAPhD,QAAQ,CAAC,KAAK,6BAAoC;IAClD,mEAAmE;IACnE,QAAQ,CAAC,YAAY,2BAAkC;IACvD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAqB;IACrD,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,SAAS,CAAK;gBAED,MAAM,GAAE,mBAAmD;IAEhF,IAAI,eAAe,IAAI,MAAM,CAE5B;IAED,yDAAyD;IACzD,IAAI,kBAAkB,IAAI,MAAM,CAE/B;IAED;;;;OAIG;IACH,aAAa,CAAC,GAAG,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAS9C;;;;OAIG;IACH,OAAO,CAAC,GAAG,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,QAAQ,CAAA;KAAE,EAAE,IAAI,EAAE,iBAAiB,GAAG,IAAI;IAmCjG,OAAO,CAAC,SAAS;IAKjB,+EAA+E;IAC/E,OAAO,CAAC,GAAG,EAAE,QAAQ,GAAG,cAAc;CAMvC;AAED,qEAAqE;AACrE,MAAM,MAAM,eAAe,GAAG,CAAC,GAAG,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAErG;;;;;GAKG;AACH,wBAAsB,oBAAoB,CACxC,SAAS,EAAE,aAAa,EACxB,SAAS,EAAE,eAAe,EAC1B,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAO,GAC9E,OAAO,CAAC,IAAI,CAAC,CAef"}
@@ -0,0 +1,210 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+ /**
5
+ * COPC hierarchy pages (#6869).
6
+ *
7
+ * A page is a packed array of 32-byte entries:
8
+ * VoxelKey (int32 level, x, y, z) | uint64 offset | int32 byteSize | int32 pointCount
9
+ * `pointCount >= 0` describes a node whose LAZ chunk sits at `offset`;
10
+ * `pointCount === -1` points at a CHILD PAGE of `byteSize` bytes at `offset`.
11
+ *
12
+ * The page tree comes from the file, so its shape is attacker-controlled.
13
+ * Following AGENTS.md "Bounding walks over file-supplied references", the
14
+ * walk is ITERATIVE (no stack to overflow), keeps a global visited set of
15
+ * page offsets (a page is a pure function of its offset, so memoising is
16
+ * safe and kills cycles and fan-out), and carries work budgets for pages,
17
+ * nodes and page bytes. Exceeding a budget THROWS: a truncated hierarchy
18
+ * would render as silently missing geometry.
19
+ */
20
+ import { voxelKeyId } from './copc-info.js';
21
+ const ENTRY_SIZE = 32;
22
+ /** Deepest level accepted: keeps `2^d` cell indices inside int32. */
23
+ export const MAX_COPC_LEVEL = 30;
24
+ /** Largest single node accepted (points). Real writers stay far below. */
25
+ export const MAX_COPC_NODE_POINTS = 1 << 24;
26
+ export const DEFAULT_COPC_HIERARCHY_LIMITS = {
27
+ maxPages: 1 << 16,
28
+ maxNodes: 1 << 21,
29
+ maxPageBytes: (1 << 21) * ENTRY_SIZE,
30
+ };
31
+ function checkSpan(what, offset, size, fileSize) {
32
+ if (!Number.isSafeInteger(offset) || offset < 0 || offset + size > fileSize) {
33
+ throw new Error(`COPC: ${what} [${offset}, +${size}) lies outside the ${fileSize}-byte file`);
34
+ }
35
+ }
36
+ /** Parse and validate one page. Every entry is checked against the file size. */
37
+ export function parseCopcHierarchyPage(bytes, fileSize) {
38
+ if (bytes.length % ENTRY_SIZE !== 0) {
39
+ throw new Error(`COPC: hierarchy page of ${bytes.length} bytes is not a multiple of ${ENTRY_SIZE}`);
40
+ }
41
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
42
+ const page = { nodes: [], pages: [] };
43
+ for (let at = 0; at < bytes.length; at += ENTRY_SIZE) {
44
+ const key = {
45
+ d: view.getInt32(at, true),
46
+ x: view.getInt32(at + 4, true),
47
+ y: view.getInt32(at + 8, true),
48
+ z: view.getInt32(at + 12, true),
49
+ };
50
+ const offset = view.getUint32(at + 16, true) + view.getUint32(at + 20, true) * 0x100000000;
51
+ const byteSize = view.getInt32(at + 24, true);
52
+ const pointCount = view.getInt32(at + 28, true);
53
+ if (key.d < 0 || key.d > MAX_COPC_LEVEL)
54
+ throw new Error(`COPC: hierarchy key level ${key.d} out of range`);
55
+ const cells = 2 ** key.d;
56
+ for (const c of [key.x, key.y, key.z]) {
57
+ if (c < 0 || c >= cells)
58
+ throw new Error(`COPC: hierarchy key ${voxelKeyId(key)} has a cell outside [0, ${cells})`);
59
+ }
60
+ if (pointCount === -1) {
61
+ if (byteSize <= 0 || byteSize % ENTRY_SIZE !== 0) {
62
+ throw new Error(`COPC: child page ${voxelKeyId(key)} has invalid size ${byteSize}`);
63
+ }
64
+ checkSpan(`child page ${voxelKeyId(key)}`, offset, byteSize, fileSize);
65
+ page.pages.push({ key, offset, byteSize });
66
+ }
67
+ else if (pointCount === 0) {
68
+ page.nodes.push({ key, offset: 0, byteSize: 0, pointCount: 0 });
69
+ }
70
+ else if (pointCount > 0) {
71
+ if (pointCount > MAX_COPC_NODE_POINTS) {
72
+ throw new Error(`COPC: node ${voxelKeyId(key)} declares ${pointCount} points (limit ${MAX_COPC_NODE_POINTS})`);
73
+ }
74
+ if (byteSize <= 0)
75
+ throw new Error(`COPC: node ${voxelKeyId(key)} has ${pointCount} points but ${byteSize} bytes`);
76
+ checkSpan(`node ${voxelKeyId(key)}`, offset, byteSize, fileSize);
77
+ page.nodes.push({ key, offset, byteSize, pointCount });
78
+ }
79
+ else {
80
+ throw new Error(`COPC: hierarchy entry ${voxelKeyId(key)} has invalid point count ${pointCount}`);
81
+ }
82
+ }
83
+ return page;
84
+ }
85
+ /** The eight children of `key`. */
86
+ export function copcChildKeys(key) {
87
+ const out = [];
88
+ for (let i = 0; i < 8; i++) {
89
+ out.push({ d: key.d + 1, x: key.x * 2 + (i & 1), y: key.y * 2 + ((i >> 1) & 1), z: key.z * 2 + ((i >> 2) & 1) });
90
+ }
91
+ return out;
92
+ }
93
+ /**
94
+ * The hierarchy known so far: nodes from loaded pages, plus pointers to
95
+ * pages not yet loaded. Pages load on demand, so a huge file's hierarchy is
96
+ * only read where the camera looks.
97
+ */
98
+ export class CopcHierarchy {
99
+ limits;
100
+ nodes = new Map();
101
+ /** Unloaded child pages, keyed by the id of their subtree root. */
102
+ pendingPages = new Map();
103
+ seenPageOffsets = new Set();
104
+ pageBytes = 0;
105
+ pageCount = 0;
106
+ constructor(limits = DEFAULT_COPC_HIERARCHY_LIMITS) {
107
+ this.limits = limits;
108
+ }
109
+ get loadedPageCount() {
110
+ return this.pageCount;
111
+ }
112
+ /** Page bytes still allowed by `limits.maxPageBytes`. */
113
+ get remainingPageBytes() {
114
+ return Math.max(0, this.limits.maxPageBytes - this.pageBytes);
115
+ }
116
+ /**
117
+ * Throw unless `ref` fits the page and byte budgets. Call BEFORE reading a
118
+ * page: a hostile pointer can name a page as large as the file, and a
119
+ * budget checked only after parsing has already paid for it.
120
+ */
121
+ assertCanLoad(ref) {
122
+ if (this.pageCount + 1 > this.limits.maxPages) {
123
+ throw new Error(`COPC: hierarchy exceeds ${this.limits.maxPages} pages`);
124
+ }
125
+ if (ref.byteSize > this.remainingPageBytes) {
126
+ throw new Error(`COPC: hierarchy exceeds ${this.limits.maxPageBytes} page bytes`);
127
+ }
128
+ }
129
+ /**
130
+ * Admit the page read from `ref`: the root page first, then only pages
131
+ * that are pending. Throws when a budget is exceeded or a node key
132
+ * repeats; pointers to already-seen pages are dropped.
133
+ */
134
+ addPage(ref, page) {
135
+ if (this.pageCount > 0 && !this.isPending(ref)) {
136
+ throw new Error(`COPC: page at ${ref.offset} is not a pending hierarchy page`);
137
+ }
138
+ this.pageCount++;
139
+ this.pageBytes += ref.byteSize;
140
+ if (this.pageCount > this.limits.maxPages) {
141
+ throw new Error(`COPC: hierarchy exceeds ${this.limits.maxPages} pages`);
142
+ }
143
+ if (this.pageBytes > this.limits.maxPageBytes) {
144
+ throw new Error(`COPC: hierarchy exceeds ${this.limits.maxPageBytes} page bytes`);
145
+ }
146
+ this.seenPageOffsets.add(ref.offset);
147
+ if (ref.key)
148
+ this.pendingPages.delete(voxelKeyId(ref.key));
149
+ if (this.nodes.size + page.nodes.length > this.limits.maxNodes) {
150
+ throw new Error(`COPC: hierarchy exceeds ${this.limits.maxNodes} nodes`);
151
+ }
152
+ for (const node of page.nodes) {
153
+ const id = voxelKeyId(node.key);
154
+ if (this.nodes.has(id))
155
+ throw new Error(`COPC: hierarchy lists node ${id} twice`);
156
+ this.nodes.set(id, node);
157
+ }
158
+ for (const child of page.pages) {
159
+ // The visited set is the cycle guard: a pointer back to any page we
160
+ // have read or queued is dropped, never followed.
161
+ if (this.seenPageOffsets.has(child.offset))
162
+ continue;
163
+ const id = voxelKeyId(child.key);
164
+ // Two pointers for one subtree root: overwriting would silently drop
165
+ // the first page's subtree, so treat it like a repeated node.
166
+ if (this.pendingPages.has(id))
167
+ throw new Error(`COPC: hierarchy lists child page ${id} twice`);
168
+ this.seenPageOffsets.add(child.offset);
169
+ this.pendingPages.set(id, child);
170
+ }
171
+ }
172
+ isPending(ref) {
173
+ if (!ref.key)
174
+ return false;
175
+ return this.pendingPages.get(voxelKeyId(ref.key))?.offset === ref.offset;
176
+ }
177
+ /** Whether `key` is a known node, the root of an unloaded page, or neither. */
178
+ stateOf(key) {
179
+ const id = voxelKeyId(key);
180
+ if (this.nodes.has(id))
181
+ return 'node';
182
+ if (this.pendingPages.has(id))
183
+ return 'page';
184
+ return 'absent';
185
+ }
186
+ }
187
+ /**
188
+ * Load every pending page, iteratively (breadth-first, `concurrency` reads
189
+ * in flight), optionally only those whose subtree root is at most
190
+ * `maxLevel` deep. The visited set and the hierarchy's budgets bound the
191
+ * work; there is no recursion.
192
+ */
193
+ export async function loadPendingCopcPages(hierarchy, fetchPage, options = {}) {
194
+ const maxLevel = options.maxLevel ?? Infinity;
195
+ const concurrency = Math.max(1, Math.floor(options.concurrency ?? 6));
196
+ while (true) {
197
+ options.signal?.throwIfAborted();
198
+ const batch = [...hierarchy.pendingPages.values()].filter((ref) => ref.key.d <= maxLevel).slice(0, concurrency);
199
+ if (batch.length === 0)
200
+ return;
201
+ // Budget the whole batch before any read starts.
202
+ batch.reduce((bytes, ref) => {
203
+ hierarchy.assertCanLoad({ byteSize: bytes + ref.byteSize });
204
+ return bytes + ref.byteSize;
205
+ }, 0);
206
+ const pages = await Promise.all(batch.map((ref) => fetchPage(ref, options.signal)));
207
+ batch.forEach((ref, i) => hierarchy.addPage(ref, pages[i]));
208
+ }
209
+ }
210
+ //# sourceMappingURL=copc-hierarchy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copc-hierarchy.js","sourceRoot":"","sources":["../../src/copc/copc-hierarchy.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,UAAU,EAAiB,MAAM,gBAAgB,CAAC;AAE3D,MAAM,UAAU,GAAG,EAAE,CAAC;AACtB,qEAAqE;AACrE,MAAM,CAAC,MAAM,cAAc,GAAG,EAAE,CAAC;AACjC,0EAA0E;AAC1E,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,IAAI,EAAE,CAAC;AA4B5C,MAAM,CAAC,MAAM,6BAA6B,GAAwB;IAChE,QAAQ,EAAE,CAAC,IAAI,EAAE;IACjB,QAAQ,EAAE,CAAC,IAAI,EAAE;IACjB,YAAY,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,UAAU;CACrC,CAAC;AAEF,SAAS,SAAS,CAAC,IAAY,EAAE,MAAc,EAAE,IAAY,EAAE,QAAgB;IAC7E,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,IAAI,MAAM,GAAG,IAAI,GAAG,QAAQ,EAAE,CAAC;QAC5E,MAAM,IAAI,KAAK,CAAC,SAAS,IAAI,KAAK,MAAM,MAAM,IAAI,sBAAsB,QAAQ,YAAY,CAAC,CAAC;IAChG,CAAC;AACH,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,sBAAsB,CAAC,KAAiB,EAAE,QAAgB;IACxE,IAAI,KAAK,CAAC,MAAM,GAAG,UAAU,KAAK,CAAC,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,2BAA2B,KAAK,CAAC,MAAM,+BAA+B,UAAU,EAAE,CAAC,CAAC;IACtG,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IAC5E,MAAM,IAAI,GAAsB,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;IACzD,KAAK,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,GAAG,KAAK,CAAC,MAAM,EAAE,EAAE,IAAI,UAAU,EAAE,CAAC;QACrD,MAAM,GAAG,GAAa;YACpB,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;YAC1B,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,CAAC,EAAE,IAAI,CAAC;YAC9B,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,CAAC,EAAE,IAAI,CAAC;YAC9B,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,EAAE,IAAI,CAAC;SAChC,CAAC;QACF,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,EAAE,IAAI,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,EAAE,IAAI,CAAC,GAAG,WAAW,CAAC;QAC3F,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;QAC9C,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;QAChD,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,cAAc;YAAE,MAAM,IAAI,KAAK,CAAC,6BAA6B,GAAG,CAAC,CAAC,eAAe,CAAC,CAAC;QAC5G,MAAM,KAAK,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;QACzB,KAAK,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YACtC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,KAAK;gBAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,UAAU,CAAC,GAAG,CAAC,2BAA2B,KAAK,GAAG,CAAC,CAAC;QACtH,CAAC;QACD,IAAI,UAAU,KAAK,CAAC,CAAC,EAAE,CAAC;YACtB,IAAI,QAAQ,IAAI,CAAC,IAAI,QAAQ,GAAG,UAAU,KAAK,CAAC,EAAE,CAAC;gBACjD,MAAM,IAAI,KAAK,CAAC,oBAAoB,UAAU,CAAC,GAAG,CAAC,qBAAqB,QAAQ,EAAE,CAAC,CAAC;YACtF,CAAC;YACD,SAAS,CAAC,cAAc,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;YACvE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC7C,CAAC;aAAM,IAAI,UAAU,KAAK,CAAC,EAAE,CAAC;YAC5B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC,CAAC;QAClE,CAAC;aAAM,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;YAC1B,IAAI,UAAU,GAAG,oBAAoB,EAAE,CAAC;gBACtC,MAAM,IAAI,KAAK,CAAC,cAAc,UAAU,CAAC,GAAG,CAAC,aAAa,UAAU,kBAAkB,oBAAoB,GAAG,CAAC,CAAC;YACjH,CAAC;YACD,IAAI,QAAQ,IAAI,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,cAAc,UAAU,CAAC,GAAG,CAAC,QAAQ,UAAU,eAAe,QAAQ,QAAQ,CAAC,CAAC;YACnH,SAAS,CAAC,QAAQ,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;YACjE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAC;QACzD,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,KAAK,CAAC,yBAAyB,UAAU,CAAC,GAAG,CAAC,4BAA4B,UAAU,EAAE,CAAC,CAAC;QACpG,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAID,mCAAmC;AACnC,MAAM,UAAU,aAAa,CAAC,GAAa;IACzC,MAAM,GAAG,GAAe,EAAE,CAAC;IAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3B,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;IACnH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;GAIG;AACH,MAAM,OAAO,aAAa;IAQH;IAPZ,KAAK,GAAG,IAAI,GAAG,EAAyB,CAAC;IAClD,mEAAmE;IAC1D,YAAY,GAAG,IAAI,GAAG,EAAuB,CAAC;IACtC,eAAe,GAAG,IAAI,GAAG,EAAU,CAAC;IAC7C,SAAS,GAAG,CAAC,CAAC;IACd,SAAS,GAAG,CAAC,CAAC;IAEtB,YAAqB,SAA8B,6BAA6B;QAA3D,WAAM,GAAN,MAAM,CAAqD;IAAG,CAAC;IAEpF,IAAI,eAAe;QACjB,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED,yDAAyD;IACzD,IAAI,kBAAkB;QACpB,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC;IAChE,CAAC;IAED;;;;OAIG;IACH,aAAa,CAAC,GAAyB;QACrC,IAAI,IAAI,CAAC,SAAS,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;YAC9C,MAAM,IAAI,KAAK,CAAC,2BAA2B,IAAI,CAAC,MAAM,CAAC,QAAQ,QAAQ,CAAC,CAAC;QAC3E,CAAC;QACD,IAAI,GAAG,CAAC,QAAQ,GAAG,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC3C,MAAM,IAAI,KAAK,CAAC,2BAA2B,IAAI,CAAC,MAAM,CAAC,YAAY,aAAa,CAAC,CAAC;QACpF,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,OAAO,CAAC,GAAyD,EAAE,IAAuB;QACxF,IAAI,IAAI,CAAC,SAAS,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;YAC/C,MAAM,IAAI,KAAK,CAAC,iBAAiB,GAAG,CAAC,MAAM,kCAAkC,CAAC,CAAC;QACjF,CAAC;QACD,IAAI,CAAC,SAAS,EAAE,CAAC;QACjB,IAAI,CAAC,SAAS,IAAI,GAAG,CAAC,QAAQ,CAAC;QAC/B,IAAI,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;YAC1C,MAAM,IAAI,KAAK,CAAC,2BAA2B,IAAI,CAAC,MAAM,CAAC,QAAQ,QAAQ,CAAC,CAAC;QAC3E,CAAC;QACD,IAAI,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC;YAC9C,MAAM,IAAI,KAAK,CAAC,2BAA2B,IAAI,CAAC,MAAM,CAAC,YAAY,aAAa,CAAC,CAAC;QACpF,CAAC;QACD,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACrC,IAAI,GAAG,CAAC,GAAG;YAAE,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;QAC3D,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;YAC/D,MAAM,IAAI,KAAK,CAAC,2BAA2B,IAAI,CAAC,MAAM,CAAC,QAAQ,QAAQ,CAAC,CAAC;QAC3E,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAC9B,MAAM,EAAE,GAAG,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAChC,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,8BAA8B,EAAE,QAAQ,CAAC,CAAC;YAClF,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QAC3B,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAC/B,oEAAoE;YACpE,kDAAkD;YAClD,IAAI,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC;gBAAE,SAAS;YACrD,MAAM,EAAE,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YACjC,qEAAqE;YACrE,8DAA8D;YAC9D,IAAI,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,oCAAoC,EAAE,QAAQ,CAAC,CAAC;YAC/F,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YACvC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QACnC,CAAC;IACH,CAAC;IAEO,SAAS,CAAC,GAAuC;QACvD,IAAI,CAAC,GAAG,CAAC,GAAG;YAAE,OAAO,KAAK,CAAC;QAC3B,OAAO,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,KAAK,GAAG,CAAC,MAAM,CAAC;IAC3E,CAAC;IAED,+EAA+E;IAC/E,OAAO,CAAC,GAAa;QACnB,MAAM,EAAE,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;QAC3B,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;YAAE,OAAO,MAAM,CAAC;QACtC,IAAI,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC;YAAE,OAAO,MAAM,CAAC;QAC7C,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF;AAKD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,SAAwB,EACxB,SAA0B,EAC1B,UAA6E,EAAE;IAE/E,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,QAAQ,CAAC;IAC9C,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC,CAAC;IACtE,OAAO,IAAI,EAAE,CAAC;QACZ,OAAO,CAAC,MAAM,EAAE,cAAc,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,CAAC,GAAG,SAAS,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC;QAChH,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAC/B,iDAAiD;QACjD,KAAK,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;YAC1B,SAAS,CAAC,aAAa,CAAC,EAAE,QAAQ,EAAE,KAAK,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;YAC5D,OAAO,KAAK,GAAG,GAAG,CAAC,QAAQ,CAAC;QAC9B,CAAC,EAAE,CAAC,CAAC,CAAC;QACN,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACpF,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9D,CAAC;AACH,CAAC"}
@@ -0,0 +1,52 @@
1
+ import { type LasHeader } from '../formats/las.js';
2
+ import type { PointCloudBBox } from '../types.js';
3
+ import type { PointSourceSpatialMetadata, RangeByteSource } from '../streaming/types.js';
4
+ /** Byte size of the LAS 1.4 public header block. */
5
+ export declare const LAS14_HEADER_SIZE = 375;
6
+ /** Header + the `copc` VLR header + its payload: everything detection needs. */
7
+ export declare const COPC_PROBE_BYTES: number;
8
+ /** VLRs are control-plane metadata; a larger block is refused before allocation. */
9
+ export declare const MAX_COPC_VLR_BYTES: number;
10
+ /** The `copc`/`info` VLR payload. */
11
+ export interface CopcInfo {
12
+ /** Centre of the root octree cube, in native (unshifted) coordinates. */
13
+ center: [number, number, number];
14
+ /** Half the edge length of the root cube. */
15
+ halfsize: number;
16
+ /** Point spacing at the root level; halves with every level. */
17
+ spacing: number;
18
+ /** Absolute file offset of the root hierarchy page. */
19
+ rootHierOffset: number;
20
+ /** Byte size of the root hierarchy page (a multiple of 32). */
21
+ rootHierSize: number;
22
+ gpsTimeMin: number;
23
+ gpsTimeMax: number;
24
+ }
25
+ /** Everything `openCopc` learns before touching point data. */
26
+ export interface CopcFileInfo {
27
+ header: LasHeader;
28
+ info: CopcInfo;
29
+ fileSize: number;
30
+ spatialMetadata?: PointSourceSpatialMetadata;
31
+ }
32
+ /** True when `bytes` (at least `COPC_PROBE_BYTES` of a file's start) is a COPC header. */
33
+ export declare function isCopcHeader(bytes: Uint8Array): boolean;
34
+ /** Parse the 160-byte `copc`/`info` payload. Throws on a non-finite or impossible cube. */
35
+ export declare function parseCopcInfo(payload: Uint8Array, fileSize: number): CopcInfo;
36
+ /**
37
+ * Read the header and VLR block through `source`, validating that this is a
38
+ * COPC file this reader can decode. Point data is not touched.
39
+ */
40
+ export declare function readCopcFileInfo(source: RangeByteSource, signal?: AbortSignal): Promise<CopcFileInfo>;
41
+ /** An octree address: level `d` and integer cell `x, y, z` in `[0, 2^d)`. */
42
+ export interface VoxelKey {
43
+ d: number;
44
+ x: number;
45
+ y: number;
46
+ z: number;
47
+ }
48
+ /** Stable string id for a key (`"d-x-y-z"`), the form COPC tools print. */
49
+ export declare function voxelKeyId(key: VoxelKey): string;
50
+ /** The cube a node covers, in native coordinates: the root cube split `2^d` ways per axis. */
51
+ export declare function copcNodeBounds(info: CopcInfo, key: VoxelKey): PointCloudBBox;
52
+ //# sourceMappingURL=copc-info.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copc-info.d.ts","sourceRoot":"","sources":["../../src/copc/copc-info.ts"],"names":[],"mappings":"AAgBA,OAAO,EAA6B,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAAE,0BAA0B,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAEzF,oDAAoD;AACpD,eAAO,MAAM,iBAAiB,MAAM,CAAC;AAGrC,gFAAgF;AAChF,eAAO,MAAM,gBAAgB,QAAuD,CAAC;AACrF,oFAAoF;AACpF,eAAO,MAAM,kBAAkB,QAAW,CAAC;AAK3C,qCAAqC;AACrC,MAAM,WAAW,QAAQ;IACvB,yEAAyE;IACzE,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,6CAA6C;IAC7C,QAAQ,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,OAAO,EAAE,MAAM,CAAC;IAChB,uDAAuD;IACvD,cAAc,EAAE,MAAM,CAAC;IACvB,+DAA+D;IAC/D,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,+DAA+D;AAC/D,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,SAAS,CAAC;IAClB,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,0BAA0B,CAAC;CAC9C;AAYD,0FAA0F;AAC1F,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAQvD;AAED,2FAA2F;AAC3F,wBAAgB,aAAa,CAAC,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAoB7E;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,eAAe,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CA0B3G;AA2BD,6EAA6E;AAC7E,MAAM,WAAW,QAAQ;IACvB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED,2EAA2E;AAC3E,wBAAgB,UAAU,CAAC,GAAG,EAAE,QAAQ,GAAG,MAAM,CAEhD;AAED,8FAA8F;AAC9F,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ,GAAG,cAAc,CAQ5E"}
@@ -0,0 +1,147 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+ /**
5
+ * COPC 1.0 header and `copc`/`info` VLR (#6869), from the specification at
6
+ * https://copc.io.
7
+ *
8
+ * A COPC file is a LAS 1.4 file (point formats 6, 7 or 8, LAZ-compressed)
9
+ * whose FIRST VLR is `copc`/1, a 160-byte record describing the octree cube
10
+ * and where the root hierarchy page lives. Detection is by that VLR, not by
11
+ * the `.copc.laz` file name: a renamed COPC file is still COPC and a plain
12
+ * LAZ named `.copc.laz` is not.
13
+ */
14
+ import { extractWktCrsIdentifiers } from '../spatial-wkt.js';
15
+ import { parseLasHeader, readU64LE } from '../formats/las.js';
16
+ /** Byte size of the LAS 1.4 public header block. */
17
+ export const LAS14_HEADER_SIZE = 375;
18
+ const VLR_HEADER_SIZE = 54;
19
+ const COPC_INFO_SIZE = 160;
20
+ /** Header + the `copc` VLR header + its payload: everything detection needs. */
21
+ export const COPC_PROBE_BYTES = LAS14_HEADER_SIZE + VLR_HEADER_SIZE + COPC_INFO_SIZE;
22
+ /** VLRs are control-plane metadata; a larger block is refused before allocation. */
23
+ export const MAX_COPC_VLR_BYTES = 16 << 20;
24
+ const COPC_POINT_FORMATS = new Set([6, 7, 8]);
25
+ const LASF_PROJECTION = 'LASF_Projection';
26
+ const WKT_RECORD_ID = 2112;
27
+ function readAscii(bytes, start, length) {
28
+ let out = '';
29
+ for (let i = start; i < start + length; i++) {
30
+ const c = bytes[i];
31
+ if (c === 0)
32
+ break;
33
+ out += String.fromCharCode(c);
34
+ }
35
+ return out;
36
+ }
37
+ /** True when `bytes` (at least `COPC_PROBE_BYTES` of a file's start) is a COPC header. */
38
+ export function isCopcHeader(bytes) {
39
+ if (bytes.length < COPC_PROBE_BYTES)
40
+ return false;
41
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
42
+ if (view.getUint32(0, true) !== 0x4653414c)
43
+ return false; // "LASF"
44
+ if (view.getUint8(24) !== 1 || view.getUint8(25) !== 4)
45
+ return false;
46
+ const headerSize = view.getUint16(94, true);
47
+ if (headerSize !== LAS14_HEADER_SIZE)
48
+ return false;
49
+ return readAscii(bytes, headerSize + 2, 16) === 'copc' && view.getUint16(headerSize + 18, true) === 1;
50
+ }
51
+ /** Parse the 160-byte `copc`/`info` payload. Throws on a non-finite or impossible cube. */
52
+ export function parseCopcInfo(payload, fileSize) {
53
+ if (payload.length < COPC_INFO_SIZE)
54
+ throw new Error('COPC: info VLR truncated');
55
+ const view = new DataView(payload.buffer, payload.byteOffset, payload.byteLength);
56
+ const center = [view.getFloat64(0, true), view.getFloat64(8, true), view.getFloat64(16, true)];
57
+ const halfsize = view.getFloat64(24, true);
58
+ const spacing = view.getFloat64(32, true);
59
+ const rootHierOffset = readU64LE(view, 40);
60
+ const rootHierSize = readU64LE(view, 48);
61
+ const gpsTimeMin = view.getFloat64(56, true);
62
+ const gpsTimeMax = view.getFloat64(64, true);
63
+ if (!center.every(Number.isFinite))
64
+ throw new Error('COPC: info VLR has a non-finite octree centre');
65
+ if (!(Number.isFinite(halfsize) && halfsize > 0))
66
+ throw new Error(`COPC: invalid octree halfsize ${halfsize}`);
67
+ if (!(Number.isFinite(spacing) && spacing > 0))
68
+ throw new Error(`COPC: invalid root spacing ${spacing}`);
69
+ if (rootHierSize <= 0 || rootHierSize % 32 !== 0) {
70
+ throw new Error(`COPC: root hierarchy page size ${rootHierSize} is not a positive multiple of 32`);
71
+ }
72
+ if (rootHierOffset < LAS14_HEADER_SIZE || rootHierOffset + rootHierSize > fileSize) {
73
+ throw new Error(`COPC: root hierarchy page [${rootHierOffset}, +${rootHierSize}) lies outside the ${fileSize}-byte file`);
74
+ }
75
+ return { center, halfsize, spacing, rootHierOffset, rootHierSize, gpsTimeMin, gpsTimeMax };
76
+ }
77
+ /**
78
+ * Read the header and VLR block through `source`, validating that this is a
79
+ * COPC file this reader can decode. Point data is not touched.
80
+ */
81
+ export async function readCopcFileInfo(source, signal) {
82
+ const head = await source.read(0, COPC_PROBE_BYTES, signal);
83
+ if (!isCopcHeader(head)) {
84
+ throw new Error('COPC: not a COPC file (LAS 1.4 header whose first VLR is copc/info expected)');
85
+ }
86
+ const header = parseLasHeader(head);
87
+ if (!COPC_POINT_FORMATS.has(header.pointDataFormatId)) {
88
+ throw new Error(`COPC: point data format ${header.pointDataFormatId} is not one of 6, 7, 8`);
89
+ }
90
+ const vlrEnd = header.pointDataOffset;
91
+ if (vlrEnd < COPC_PROBE_BYTES || vlrEnd > source.size) {
92
+ throw new Error(`COPC: point data offset ${vlrEnd} lies outside the file`);
93
+ }
94
+ if (vlrEnd - LAS14_HEADER_SIZE > MAX_COPC_VLR_BYTES) {
95
+ throw new Error(`COPC: VLR block of ${vlrEnd - LAS14_HEADER_SIZE} bytes exceeds the ${MAX_COPC_VLR_BYTES}-byte limit`);
96
+ }
97
+ const vlrs = vlrEnd > COPC_PROBE_BYTES
98
+ ? concat(head, await source.read(COPC_PROBE_BYTES, vlrEnd, signal))
99
+ : head;
100
+ if (vlrs.length < vlrEnd)
101
+ throw new Error('COPC: VLR block truncated');
102
+ const info = parseCopcInfo(vlrs.subarray(LAS14_HEADER_SIZE + VLR_HEADER_SIZE, COPC_PROBE_BYTES), source.size);
103
+ const wkt = findWkt(vlrs, header.numberOfVlrs, vlrEnd);
104
+ const spatialMetadata = wkt
105
+ ? { ...extractWktCrsIdentifiers(wkt), wkt, provenance: 'LAS OGC WKT VLR' }
106
+ : undefined;
107
+ return { header, info, fileSize: source.size, ...(spatialMetadata ? { spatialMetadata } : {}) };
108
+ }
109
+ /** Walk the VLR list (bounded by both the declared count and the byte block). */
110
+ function findWkt(bytes, count, end) {
111
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
112
+ let at = LAS14_HEADER_SIZE;
113
+ for (let i = 0; i < count && at + VLR_HEADER_SIZE <= end; i++) {
114
+ const userId = readAscii(bytes, at + 2, 16);
115
+ const recordId = view.getUint16(at + 18, true);
116
+ const length = view.getUint16(at + 20, true);
117
+ const dataStart = at + VLR_HEADER_SIZE;
118
+ if (dataStart + length > end)
119
+ break;
120
+ if (userId === LASF_PROJECTION && recordId === WKT_RECORD_ID) {
121
+ return new TextDecoder().decode(bytes.subarray(dataStart, dataStart + length)).replace(/\0+$/, '');
122
+ }
123
+ at = dataStart + length;
124
+ }
125
+ return undefined;
126
+ }
127
+ function concat(a, b) {
128
+ const out = new Uint8Array(a.length + b.length);
129
+ out.set(a, 0);
130
+ out.set(b, a.length);
131
+ return out;
132
+ }
133
+ /** Stable string id for a key (`"d-x-y-z"`), the form COPC tools print. */
134
+ export function voxelKeyId(key) {
135
+ return `${key.d}-${key.x}-${key.y}-${key.z}`;
136
+ }
137
+ /** The cube a node covers, in native coordinates: the root cube split `2^d` ways per axis. */
138
+ export function copcNodeBounds(info, key) {
139
+ const side = (2 * info.halfsize) / 2 ** key.d;
140
+ const min = [
141
+ info.center[0] - info.halfsize + key.x * side,
142
+ info.center[1] - info.halfsize + key.y * side,
143
+ info.center[2] - info.halfsize + key.z * side,
144
+ ];
145
+ return { min, max: [min[0] + side, min[1] + side, min[2] + side] };
146
+ }
147
+ //# sourceMappingURL=copc-info.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copc-info.js","sourceRoot":"","sources":["../../src/copc/copc-info.ts"],"names":[],"mappings":"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"}