@loaders.gl/tile-converter 5.0.0-alpha.11 → 5.0.0-alpha.14
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/LICENSE +1 -1
- package/README.md +180 -4
- package/bin/converter-v5.js +2 -0
- package/dist/converter.min.cjs +172 -182
- package/dist/i3s-server/bin/i3s-server.min.cjs +113 -158
- package/dist/index.cjs +18832 -19228
- package/dist/index.js +19566 -19968
- package/dist/v5/adapters.cjs +2002 -230
- package/dist/v5/adapters.d.ts +20 -0
- package/dist/v5/adapters.js +2005 -214
- package/dist/v5/archive-source.d.ts +36 -0
- package/dist/v5/browser-tileset-source.d.ts +55 -11
- package/dist/v5/browser.cjs +18 -0
- package/dist/v5/browser.d.ts +15 -1
- package/dist/v5/browser.js +26 -0
- package/dist/v5/conversion-api.d.ts +34 -0
- package/dist/v5/converter-cli.d.ts +1 -0
- package/dist/v5/converter-cli.js +1230 -0
- package/dist/v5/core.cjs +547 -77
- package/dist/v5/core.d.ts +3 -1
- package/dist/v5/core.js +540 -70
- package/dist/v5/feature-arrow.d.ts +1 -1
- package/dist/v5/i3s-mesh-conversion.d.ts +5 -1
- package/dist/v5/i3s-mesh-source-features.d.ts +2 -2
- package/dist/v5/i3s-mesh-source-material.d.ts +1 -1
- package/dist/v5/i3s-mesh-source.d.ts +2 -2
- package/dist/v5/i3s-selection-source.d.ts +70 -0
- package/dist/v5/index.cjs +15 -0
- package/dist/v5/index.d.ts +13 -1
- package/dist/v5/index.js +22 -0
- package/dist/v5/mesh-conversion.d.ts +21 -3
- package/dist/v5/mesh-draco.d.ts +2 -2
- package/dist/v5/mesh-features.d.ts +2 -2
- package/dist/v5/mesh-hierarchy.d.ts +47 -0
- package/dist/v5/mesh-source-features.d.ts +6 -5
- package/dist/v5/mesh-source-schema.d.ts +12 -0
- package/dist/v5/mesh-source-texture.d.ts +3 -3
- package/dist/v5/mesh-source.d.ts +10 -7
- package/dist/v5/mesh-tileset-sink.d.ts +5 -0
- package/dist/v5/mesh.d.ts +1 -1
- package/dist/v5/node-cli.d.ts +9 -0
- package/dist/v5/node-conversion.d.ts +34 -0
- package/dist/v5/node-feature-schema.d.ts +3 -0
- package/dist/v5/node-i3s-source.d.ts +41 -0
- package/dist/v5/node-source.d.ts +47 -0
- package/dist/v5/node.cjs +913 -0
- package/dist/v5/node.d.ts +6 -0
- package/dist/v5/node.js +890 -0
- package/dist/v5/point-cloud-attributes.d.ts +19 -0
- package/dist/v5/point-cloud-conversion.d.ts +1 -1
- package/dist/v5/point-cloud-copc.d.ts +44 -0
- package/dist/v5/point-cloud-file.d.ts +83 -0
- package/dist/v5/point-cloud-potree.d.ts +47 -0
- package/dist/v5/point-cloud-source-encoder.d.ts +1 -1
- package/dist/v5/point-cloud-source.d.ts +1 -1
- package/dist/v5/point-cloud-tileset.d.ts +44 -0
- package/dist/v5/point-cloud.d.ts +1 -1
- package/dist/v5/resource-manifest.d.ts +10 -1
- package/dist/v5/selected-mesh-source.d.ts +53 -0
- package/dist/v5/spatial-conversion.d.ts +1 -1
- package/dist/v5/tileset-source.d.ts +1 -1
- package/package.json +25 -32
- package/src/index.ts +4 -1
- package/src/v4/3d-tiles-converter/3d-tiles-converter.ts +11 -12
- package/src/v4/3d-tiles-converter/helpers/3d-tiles-content-converter.ts +7 -3
- package/src/v4/3d-tiles-converter/helpers/i3s-obb-to-3d-tiles-obb.ts +2 -2
- package/src/v4/3d-tiles-converter/helpers/load-i3s.ts +4 -6
- package/src/v4/3d-tiles-converter/helpers/texture-atlas.ts +1 -1
- package/src/v4/3d-tiles-converter/helpers/validate-i3s-spatial-reference.ts +2 -2
- package/src/v4/3d-tiles-converter/json-templates/tileset.ts +58 -39
- package/src/v4/converter-cli.ts +10 -4
- package/src/v4/deps-installer/deps-installer.ts +3 -3
- package/src/v4/i3s-converter/helpers/attribute-metadata-info.ts +1 -1
- package/src/v4/i3s-converter/helpers/batch-ids-extensions.ts +9 -10
- package/src/v4/i3s-converter/helpers/conversion-gltf.ts +108 -0
- package/src/v4/i3s-converter/helpers/coordinate-converter.ts +6 -5
- package/src/v4/i3s-converter/helpers/create-scene-server-path.ts +5 -6
- package/src/v4/i3s-converter/helpers/feature-attributes.ts +4 -4
- package/src/v4/i3s-converter/helpers/geometry-attributes.ts +1 -1
- package/src/v4/i3s-converter/helpers/geometry-converter.ts +69 -77
- package/src/v4/i3s-converter/helpers/gltf-attributes.ts +58 -74
- package/src/v4/i3s-converter/helpers/load-3d-tiles.ts +6 -54
- package/src/v4/i3s-converter/helpers/node-debug.ts +7 -3
- package/src/v4/i3s-converter/helpers/node-index-document.ts +11 -9
- package/src/v4/i3s-converter/helpers/node-pages.ts +3 -4
- package/src/v4/i3s-converter/helpers/preprocess-3d-tiles.ts +2 -2
- package/src/v4/i3s-converter/helpers/tileset-traversal.ts +1 -1
- package/src/v4/i3s-converter/i3s-converter.ts +29 -31
- package/src/v4/i3s-converter/json-templates/geometry-definitions.ts +71 -66
- package/src/v4/i3s-converter/json-templates/layers.ts +97 -132
- package/src/v4/i3s-converter/json-templates/metadata.ts +31 -21
- package/src/v4/i3s-converter/json-templates/node.ts +60 -74
- package/src/v4/i3s-converter/json-templates/scene-server.ts +36 -27
- package/src/v4/i3s-converter/json-templates/shared-resources.ts +62 -123
- package/src/v4/i3s-converter/json-templates/store.ts +39 -57
- package/src/v4/i3s-converter/types.ts +4 -4
- package/src/v4/i3s-server/controllers/slpk-controller.ts +3 -3
- package/src/v4/i3s-server/utils/create-scene-server.ts +3 -3
- package/src/v4/lib/utils/builder-input.ts +8 -0
- package/src/v4/lib/utils/conversion-dump.ts +5 -1
- package/src/v4/lib/utils/file-utils.ts +1 -1
- package/src/v4/lib/utils/hash-string.ts +21 -0
- package/src/v4/lib/utils/lod-conversion-utils.ts +3 -3
- package/src/v4/pgm-loader.ts +2 -2
- package/src/v5/adapters.ts +53 -0
- package/src/v5/archive-source.ts +235 -0
- package/src/v5/browser-tileset-source.ts +418 -48
- package/src/v5/browser.ts +46 -0
- package/src/v5/conversion-api.ts +184 -17
- package/src/v5/conversion-archive.ts +2 -2
- package/src/v5/converter-cli.ts +28 -0
- package/src/v5/core.ts +7 -0
- package/src/v5/feature-arrow.ts +2 -2
- package/src/v5/i3s-mesh-conversion.ts +41 -3
- package/src/v5/i3s-mesh-sink.ts +1 -1
- package/src/v5/i3s-mesh-source-features.ts +3 -3
- package/src/v5/i3s-mesh-source-material.ts +18 -11
- package/src/v5/i3s-mesh-source.ts +3 -2
- package/src/v5/i3s-selection-source.ts +298 -0
- package/src/v5/index.ts +41 -0
- package/src/v5/mesh-conversion.ts +24 -4
- package/src/v5/mesh-draco.ts +2 -2
- package/src/v5/mesh-features.ts +3 -3
- package/src/v5/mesh-hierarchy.ts +648 -0
- package/src/v5/mesh-source-features.ts +165 -51
- package/src/v5/mesh-source-schema.ts +140 -0
- package/src/v5/mesh-source-texture.ts +49 -19
- package/src/v5/mesh-source.ts +80 -49
- package/src/v5/mesh-tileset-archive.ts +1 -1
- package/src/v5/mesh-tileset-sink.ts +2 -2
- package/src/v5/mesh.ts +1 -1
- package/src/v5/node-cli.ts +201 -0
- package/src/v5/node-conversion.ts +362 -0
- package/src/v5/node-feature-schema.ts +155 -0
- package/src/v5/node-i3s-source.ts +181 -0
- package/src/v5/node-source.ts +328 -0
- package/src/v5/node.ts +16 -0
- package/src/v5/point-cloud-attributes.ts +113 -0
- package/src/v5/point-cloud-conversion.ts +1 -1
- package/src/v5/point-cloud-copc.ts +289 -0
- package/src/v5/point-cloud-file.ts +306 -0
- package/src/v5/point-cloud-potree.ts +134 -0
- package/src/v5/point-cloud-source-encoder.ts +1 -1
- package/src/v5/point-cloud-source.ts +1 -1
- package/src/v5/point-cloud-spatial.ts +2 -2
- package/src/v5/point-cloud-tileset.ts +137 -0
- package/src/v5/point-cloud.ts +3 -3
- package/src/v5/resource-fetcher.ts +4 -1
- package/src/v5/resource-manifest.ts +40 -5
- package/src/v5/selected-mesh-source.ts +384 -0
- package/src/v5/spatial-conversion.ts +90 -19
- package/src/v5/tileset-source.ts +2 -2
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// loaders.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import type {Mesh, MeshArrowTable} from '@loaders.gl/core/schema';
|
|
6
|
+
import {PointCloudTiler, type PointCloudTilerOptions} from '@loaders.gl/core/schema-utils';
|
|
7
|
+
import {encodePotreeDataset} from '@loaders.gl/tiles-3d/potree';
|
|
8
|
+
import type {
|
|
9
|
+
PotreeDataset,
|
|
10
|
+
PotreeWriterNode,
|
|
11
|
+
PotreeWriterOptions
|
|
12
|
+
} from '@loaders.gl/tiles-3d/potree';
|
|
13
|
+
import {encodePotreeDatasetArchive} from './point-cloud-file.js';
|
|
14
|
+
|
|
15
|
+
/** Bounded authoring of one decoded dataset in its already placed native coordinates. */
|
|
16
|
+
export interface ConvertPointCloudToPotreeOptions {
|
|
17
|
+
/** Explicit CRS declaration written verbatim; coordinates are not reprojected. */
|
|
18
|
+
readonly projection: string;
|
|
19
|
+
/** Maximum bytes retained across all gathered node attribute buffers. */
|
|
20
|
+
readonly maxGatheredBytes: number;
|
|
21
|
+
/** Maximum complete encoded dataset bytes, including metadata and hierarchy. */
|
|
22
|
+
readonly maxOutputBytes: number;
|
|
23
|
+
/** Input, index, per-node and node-count limits of the shared dynamic tiler. */
|
|
24
|
+
readonly tiling?: Omit<PointCloudTilerOptions, 'signal'>;
|
|
25
|
+
/** Existing version, precision, spacing and hierarchy controls. */
|
|
26
|
+
readonly potree?: Omit<
|
|
27
|
+
NonNullable<PotreeWriterOptions['potree']>,
|
|
28
|
+
'projection' | 'signal' | 'maxOutputBytes'
|
|
29
|
+
>;
|
|
30
|
+
/** Also package the completed files as a loaders.gl STORE ZIP32 bundle. */
|
|
31
|
+
readonly archive?: {readonly maxArchiveBytes: number};
|
|
32
|
+
/** Cooperative cancellation during indexing, gathering, writing and packaging. */
|
|
33
|
+
readonly signal?: AbortSignal;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Complete Potree files; no partial dataset is returned after a failure. */
|
|
37
|
+
export interface ConvertedPointCloudPotree {
|
|
38
|
+
/** Exact input row count, emitted once across the additive octree. */
|
|
39
|
+
readonly pointCount: number;
|
|
40
|
+
/** Number of connected octree nodes, including an empty root for empty input. */
|
|
41
|
+
readonly nodeCount: number;
|
|
42
|
+
/** Metadata and encoded resources; applications own storage. */
|
|
43
|
+
readonly dataset: PotreeDataset;
|
|
44
|
+
/** Optional downloadable bundle; applications own object URL and saving lifetimes. */
|
|
45
|
+
readonly archive?: Blob;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Connects the portable point tiler to the existing Potree 1.x/2.0 dataset writer.
|
|
50
|
+
* Retains additive octree addresses and every source row exactly once, including terminal
|
|
51
|
+
* overflow. Input must be immutable, unindexed decoded points already placed in the declared
|
|
52
|
+
* CRS. No origins, transforms, reprojection, attribute mappings or surface sampling are inferred.
|
|
53
|
+
* The writer rejects attributes or precision that the selected version cannot represent.
|
|
54
|
+
* Gathered nodes are bounded separately from source buffers, indexes, encoded output and ZIP
|
|
55
|
+
* temporaries. This is in-memory authoring, not streaming ingestion or a peak-heap guarantee.
|
|
56
|
+
* The shared tiler's sample coverage bound is not a qualified viewer LOD/error contract.
|
|
57
|
+
* @param input - One decoded point Mesh or MeshArrowTable, including loader output.
|
|
58
|
+
* @param options - Explicit native CRS, resource budgets and existing writer controls.
|
|
59
|
+
* @returns A complete dataset and optional bundle after releasing the owned tiler.
|
|
60
|
+
*/
|
|
61
|
+
export async function convertPointCloudToPotree(
|
|
62
|
+
input: Mesh | MeshArrowTable,
|
|
63
|
+
options: ConvertPointCloudToPotreeOptions
|
|
64
|
+
): Promise<ConvertedPointCloudPotree> {
|
|
65
|
+
if (typeof options.projection !== 'string' || !options.projection.trim()) {
|
|
66
|
+
throw new Error('Potree authoring requires an explicit native CRS declaration');
|
|
67
|
+
}
|
|
68
|
+
for (const limit of [
|
|
69
|
+
options.maxGatheredBytes,
|
|
70
|
+
options.maxOutputBytes,
|
|
71
|
+
options.archive?.maxArchiveBytes
|
|
72
|
+
]) {
|
|
73
|
+
if (limit !== undefined && (!Number.isSafeInteger(limit) || limit < 1)) {
|
|
74
|
+
throw new RangeError('Potree authoring byte limits must be positive safe integers');
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Required budgets must remain required for JavaScript callers too.
|
|
78
|
+
if (options.maxGatheredBytes === undefined || options.maxOutputBytes === undefined) {
|
|
79
|
+
throw new RangeError('Potree authoring requires gathered and encoded byte limits');
|
|
80
|
+
}
|
|
81
|
+
options.signal?.throwIfAborted();
|
|
82
|
+
const tiler = new PointCloudTiler(input, {
|
|
83
|
+
...options.tiling,
|
|
84
|
+
maxNodes: Math.min(options.tiling?.maxNodes ?? 100000, options.potree?.maxNodes ?? 100000),
|
|
85
|
+
maxTileBytes: Math.min(
|
|
86
|
+
options.tiling?.maxTileBytes ?? 64 * 1024 * 1024,
|
|
87
|
+
options.maxGatheredBytes
|
|
88
|
+
),
|
|
89
|
+
signal: options.signal
|
|
90
|
+
});
|
|
91
|
+
const nodes: PotreeWriterNode[] = [];
|
|
92
|
+
let gatheredBytes = 0;
|
|
93
|
+
let pointByteLength = 0;
|
|
94
|
+
let pointCount: number;
|
|
95
|
+
try {
|
|
96
|
+
const pending = [await tiler.getRootNode()];
|
|
97
|
+
pointCount = tiler.pointCount;
|
|
98
|
+
while (pending.length) {
|
|
99
|
+
options.signal?.throwIfAborted();
|
|
100
|
+
const node = pending.pop()!;
|
|
101
|
+
// Root gathering is bounded by maxTileBytes; its packed layout sizes later allocations.
|
|
102
|
+
if (node.pointCount * pointByteLength > options.maxGatheredBytes - gatheredBytes) {
|
|
103
|
+
throw new RangeError('Potree gathered node byte budget exceeded');
|
|
104
|
+
}
|
|
105
|
+
const mesh = await tiler.getTileMesh(node.id);
|
|
106
|
+
const nodeBytes = Array.from(
|
|
107
|
+
new Set(Object.values(mesh.attributes).map(attribute => attribute.value.buffer))
|
|
108
|
+
).reduce((bytes, buffer) => bytes + buffer.byteLength, 0);
|
|
109
|
+
if (nodeBytes > options.maxGatheredBytes - gatheredBytes) {
|
|
110
|
+
throw new RangeError('Potree gathered node byte budget exceeded');
|
|
111
|
+
}
|
|
112
|
+
if (node.pointCount) pointByteLength = nodeBytes / node.pointCount;
|
|
113
|
+
gatheredBytes += nodeBytes;
|
|
114
|
+
nodes.push({id: node.id, mesh});
|
|
115
|
+
const children = await tiler.getChildNodes(node.id);
|
|
116
|
+
for (let index = children.length - 1; index >= 0; index--) pending.push(children[index]);
|
|
117
|
+
}
|
|
118
|
+
} finally {
|
|
119
|
+
tiler.close();
|
|
120
|
+
}
|
|
121
|
+
const dataset = await encodePotreeDataset(nodes, {
|
|
122
|
+
potree: {
|
|
123
|
+
...options.potree,
|
|
124
|
+
projection: options.projection,
|
|
125
|
+
maxOutputBytes: options.maxOutputBytes,
|
|
126
|
+
signal: options.signal
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
const archive = options.archive
|
|
130
|
+
? await encodePotreeDatasetArchive(dataset, options.archive.maxArchiveBytes, options.signal)
|
|
131
|
+
: undefined;
|
|
132
|
+
options.signal?.throwIfAborted();
|
|
133
|
+
return {pointCount, nodeCount: nodes.length, dataset, archive};
|
|
134
|
+
}
|
|
@@ -7,7 +7,7 @@ import type {
|
|
|
7
7
|
PointCloudCoordinateSystem,
|
|
8
8
|
PointCloudTilesetSource,
|
|
9
9
|
TilesetSpatialReference
|
|
10
|
-
} from '@loaders.gl/tiles';
|
|
10
|
+
} from '@loaders.gl/tiles-3d';
|
|
11
11
|
import {transformPointCloudSourceTile} from './point-cloud-spatial.js';
|
|
12
12
|
import type {PointCloudSpatialOptions} from './point-cloud-spatial.js';
|
|
13
13
|
import {encodePointCloudTileWithMetadata} from './point-cloud.js';
|
|
@@ -6,7 +6,7 @@ import type {
|
|
|
6
6
|
PointCloudTileContent,
|
|
7
7
|
PointCloudTileHeader,
|
|
8
8
|
PointCloudTilesetSource
|
|
9
|
-
} from '@loaders.gl/tiles';
|
|
9
|
+
} from '@loaders.gl/tiles-3d';
|
|
10
10
|
|
|
11
11
|
/** A source tile and its decoded point content, including null content when no payload exists. */
|
|
12
12
|
export type PointCloudSourceTile = {
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
5
|
import {Matrix4} from '@math.gl/core';
|
|
6
|
-
import {getSpatialCoordinateFrame, createTilesetSpatialReference} from '@loaders.gl/tiles';
|
|
7
|
-
import {convertTableToMesh, makeMeshArrowTable} from '@loaders.gl/schema-utils';
|
|
6
|
+
import {getSpatialCoordinateFrame, createTilesetSpatialReference} from '@loaders.gl/tiles-3d';
|
|
7
|
+
import {convertTableToMesh, makeMeshArrowTable} from '@loaders.gl/core/schema-utils';
|
|
8
8
|
import {TileConversionError} from '@loaders.gl/tile-converter/v5/core';
|
|
9
9
|
import type {
|
|
10
10
|
I3SConversionSpatialContext,
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// loaders.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import type {Mesh, MeshArrowTable} from '@loaders.gl/core/schema';
|
|
6
|
+
import type {PointCloudTilerOptions} from '@loaders.gl/core/schema-utils';
|
|
7
|
+
import type {TilesetSpatialReference} from '@loaders.gl/tiles-3d';
|
|
8
|
+
import {PointCloudTileSource} from '@loaders.gl/tiles-3d/point-cloud-tile-source';
|
|
9
|
+
import {TileConversionError} from '@loaders.gl/tile-converter/v5/core';
|
|
10
|
+
import type {
|
|
11
|
+
BrowserTileConversionFile,
|
|
12
|
+
TileConversionProgress,
|
|
13
|
+
TileConversionReport
|
|
14
|
+
} from '@loaders.gl/tile-converter/v5/core';
|
|
15
|
+
import {convertPointCloudSource} from './point-cloud-conversion.js';
|
|
16
|
+
import {createPointCloudTilesetSink} from './point-cloud-tileset-sink.js';
|
|
17
|
+
import type {PointCloudTilesetSinkOptions} from './point-cloud-tileset-sink.js';
|
|
18
|
+
import {createTileConversionArchive} from './conversion-archive.js';
|
|
19
|
+
import type {TileConversionArchiveOptions} from './conversion-archive.js';
|
|
20
|
+
|
|
21
|
+
/** Bounded authoring of a complete decoded ECEF point dataset. */
|
|
22
|
+
export interface ConvertPointCloudToTilesetOptions extends PointCloudTilesetSinkOptions {
|
|
23
|
+
/** Explicit native EPSG:4978 XYZ meters with ellipsoidal heights. No CRS is inferred. */
|
|
24
|
+
readonly spatialReference: TilesetSpatialReference;
|
|
25
|
+
/** In-memory input, index, gathered-tile and node limits; defaults belong to PointCloudTiler. */
|
|
26
|
+
readonly tiling?: Omit<PointCloudTilerOptions, 'signal'>;
|
|
27
|
+
/** Also create an indexed 3TZ Blob within this final archive-size budget. */
|
|
28
|
+
readonly archive?: Pick<TileConversionArchiveOptions, 'maxArchiveBytes'>;
|
|
29
|
+
/** Cooperative cancellation during indexing, traversal, encoding and packaging. */
|
|
30
|
+
readonly signal?: AbortSignal;
|
|
31
|
+
/** PNTS conversion progress; excludes tileset JSON and optional archive packaging. */
|
|
32
|
+
readonly onProgress?: (progress: TileConversionProgress) => void;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Completed point authoring result; no partial files are returned on failure. */
|
|
36
|
+
export interface ConvertedPointCloudTileset {
|
|
37
|
+
/** Exact number of input points, each emitted once across the complete collection. */
|
|
38
|
+
readonly pointCount: number;
|
|
39
|
+
/** PNTS resource counts and bytes; input bytes count gathered Arrow column buffers. */
|
|
40
|
+
readonly report: TileConversionReport;
|
|
41
|
+
/** PNTS resources and tileset.json, including JSON in the retained output byte budget. */
|
|
42
|
+
readonly files: readonly BrowserTileConversionFile[];
|
|
43
|
+
/** Optional indexed 3TZ Blob. Applications own saving and object URL lifetimes. */
|
|
44
|
+
readonly archive?: Blob;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Tiles decoded point Mesh/Arrow data and authors a flat additive 3D Tiles 1.0 collection.
|
|
49
|
+
* All input rows are emitted once, including remaining rows at the tiler's maximum depth.
|
|
50
|
+
* Per-tile RTC centers reduce float32 rounding; geometricError gates measured reconstruction error.
|
|
51
|
+
* The flat collection does not preserve octree LOD or provide a sampling-error guarantee.
|
|
52
|
+
* Input must remain immutable until completion. Arrow normalization can copy buffers; retained
|
|
53
|
+
* input, row indexes, gathered tiles, output Blobs and optional ZIP temporaries are separate memory.
|
|
54
|
+
* This is bounded in-memory authoring, not streaming ingestion or an out-of-core converter.
|
|
55
|
+
* Unsupported PNTS attributes fail; callers must explicitly map or remove them beforehand.
|
|
56
|
+
* @param input - One decoded unindexed point-list with finite floating-point XYZ positions.
|
|
57
|
+
* @param options - Explicit ECEF frame, precision and resource limits, and optional 3TZ packaging.
|
|
58
|
+
* @returns Completed files and report after releasing the privately owned dynamic source.
|
|
59
|
+
*/
|
|
60
|
+
export async function convertPointCloudToTileset(
|
|
61
|
+
input: Mesh | MeshArrowTable,
|
|
62
|
+
options: ConvertPointCloudToTilesetOptions
|
|
63
|
+
): Promise<ConvertedPointCloudTileset> {
|
|
64
|
+
const reference = options.spatialReference;
|
|
65
|
+
if (
|
|
66
|
+
!reference ||
|
|
67
|
+
reference.status !== 'native' ||
|
|
68
|
+
reference.sourceCrs !== 'EPSG:4978' ||
|
|
69
|
+
(reference.targetCrs && reference.targetCrs !== 'EPSG:4978') ||
|
|
70
|
+
reference.coordinateFrame !== 'geocentric' ||
|
|
71
|
+
reference.axisOrder !== 'xyz' ||
|
|
72
|
+
reference.heightReference !== 'ellipsoidal' ||
|
|
73
|
+
reference.verticalUnitScale !== 1 ||
|
|
74
|
+
reference.units?.some(unit => unit !== 'meter')
|
|
75
|
+
) {
|
|
76
|
+
throw new TileConversionError(
|
|
77
|
+
'POINT_CLOUD_TILESET_FRAME_UNSUPPORTED',
|
|
78
|
+
'Point authoring requires explicit native EPSG:4978 xyz meters and ellipsoidal heights'
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
if (
|
|
82
|
+
options.archive &&
|
|
83
|
+
(!Number.isSafeInteger(options.archive.maxArchiveBytes) || options.archive.maxArchiveBytes < 0)
|
|
84
|
+
) {
|
|
85
|
+
throw new TileConversionError(
|
|
86
|
+
'INVALID_ARCHIVE_BYTE_LIMIT',
|
|
87
|
+
'Archive byte limit must be a non-negative safe integer'
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
options.signal?.throwIfAborted();
|
|
91
|
+
const sink = createPointCloudTilesetSink(options);
|
|
92
|
+
let source: PointCloudTileSource | undefined;
|
|
93
|
+
try {
|
|
94
|
+
source = new PointCloudTileSource(input, {
|
|
95
|
+
pointCloudTiler: {...options.tiling, signal: options.signal, spatialReference: reference}
|
|
96
|
+
});
|
|
97
|
+
const {pointCount} = await source.getMetadata();
|
|
98
|
+
const report = await convertPointCloudSource(source, {
|
|
99
|
+
sink,
|
|
100
|
+
signal: options.signal,
|
|
101
|
+
onProgress: options.onProgress,
|
|
102
|
+
measureInputBytes: tile => {
|
|
103
|
+
const table = tile.content?.data.data;
|
|
104
|
+
return table
|
|
105
|
+
? table.schema.fields.reduce(
|
|
106
|
+
(bytes, _field, index) => bytes + (table.getChildAt(index)?.byteLength ?? 0),
|
|
107
|
+
0
|
|
108
|
+
)
|
|
109
|
+
: 0;
|
|
110
|
+
},
|
|
111
|
+
getTileEncodingOptions: tile => ({
|
|
112
|
+
rtcCenter: [
|
|
113
|
+
tile.header.boundingVolume.center[0],
|
|
114
|
+
tile.header.boundingVolume.center[1],
|
|
115
|
+
tile.header.boundingVolume.center[2]
|
|
116
|
+
],
|
|
117
|
+
maxPositionError: options.geometricError
|
|
118
|
+
})
|
|
119
|
+
});
|
|
120
|
+
options.signal?.throwIfAborted();
|
|
121
|
+
const files = sink.getFiles();
|
|
122
|
+
const archive = options.archive
|
|
123
|
+
? await createTileConversionArchive(files, {
|
|
124
|
+
format: '3tz',
|
|
125
|
+
maxArchiveBytes: options.archive.maxArchiveBytes,
|
|
126
|
+
signal: options.signal
|
|
127
|
+
})
|
|
128
|
+
: undefined;
|
|
129
|
+
options.signal?.throwIfAborted();
|
|
130
|
+
return {pointCount, report, files, archive};
|
|
131
|
+
} catch (error) {
|
|
132
|
+
await sink.abort(error);
|
|
133
|
+
throw error;
|
|
134
|
+
} finally {
|
|
135
|
+
source?.close();
|
|
136
|
+
}
|
|
137
|
+
}
|
package/src/v5/point-cloud.ts
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
-
import {Tile3DWriter, TILE3D_TYPE} from '@loaders.gl/3d-tiles';
|
|
5
|
+
import {Tile3DWriter, TILE3D_TYPE} from '@loaders.gl/tiles-3d/3d-tiles';
|
|
6
6
|
import type {
|
|
7
7
|
Mesh,
|
|
8
8
|
MeshArrowTable,
|
|
9
9
|
MeshAttribute,
|
|
10
10
|
MeshAttributeQuantizationTransform
|
|
11
|
-
} from '@loaders.gl/schema';
|
|
12
|
-
import {convertTableToMesh} from '@loaders.gl/schema-utils';
|
|
11
|
+
} from '@loaders.gl/core/schema';
|
|
12
|
+
import {convertTableToMesh} from '@loaders.gl/core/schema-utils';
|
|
13
13
|
import {TileConversionError} from '@loaders.gl/tile-converter/v5/core';
|
|
14
14
|
|
|
15
15
|
/** Options for encoding one Arrow or mesh point batch as a 3D Tiles point tile. */
|
|
@@ -101,10 +101,13 @@ export function createTileConversionResourceFetcher(
|
|
|
101
101
|
bytes.set(part, offset);
|
|
102
102
|
offset += part.byteLength;
|
|
103
103
|
}
|
|
104
|
-
|
|
104
|
+
const boundedResponse = new Response(bytes, {
|
|
105
105
|
status: response.status,
|
|
106
106
|
statusText: response.statusText,
|
|
107
107
|
headers: response.headers
|
|
108
108
|
});
|
|
109
|
+
// Relative dependencies must resolve against the final URL after redirects.
|
|
110
|
+
Object.defineProperty(boundedResponse, 'url', {value: response.url});
|
|
111
|
+
return boundedResponse;
|
|
109
112
|
};
|
|
110
113
|
}
|
|
@@ -43,6 +43,17 @@ export interface ManifestBackedTileConversionSinkOptions<TResource> {
|
|
|
43
43
|
readonly sink: TileConversionSink<TResource>;
|
|
44
44
|
/** Persistence adapter for snapshots written after each resource. */
|
|
45
45
|
readonly manifestStore: TileResourceManifestStore;
|
|
46
|
+
/** Optional cap on unique checkpoint entries loaded or written; defaults to no cap. */
|
|
47
|
+
readonly maxResources?: number;
|
|
48
|
+
/**
|
|
49
|
+
* Checks destination bytes before skipping a matching checkpoint. Return true only when
|
|
50
|
+
* stored bytes match the entry's size and fingerprint; false rewrites the resource.
|
|
51
|
+
* Without this callback the caller guarantees checkpointed destination bytes remain intact.
|
|
52
|
+
*/
|
|
53
|
+
readonly verifyResource?: (
|
|
54
|
+
entry: TileResourceManifestEntry,
|
|
55
|
+
signal?: AbortSignal
|
|
56
|
+
) => Promise<boolean>;
|
|
46
57
|
/** Stable profile-specific path or resource key. */
|
|
47
58
|
readonly getResourceId: (resource: TResource) => string;
|
|
48
59
|
/** Byte length used to detect changed content under an existing resource ID. */
|
|
@@ -62,7 +73,8 @@ export interface ManifestBackedTileConversionSinkOptions<TResource> {
|
|
|
62
73
|
* including writes skipped because the matching resource was already present. A graceful abort
|
|
63
74
|
* clears the manifest checkpoint before asking the destination to clean partial output, so the
|
|
64
75
|
* next run starts cleanly. A process interruption that does not call `abort` can resume from its
|
|
65
|
-
* last saved checkpoint.
|
|
76
|
+
* last saved checkpoint. Optional destination verification repairs missing or changed stored
|
|
77
|
+
* resources before treating them as complete. maxResources caps unique checkpoint entries.
|
|
66
78
|
*
|
|
67
79
|
* @param options - Destination, manifest store, stable IDs, byte measurement, and fingerprinting.
|
|
68
80
|
* @returns A `TileConversionSink` with resumable per-resource progress.
|
|
@@ -70,10 +82,17 @@ export interface ManifestBackedTileConversionSinkOptions<TResource> {
|
|
|
70
82
|
export async function createManifestBackedTileConversionSink<TResource>(
|
|
71
83
|
options: ManifestBackedTileConversionSinkOptions<TResource>
|
|
72
84
|
): Promise<TileConversionSink<TResource>> {
|
|
85
|
+
const maxResources = options.maxResources ?? Infinity;
|
|
86
|
+
if (maxResources !== Infinity && (!Number.isSafeInteger(maxResources) || maxResources < 0)) {
|
|
87
|
+
throw new TileConversionError(
|
|
88
|
+
'INVALID_MANIFEST_RESOURCE_LIMIT',
|
|
89
|
+
'maxResources must be a nonnegative safe integer or Infinity'
|
|
90
|
+
);
|
|
91
|
+
}
|
|
73
92
|
const priorManifest = await options.manifestStore.load();
|
|
74
93
|
const resources = new Map<string, TileResourceManifestEntry>();
|
|
75
94
|
if (priorManifest) {
|
|
76
|
-
validateManifest(priorManifest);
|
|
95
|
+
validateManifest(priorManifest, maxResources);
|
|
77
96
|
for (const entry of priorManifest.resources) {
|
|
78
97
|
resources.set(entry.resourceId, entry);
|
|
79
98
|
}
|
|
@@ -102,6 +121,7 @@ export async function createManifestBackedTileConversionSink<TResource>(
|
|
|
102
121
|
if (writeFailed) {
|
|
103
122
|
throw writeFailure;
|
|
104
123
|
}
|
|
124
|
+
signal?.throwIfAborted();
|
|
105
125
|
const entry = createManifestEntry(options, resource);
|
|
106
126
|
const priorEntry = resources.get(entry.resourceId);
|
|
107
127
|
if (priorEntry) {
|
|
@@ -114,7 +134,16 @@ export async function createManifestBackedTileConversionSink<TResource>(
|
|
|
114
134
|
`Resource "${entry.resourceId}" changed since the saved manifest`
|
|
115
135
|
);
|
|
116
136
|
}
|
|
117
|
-
|
|
137
|
+
const verified = options.verifyResource
|
|
138
|
+
? await options.verifyResource({...priorEntry}, signal)
|
|
139
|
+
: true;
|
|
140
|
+
signal?.throwIfAborted();
|
|
141
|
+
if (verified === true) return;
|
|
142
|
+
} else if (resources.size >= maxResources) {
|
|
143
|
+
throw new TileConversionError(
|
|
144
|
+
'MANIFEST_RESOURCE_LIMIT_EXCEEDED',
|
|
145
|
+
'Resource checkpoint exceeds maxResources'
|
|
146
|
+
);
|
|
118
147
|
}
|
|
119
148
|
|
|
120
149
|
await options.sink.write(resource, signal);
|
|
@@ -179,7 +208,7 @@ function createManifestEntry<TResource>(
|
|
|
179
208
|
}
|
|
180
209
|
|
|
181
210
|
/** Checks a loaded manifest before trusting it for restart decisions. */
|
|
182
|
-
function validateManifest(manifest: TileResourceManifest): void {
|
|
211
|
+
function validateManifest(manifest: TileResourceManifest, maxResources: number): void {
|
|
183
212
|
if (
|
|
184
213
|
typeof manifest !== 'object' ||
|
|
185
214
|
manifest === null ||
|
|
@@ -189,7 +218,13 @@ function validateManifest(manifest: TileResourceManifest): void {
|
|
|
189
218
|
) {
|
|
190
219
|
throw new TileConversionError(
|
|
191
220
|
'UNSUPPORTED_RESOURCE_MANIFEST',
|
|
192
|
-
`Unsupported or malformed resource manifest version: ${String(manifest
|
|
221
|
+
`Unsupported or malformed resource manifest version: ${String(manifest?.version)}`
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
if (manifest.resources.length > maxResources) {
|
|
225
|
+
throw new TileConversionError(
|
|
226
|
+
'MANIFEST_RESOURCE_LIMIT_EXCEEDED',
|
|
227
|
+
'Loaded manifest exceeds maxResources'
|
|
193
228
|
);
|
|
194
229
|
}
|
|
195
230
|
const resourceIds = new Set<string>();
|