@loaders.gl/tiles 5.0.0-alpha.2 → 5.0.0-alpha.4
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/dist/dist.dev.js +17325 -5650
- package/dist/dist.min.js +4 -1
- package/dist/index.cjs +1924 -178
- package/dist/index.cjs.map +3 -3
- package/dist/index.d.ts +9 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/point-cloud/point-cloud-tileset.d.ts +4 -0
- package/dist/point-cloud/point-cloud-tileset.d.ts.map +1 -1
- package/dist/point-cloud/point-cloud-tileset.js +51 -12
- package/dist/point-cloud/point-cloud-tileset.js.map +1 -1
- package/dist/point-cloud/types.d.ts +17 -0
- package/dist/point-cloud/types.d.ts.map +1 -1
- package/dist/raster-set.d.ts +10 -8
- package/dist/raster-set.d.ts.map +1 -1
- package/dist/raster-set.js +18 -1
- package/dist/raster-set.js.map +1 -1
- package/dist/spatial/format-spatial-reference.d.ts +67 -0
- package/dist/spatial/format-spatial-reference.d.ts.map +1 -0
- package/dist/spatial/format-spatial-reference.js +233 -0
- package/dist/spatial/format-spatial-reference.js.map +1 -0
- package/dist/spatial/get-spatial-coordinate-frame.d.ts +9 -0
- package/dist/spatial/get-spatial-coordinate-frame.d.ts.map +1 -0
- package/dist/spatial/get-spatial-coordinate-frame.js +58 -0
- package/dist/spatial/get-spatial-coordinate-frame.js.map +1 -0
- package/dist/spatial/i3s-elevation.d.ts +20 -0
- package/dist/spatial/i3s-elevation.d.ts.map +1 -0
- package/dist/spatial/i3s-elevation.js +90 -0
- package/dist/spatial/i3s-elevation.js.map +1 -0
- package/dist/spatial/i3s-spatial-transformer.d.ts +189 -0
- package/dist/spatial/i3s-spatial-transformer.d.ts.map +1 -0
- package/dist/spatial/i3s-spatial-transformer.js +686 -0
- package/dist/spatial/i3s-spatial-transformer.js.map +1 -0
- package/dist/spatial/spatial-coordinate-transformer.d.ts +48 -0
- package/dist/spatial/spatial-coordinate-transformer.d.ts.map +1 -0
- package/dist/spatial/spatial-coordinate-transformer.js +211 -0
- package/dist/spatial/spatial-coordinate-transformer.js.map +1 -0
- package/dist/spatial/spatial-resource-registry.d.ts +44 -0
- package/dist/spatial/spatial-resource-registry.d.ts.map +1 -0
- package/dist/spatial/spatial-resource-registry.js +67 -0
- package/dist/spatial/spatial-resource-registry.js.map +1 -0
- package/dist/spatial/spatial-types.d.ts +190 -0
- package/dist/spatial/spatial-types.d.ts.map +1 -0
- package/dist/spatial/spatial-types.js +192 -0
- package/dist/spatial/spatial-types.js.map +1 -0
- package/dist/tileset-3d/common/tile-3d.d.ts +49 -1
- package/dist/tileset-3d/common/tile-3d.d.ts.map +1 -1
- package/dist/tileset-3d/common/tile-3d.js +132 -64
- package/dist/tileset-3d/common/tile-3d.js.map +1 -1
- package/dist/tileset-3d/common/tileset-3d.d.ts +25 -0
- package/dist/tileset-3d/common/tileset-3d.d.ts.map +1 -1
- package/dist/tileset-3d/common/tileset-3d.js +49 -7
- package/dist/tileset-3d/common/tileset-3d.js.map +1 -1
- package/dist/tileset-3d/common/tileset-source.d.ts +14 -0
- package/dist/tileset-3d/common/tileset-source.d.ts.map +1 -1
- package/dist/tileset-3d/common/tileset-source.js.map +1 -1
- package/dist/tileset-3d/common/tileset-traverser.d.ts +6 -1
- package/dist/tileset-3d/common/tileset-traverser.d.ts.map +1 -1
- package/dist/tileset-3d/common/tileset-traverser.js +11 -6
- package/dist/tileset-3d/common/tileset-traverser.js.map +1 -1
- package/dist/tileset-3d/format-3d-tiles/implicit-tiling.d.ts +26 -1
- package/dist/tileset-3d/format-3d-tiles/implicit-tiling.d.ts.map +1 -1
- package/dist/tileset-3d/format-3d-tiles/implicit-tiling.js +62 -18
- package/dist/tileset-3d/format-3d-tiles/implicit-tiling.js.map +1 -1
- package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.d.ts +1 -7
- package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.d.ts.map +1 -1
- package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.js +32 -55
- package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.js.map +1 -1
- package/dist/tileset-3d/format-i3s/i3s-source.d.ts +14 -1
- package/dist/tileset-3d/format-i3s/i3s-source.d.ts.map +1 -1
- package/dist/tileset-3d/format-i3s/i3s-source.js +107 -17
- package/dist/tileset-3d/format-i3s/i3s-source.js.map +1 -1
- package/dist/tileset-3d/helpers/bounding-volume.d.ts.map +1 -1
- package/dist/tileset-3d/helpers/bounding-volume.js +3 -33
- package/dist/tileset-3d/helpers/bounding-volume.js.map +1 -1
- package/dist/tileset-3d/helpers/frame-state.d.ts +3 -1
- package/dist/tileset-3d/helpers/frame-state.d.ts.map +1 -1
- package/dist/tileset-3d/helpers/frame-state.js +1 -0
- package/dist/tileset-3d/helpers/frame-state.js.map +1 -1
- package/dist/tileset-3d/helpers/i3s-lod.d.ts.map +1 -1
- package/dist/tileset-3d/helpers/i3s-lod.js +20 -8
- package/dist/tileset-3d/helpers/i3s-lod.js.map +1 -1
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +12 -9
- package/src/index.ts +42 -1
- package/src/point-cloud/point-cloud-tileset.ts +66 -12
- package/src/point-cloud/types.ts +17 -0
- package/src/raster-set.ts +46 -11
- package/src/spatial/format-spatial-reference.ts +311 -0
- package/src/spatial/get-spatial-coordinate-frame.ts +74 -0
- package/src/spatial/i3s-elevation.ts +96 -0
- package/src/spatial/i3s-spatial-transformer.ts +957 -0
- package/src/spatial/spatial-coordinate-transformer.ts +276 -0
- package/src/spatial/spatial-resource-registry.ts +79 -0
- package/src/spatial/spatial-types.ts +422 -0
- package/src/tileset-3d/common/tile-3d.ts +156 -69
- package/src/tileset-3d/common/tileset-3d.ts +77 -6
- package/src/tileset-3d/common/tileset-source.ts +15 -0
- package/src/tileset-3d/common/tileset-traverser.ts +11 -6
- package/src/tileset-3d/format-3d-tiles/implicit-tiling.ts +100 -22
- package/src/tileset-3d/format-3d-tiles/tiles-3d-source.ts +36 -56
- package/src/tileset-3d/format-i3s/i3s-source.ts +119 -18
- package/src/tileset-3d/helpers/bounding-volume.ts +3 -55
- package/src/tileset-3d/helpers/frame-state.ts +3 -0
- package/src/tileset-3d/helpers/i3s-lod.ts +22 -8
- package/src/types.ts +3 -0
package/src/raster-set.ts
CHANGED
|
@@ -12,7 +12,7 @@ import type {
|
|
|
12
12
|
/** Accepted raster request currently retained by {@link RasterSet}. */
|
|
13
13
|
export type RasterSetRequest<
|
|
14
14
|
DataT extends RasterData = RasterData,
|
|
15
|
-
ParametersT extends
|
|
15
|
+
ParametersT extends object = GetRasterParameters
|
|
16
16
|
> = {
|
|
17
17
|
/** Unique request id assigned by the manager. */
|
|
18
18
|
requestId: number;
|
|
@@ -25,7 +25,7 @@ export type RasterSetRequest<
|
|
|
25
25
|
/** Arguments supplied to {@link RasterSetBaseProps.shouldRefetch}. */
|
|
26
26
|
export type RasterSetShouldRefetchArgs<
|
|
27
27
|
DataT extends RasterData = RasterData,
|
|
28
|
-
ParametersT extends
|
|
28
|
+
ParametersT extends object = GetRasterParameters,
|
|
29
29
|
MetadataT extends RasterSourceMetadata = RasterSourceMetadata
|
|
30
30
|
> = {
|
|
31
31
|
/** Current accepted request retained by the raster manager, if any. */
|
|
@@ -39,7 +39,7 @@ export type RasterSetShouldRefetchArgs<
|
|
|
39
39
|
/** Configuration shared by all {@link RasterSet} instances. */
|
|
40
40
|
export type RasterSetBaseProps<
|
|
41
41
|
DataT extends RasterData = RasterData,
|
|
42
|
-
ParametersT extends
|
|
42
|
+
ParametersT extends object = GetRasterParameters,
|
|
43
43
|
MetadataT extends RasterSourceMetadata = RasterSourceMetadata
|
|
44
44
|
> = {
|
|
45
45
|
/** Callback used to load source metadata. */
|
|
@@ -55,17 +55,17 @@ export type RasterSetBaseProps<
|
|
|
55
55
|
/** Options for creating a {@link RasterSet}. */
|
|
56
56
|
export type RasterSetProps<
|
|
57
57
|
DataT extends RasterData = RasterData,
|
|
58
|
-
ParametersT extends
|
|
58
|
+
ParametersT extends object = GetRasterParameters,
|
|
59
59
|
MetadataT extends RasterSourceMetadata = RasterSourceMetadata
|
|
60
60
|
> = Partial<RasterSetBaseProps<DataT, ParametersT, MetadataT>> & {
|
|
61
61
|
/** Optional loaders.gl raster source backing this manager. */
|
|
62
|
-
rasterSource?: RasterSource<DataT, ParametersT, MetadataT> | null;
|
|
62
|
+
rasterSource?: RasterSource<DataT, ParametersT & GetRasterParameters, MetadataT> | null;
|
|
63
63
|
};
|
|
64
64
|
|
|
65
65
|
/** Subscription callbacks emitted by {@link RasterSet}. */
|
|
66
66
|
export type RasterSetListener<
|
|
67
67
|
DataT extends RasterData = RasterData,
|
|
68
|
-
ParametersT extends
|
|
68
|
+
ParametersT extends object = GetRasterParameters,
|
|
69
69
|
MetadataT extends RasterSourceMetadata = RasterSourceMetadata
|
|
70
70
|
> = {
|
|
71
71
|
/** Fired when metadata/raster loading starts or stops. */
|
|
@@ -112,7 +112,7 @@ const DEFAULT_RASTERSET_PROPS: Required<Omit<RasterSetProps, 'rasterSource'>> =
|
|
|
112
112
|
*/
|
|
113
113
|
export class RasterSet<
|
|
114
114
|
DataT extends RasterData = RasterData,
|
|
115
|
-
ParametersT extends
|
|
115
|
+
ParametersT extends object = GetRasterParameters,
|
|
116
116
|
MetadataT extends RasterSourceMetadata = RasterSourceMetadata
|
|
117
117
|
> {
|
|
118
118
|
/** Cached metadata returned by the backing source, if any. */
|
|
@@ -153,6 +153,17 @@ export class RasterSet<
|
|
|
153
153
|
return new RasterSet<DataT, ParametersT, MetadataT>({...opts, rasterSource});
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
+
/** Convenience factory for sources whose requests are not viewport-shaped. */
|
|
157
|
+
static fromCallbacks<
|
|
158
|
+
DataT extends RasterData = RasterData,
|
|
159
|
+
ParametersT extends object = GetRasterParameters,
|
|
160
|
+
MetadataT extends RasterSourceMetadata = RasterSourceMetadata
|
|
161
|
+
>(
|
|
162
|
+
opts: RasterSetBaseProps<DataT, ParametersT, MetadataT>
|
|
163
|
+
): RasterSet<DataT, ParametersT, MetadataT> {
|
|
164
|
+
return new RasterSet<DataT, ParametersT, MetadataT>(opts);
|
|
165
|
+
}
|
|
166
|
+
|
|
156
167
|
/** Whether the manager has no requests in flight and a current raster. */
|
|
157
168
|
get isLoaded(): boolean {
|
|
158
169
|
return this._loadCounter === 0 && Boolean(this._currentRequest);
|
|
@@ -169,7 +180,7 @@ export class RasterSet<
|
|
|
169
180
|
}
|
|
170
181
|
|
|
171
182
|
/** Backing raster source when constructed from one. */
|
|
172
|
-
get rasterSource(): RasterSource<DataT, ParametersT, MetadataT> | null {
|
|
183
|
+
get rasterSource(): RasterSource<DataT, ParametersT & GetRasterParameters, MetadataT> | null {
|
|
173
184
|
return this._opts.rasterSource;
|
|
174
185
|
}
|
|
175
186
|
|
|
@@ -279,7 +290,9 @@ export class RasterSet<
|
|
|
279
290
|
try {
|
|
280
291
|
const abortController = new AbortController();
|
|
281
292
|
this._abortController = abortController;
|
|
282
|
-
const raster = await this._opts.getRaster(
|
|
293
|
+
const raster = await this._opts.getRaster(
|
|
294
|
+
withAbortSignal(parameters, abortController.signal)
|
|
295
|
+
);
|
|
283
296
|
if (
|
|
284
297
|
this._finalized ||
|
|
285
298
|
abortController.signal.aborted ||
|
|
@@ -332,9 +345,11 @@ export class RasterSet<
|
|
|
332
345
|
opts.getRaster ||
|
|
333
346
|
((parameters: ParametersT) => {
|
|
334
347
|
if (rasterSource) {
|
|
335
|
-
return rasterSource.getRaster(parameters);
|
|
348
|
+
return rasterSource.getRaster(parameters as ParametersT & GetRasterParameters);
|
|
336
349
|
}
|
|
337
|
-
return DEFAULT_RASTERSET_PROPS.getRaster(
|
|
350
|
+
return DEFAULT_RASTERSET_PROPS.getRaster(
|
|
351
|
+
parameters as GetRasterParameters
|
|
352
|
+
) as Promise<DataT>;
|
|
338
353
|
}),
|
|
339
354
|
debounceTime: opts.debounceTime ?? DEFAULT_RASTERSET_PROPS.debounceTime,
|
|
340
355
|
shouldRefetch:
|
|
@@ -395,6 +410,26 @@ export class RasterSet<
|
|
|
395
410
|
}
|
|
396
411
|
}
|
|
397
412
|
|
|
413
|
+
/** Adds cancellation to a request while preserving its array/prototype shape. */
|
|
414
|
+
function withAbortSignal<ParametersT extends object>(
|
|
415
|
+
parameters: ParametersT,
|
|
416
|
+
signal: AbortSignal
|
|
417
|
+
): ParametersT {
|
|
418
|
+
const request = Array.isArray(parameters)
|
|
419
|
+
? parameters.slice()
|
|
420
|
+
: Object.create(
|
|
421
|
+
Object.getPrototypeOf(parameters),
|
|
422
|
+
Object.getOwnPropertyDescriptors(parameters)
|
|
423
|
+
);
|
|
424
|
+
Object.defineProperty(request, 'signal', {
|
|
425
|
+
configurable: true,
|
|
426
|
+
enumerable: true,
|
|
427
|
+
value: signal,
|
|
428
|
+
writable: true
|
|
429
|
+
});
|
|
430
|
+
return request as ParametersT;
|
|
431
|
+
}
|
|
432
|
+
|
|
398
433
|
/** Normalizes arbitrary thrown values to `Error` instances for raster-set callbacks. */
|
|
399
434
|
function normalizeRasterSetError(error: unknown, message: string): Error {
|
|
400
435
|
if (error instanceof Error) {
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
// loaders.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import type {ReadonlyCRSDefinition} from '@math.gl/crs';
|
|
6
|
+
import {getI3SLinearUnitScale, getI3SVerticalUnitScale} from './i3s-elevation';
|
|
7
|
+
import {createTilesetSpatialReference} from './spatial-types';
|
|
8
|
+
import type {TilesetElevationMode, TilesetSpatialReference} from './spatial-types';
|
|
9
|
+
|
|
10
|
+
/** ArcGIS/I3S spatial reference fields used without depending on the I3S package. */
|
|
11
|
+
type I3SSpatialReferenceLike = {
|
|
12
|
+
wkid?: number;
|
|
13
|
+
latestWkid?: number;
|
|
14
|
+
vcsWkid?: number;
|
|
15
|
+
latestVcsWkid?: number;
|
|
16
|
+
wkt?: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/** I3S layer fields needed for spatial metadata discovery. */
|
|
20
|
+
type I3SLayerLike = {
|
|
21
|
+
spatialReference?: I3SSpatialReferenceLike;
|
|
22
|
+
fullExtent?: {spatialReference?: I3SSpatialReferenceLike};
|
|
23
|
+
heightModelInfo?: {
|
|
24
|
+
heightModel?: string;
|
|
25
|
+
vertCRS?: ReadonlyCRSDefinition;
|
|
26
|
+
heightUnit?: string;
|
|
27
|
+
};
|
|
28
|
+
ZFactor?: number;
|
|
29
|
+
elevationInfo?: {mode?: string; offset?: number; unit?: string};
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/** 3D Tiles metadata schema property used for semantic lookup. */
|
|
33
|
+
type Tiles3DMetadataProperty = {semantic?: string};
|
|
34
|
+
|
|
35
|
+
/** 3D Tiles document fields needed for CRS semantic discovery. */
|
|
36
|
+
type Tiles3DLike = {
|
|
37
|
+
schema?: {
|
|
38
|
+
classes?: Record<string, unknown>;
|
|
39
|
+
};
|
|
40
|
+
schemaUri?: string;
|
|
41
|
+
metadata?: {class?: string; properties?: Record<string, unknown>};
|
|
42
|
+
root?: {boundingVolume?: {region?: unknown}};
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Discovers normalized spatial metadata from an I3S scene-layer document.
|
|
47
|
+
*
|
|
48
|
+
* I3S encodes geographic coordinates in longitude/latitude wire order even for authority
|
|
49
|
+
* definitions whose formal axis order differs.
|
|
50
|
+
*
|
|
51
|
+
* @param layer - Parsed I3S layer document.
|
|
52
|
+
* @returns Normalized source spatial metadata.
|
|
53
|
+
*/
|
|
54
|
+
export function getI3SSpatialReference(layer: I3SLayerLike): TilesetSpatialReference {
|
|
55
|
+
const spatialReference = layer.spatialReference || layer.fullExtent?.spatialReference;
|
|
56
|
+
const sourceCrs = getI3SCrsDefinition(spatialReference);
|
|
57
|
+
const verticalCrs =
|
|
58
|
+
layer.heightModelInfo?.vertCRS || getI3SVerticalCrsDefinition(spatialReference);
|
|
59
|
+
const heightModel = layer.heightModelInfo?.heightModel;
|
|
60
|
+
const verticalUnit = layer.heightModelInfo?.heightUnit;
|
|
61
|
+
const verticalUnitScale = getI3SVerticalUnitScale(verticalUnit, layer.ZFactor);
|
|
62
|
+
const heightReference =
|
|
63
|
+
heightModel === 'ellipsoidal'
|
|
64
|
+
? 'ellipsoidal'
|
|
65
|
+
: heightModel === 'gravity_related_height'
|
|
66
|
+
? 'orthometric'
|
|
67
|
+
: 'unknown';
|
|
68
|
+
const sourceIdentifier = getCrsIdentifier(spatialReference);
|
|
69
|
+
const coordinateFrame =
|
|
70
|
+
sourceIdentifier === undefined
|
|
71
|
+
? getWktCoordinateFrame(spatialReference?.wkt)
|
|
72
|
+
: getIdentifierCoordinateFrame(sourceIdentifier);
|
|
73
|
+
const units = getI3SCoordinateUnits(coordinateFrame, verticalUnit, layer.ZFactor);
|
|
74
|
+
const warnings: string[] = [];
|
|
75
|
+
const elevationMode = getI3SElevationMode(layer.elevationInfo?.mode);
|
|
76
|
+
const elevationUnit = layer.elevationInfo ? layer.elevationInfo.unit || 'meter' : undefined;
|
|
77
|
+
const elevationUnitScale = getI3SLinearUnitScale(elevationUnit);
|
|
78
|
+
if (verticalUnitScale === undefined) {
|
|
79
|
+
warnings.push(
|
|
80
|
+
layer.ZFactor !== undefined
|
|
81
|
+
? `I3S ZFactor ${layer.ZFactor} is not a positive finite conversion factor`
|
|
82
|
+
: `Unsupported I3S vertical unit ${verticalUnit}`
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
if (layer.elevationInfo && elevationUnitScale === undefined) {
|
|
86
|
+
warnings.push(`Unsupported I3S elevation unit ${elevationUnit}`);
|
|
87
|
+
}
|
|
88
|
+
if (layer.elevationInfo?.mode && !elevationMode) {
|
|
89
|
+
warnings.push(`Unsupported I3S elevation mode ${layer.elevationInfo.mode}`);
|
|
90
|
+
}
|
|
91
|
+
if (elevationMode && elevationMode !== 'absoluteHeight') {
|
|
92
|
+
warnings.push(
|
|
93
|
+
`I3S elevation mode ${elevationMode} requires a terrain or scene elevation provider`
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return createTilesetSpatialReference({
|
|
98
|
+
sourceCrs,
|
|
99
|
+
sourceCrsRepresentation:
|
|
100
|
+
sourceIdentifier !== undefined ? 'identifier' : sourceCrs ? 'wkt' : undefined,
|
|
101
|
+
sourceCrsAlternatives:
|
|
102
|
+
sourceIdentifier !== undefined && spatialReference?.wkt
|
|
103
|
+
? [{definition: spatialReference.wkt, representation: 'wkt'}]
|
|
104
|
+
: undefined,
|
|
105
|
+
verticalCrs,
|
|
106
|
+
units,
|
|
107
|
+
verticalUnitScale: verticalUnitScale ?? Number.NaN,
|
|
108
|
+
heightReference,
|
|
109
|
+
elevationMode,
|
|
110
|
+
elevationOffset: layer.elevationInfo?.offset,
|
|
111
|
+
elevationUnit,
|
|
112
|
+
elevationUnitScale:
|
|
113
|
+
layer.elevationInfo?.mode && !elevationMode
|
|
114
|
+
? Number.NaN
|
|
115
|
+
: (elevationUnitScale ?? (layer.elevationInfo ? Number.NaN : 1)),
|
|
116
|
+
coordinateFrame,
|
|
117
|
+
axisOrder: sourceCrs ? 'xyz' : 'unknown',
|
|
118
|
+
provenance: sourceCrs ? 'metadata' : 'unknown',
|
|
119
|
+
warnings
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Return per-component I3S units only when their coordinate association is unambiguous. */
|
|
124
|
+
function getI3SCoordinateUnits(
|
|
125
|
+
coordinateFrame: TilesetSpatialReference['coordinateFrame'],
|
|
126
|
+
verticalUnit: string | undefined,
|
|
127
|
+
zFactor: number | undefined
|
|
128
|
+
): readonly string[] | undefined {
|
|
129
|
+
if (coordinateFrame === 'geocentric') {
|
|
130
|
+
return ['meter', 'meter', 'meter'];
|
|
131
|
+
}
|
|
132
|
+
if (coordinateFrame === 'geographic' && (verticalUnit || zFactor === undefined)) {
|
|
133
|
+
return ['degree', 'degree', verticalUnit || 'meter'];
|
|
134
|
+
}
|
|
135
|
+
return undefined;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** Return a supported I3S elevation mode without widening the public descriptor. */
|
|
139
|
+
function getI3SElevationMode(mode: string | undefined): TilesetElevationMode | undefined {
|
|
140
|
+
switch (mode) {
|
|
141
|
+
case 'absoluteHeight':
|
|
142
|
+
case 'onTheGround':
|
|
143
|
+
case 'relativeToGround':
|
|
144
|
+
case 'relativeToScene':
|
|
145
|
+
return mode;
|
|
146
|
+
default:
|
|
147
|
+
return undefined;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Discovers the 3D Tiles geocentric CRS and coordinate epoch from structured metadata semantics.
|
|
153
|
+
*
|
|
154
|
+
* The method intentionally does not infer ECEF from coordinate magnitude. A root `region`
|
|
155
|
+
* establishes the format's global ellipsoidal frame, while local tilesets without an explicit
|
|
156
|
+
* semantic remain unknown.
|
|
157
|
+
*
|
|
158
|
+
* @param tileset - Parsed 3D Tiles tileset document.
|
|
159
|
+
* @returns Normalized source spatial metadata.
|
|
160
|
+
*/
|
|
161
|
+
export function get3DTilesSpatialReference(tileset: Tiles3DLike): TilesetSpatialReference {
|
|
162
|
+
const geocentricCrs = getTilesetSemanticValue(tileset, 'TILESET_CRS_GEOCENTRIC');
|
|
163
|
+
const coordinateEpoch = getTilesetSemanticValue(tileset, 'TILESET_CRS_COORDINATE_EPOCH');
|
|
164
|
+
const warnings: string[] = [];
|
|
165
|
+
|
|
166
|
+
let sourceCrs: ReadonlyCRSDefinition | undefined;
|
|
167
|
+
let provenance: TilesetSpatialReference['provenance'] = 'unknown';
|
|
168
|
+
let sourceCrsState: 'explicit' | 'default' | 'unknown' | 'absent' | undefined;
|
|
169
|
+
if (typeof geocentricCrs === 'string' && geocentricCrs.toUpperCase() !== 'UNKNOWN') {
|
|
170
|
+
sourceCrs = geocentricCrs;
|
|
171
|
+
provenance = 'metadata';
|
|
172
|
+
sourceCrsState = 'explicit';
|
|
173
|
+
} else if (geocentricCrs !== undefined) {
|
|
174
|
+
warnings.push('3D Tiles geocentric CRS is explicitly unknown');
|
|
175
|
+
provenance = 'metadata';
|
|
176
|
+
sourceCrsState = 'unknown';
|
|
177
|
+
} else if (tileset.root?.boundingVolume?.region) {
|
|
178
|
+
sourceCrs = 'EPSG:4978';
|
|
179
|
+
provenance = 'format-default';
|
|
180
|
+
sourceCrsState = 'default';
|
|
181
|
+
} else if (tileset.schemaUri && !tileset.schema) {
|
|
182
|
+
warnings.push(
|
|
183
|
+
'External 3D Tiles metadata schema must be loaded before CRS semantics can resolve'
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const epoch = parseCoordinateEpoch(coordinateEpoch);
|
|
188
|
+
if (coordinateEpoch !== undefined && epoch === undefined) {
|
|
189
|
+
warnings.push('3D Tiles coordinate epoch is not a finite decimal year');
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return createTilesetSpatialReference({
|
|
193
|
+
sourceCrs,
|
|
194
|
+
sourceCrsState,
|
|
195
|
+
sourceCrsRepresentation: sourceCrs ? 'identifier' : undefined,
|
|
196
|
+
coordinateEpoch: epoch,
|
|
197
|
+
heightReference: sourceCrs ? 'ellipsoidal' : 'unknown',
|
|
198
|
+
coordinateFrame: sourceCrs ? 'geocentric' : 'unknown',
|
|
199
|
+
axisOrder: sourceCrs ? 'xyz' : 'unknown',
|
|
200
|
+
provenance,
|
|
201
|
+
warnings
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** Parse the 3D Tiles decimal-year string while tolerating legacy numeric producer output. */
|
|
206
|
+
function parseCoordinateEpoch(value: unknown): number | undefined {
|
|
207
|
+
if (typeof value !== 'string' && typeof value !== 'number') {
|
|
208
|
+
return undefined;
|
|
209
|
+
}
|
|
210
|
+
if (typeof value === 'string' && !value.trim()) {
|
|
211
|
+
return undefined;
|
|
212
|
+
}
|
|
213
|
+
const epoch = Number(value);
|
|
214
|
+
return Number.isFinite(epoch) ? epoch : undefined;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** Return an I3S horizontal CRS definition, preferring current WKID aliases over legacy values. */
|
|
218
|
+
function getI3SCrsDefinition(
|
|
219
|
+
spatialReference?: I3SSpatialReferenceLike
|
|
220
|
+
): ReadonlyCRSDefinition | undefined {
|
|
221
|
+
const identifier = getCrsIdentifier(spatialReference);
|
|
222
|
+
if (identifier !== undefined) {
|
|
223
|
+
return `EPSG:${identifier}`;
|
|
224
|
+
}
|
|
225
|
+
return spatialReference?.wkt;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/** Return an I3S vertical CRS definition. */
|
|
229
|
+
function getI3SVerticalCrsDefinition(
|
|
230
|
+
spatialReference?: I3SSpatialReferenceLike
|
|
231
|
+
): ReadonlyCRSDefinition | undefined {
|
|
232
|
+
const identifier = spatialReference?.latestVcsWkid ?? spatialReference?.vcsWkid;
|
|
233
|
+
return identifier === undefined ? undefined : `EPSG:${identifier}`;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** Return the preferred horizontal WKID. */
|
|
237
|
+
function getCrsIdentifier(spatialReference?: I3SSpatialReferenceLike): number | undefined {
|
|
238
|
+
return spatialReference?.latestWkid ?? spatialReference?.wkid;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/** Classify common EPSG identifiers used by I3S into their coordinate frames. */
|
|
242
|
+
function getIdentifierCoordinateFrame(
|
|
243
|
+
identifier: number
|
|
244
|
+
): TilesetSpatialReference['coordinateFrame'] {
|
|
245
|
+
if (identifier === 4978) {
|
|
246
|
+
return 'geocentric';
|
|
247
|
+
}
|
|
248
|
+
if (identifier === 4326 || identifier === 4490 || identifier === 4979) {
|
|
249
|
+
return 'geographic';
|
|
250
|
+
}
|
|
251
|
+
return 'projected';
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Classify a WKT root coordinate system without guessing when the declaration is ambiguous.
|
|
256
|
+
*
|
|
257
|
+
* WKT2 uses `GEODCRS` for both geographic and geocentric systems, so its `CS` declaration is
|
|
258
|
+
* inspected before assigning a frame.
|
|
259
|
+
*/
|
|
260
|
+
function getWktCoordinateFrame(wkt?: string): TilesetSpatialReference['coordinateFrame'] {
|
|
261
|
+
if (!wkt) {
|
|
262
|
+
return 'unknown';
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const normalizedWkt = wkt.trim().toUpperCase();
|
|
266
|
+
const rootKeyword = normalizedWkt.match(/^([A-Z][A-Z0-9_]*)\s*[\[(]/)?.[1];
|
|
267
|
+
if (rootKeyword === 'GEOCCS') {
|
|
268
|
+
return 'geocentric';
|
|
269
|
+
}
|
|
270
|
+
if (
|
|
271
|
+
rootKeyword === 'GEOGCS' ||
|
|
272
|
+
rootKeyword === 'GEOGRAPHICCRS' ||
|
|
273
|
+
rootKeyword === 'GEOGRAPHIC2DCRS' ||
|
|
274
|
+
rootKeyword === 'GEOGRAPHIC3DCRS'
|
|
275
|
+
) {
|
|
276
|
+
return 'geographic';
|
|
277
|
+
}
|
|
278
|
+
if (rootKeyword === 'PROJCS' || rootKeyword === 'PROJCRS' || rootKeyword === 'PROJECTEDCRS') {
|
|
279
|
+
return 'projected';
|
|
280
|
+
}
|
|
281
|
+
if (rootKeyword === 'GEODCRS' || rootKeyword === 'GEODETICCRS') {
|
|
282
|
+
if (/\bCS\s*[\[(]\s*CARTESIAN\s*,\s*3\b/.test(normalizedWkt)) {
|
|
283
|
+
return 'geocentric';
|
|
284
|
+
}
|
|
285
|
+
if (/\bCS\s*[\[(]\s*ELLIPSOIDAL\s*,\s*[23]\b/.test(normalizedWkt)) {
|
|
286
|
+
return 'geographic';
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return 'unknown';
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/** Resolve a tileset-wide structured metadata property by its standard semantic. */
|
|
293
|
+
function getTilesetSemanticValue(tileset: Tiles3DLike, semantic: string): unknown {
|
|
294
|
+
const classIdentifier = tileset.metadata?.class;
|
|
295
|
+
if (!classIdentifier) {
|
|
296
|
+
return undefined;
|
|
297
|
+
}
|
|
298
|
+
const classDefinition = tileset.schema?.classes?.[classIdentifier] as
|
|
299
|
+
| {properties?: Record<string, Tiles3DMetadataProperty>}
|
|
300
|
+
| undefined;
|
|
301
|
+
const propertyDefinitions = classDefinition?.properties;
|
|
302
|
+
const propertyValues = tileset.metadata?.properties;
|
|
303
|
+
for (const [propertyIdentifier, propertyDefinition] of Object.entries(
|
|
304
|
+
propertyDefinitions || {}
|
|
305
|
+
)) {
|
|
306
|
+
if (propertyDefinition.semantic === semantic) {
|
|
307
|
+
return propertyValues?.[propertyIdentifier];
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return undefined;
|
|
311
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// loaders.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import type {ReadonlyCRSDefinition, SpatialReferenceCoordinateFrame} from '@math.gl/crs';
|
|
6
|
+
|
|
7
|
+
const GEOGRAPHIC_CRS = 'EPSG:4326';
|
|
8
|
+
const GEOCENTRIC_CRS = 'EPSG:4978';
|
|
9
|
+
|
|
10
|
+
/** Normalize common OGC URL and URN CRS spellings to authority identifiers. */
|
|
11
|
+
function normalizeCrsIdentifier(identifier: string): string {
|
|
12
|
+
const normalizedIdentifier = identifier.trim().toUpperCase();
|
|
13
|
+
const ogcMatch = normalizedIdentifier.match(
|
|
14
|
+
/(?:\/DEF\/CRS\/|URN:OGC:DEF:CRS:)([A-Z0-9_-]+)(?:\/|::)(?:[^/:]*[/:])?([A-Z0-9_.-]+)$/
|
|
15
|
+
);
|
|
16
|
+
return ogcMatch ? `${ogcMatch[1]}:${ogcMatch[2]}` : normalizedIdentifier;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Classify a CRS definition into the broad coordinate frame used by 3D format adapters.
|
|
21
|
+
*
|
|
22
|
+
* @param definition - CRS definition to classify.
|
|
23
|
+
* @returns The broad geographic, geocentric, projected, local, or unknown frame.
|
|
24
|
+
*/
|
|
25
|
+
export function getSpatialCoordinateFrame(
|
|
26
|
+
definition: ReadonlyCRSDefinition
|
|
27
|
+
): SpatialReferenceCoordinateFrame {
|
|
28
|
+
if (typeof definition === 'string') {
|
|
29
|
+
const normalized = normalizeCrsIdentifier(definition);
|
|
30
|
+
if (normalized === GEOCENTRIC_CRS || /^GEOCCS\s*[\[(]/.test(normalized)) {
|
|
31
|
+
return 'geocentric';
|
|
32
|
+
}
|
|
33
|
+
if (
|
|
34
|
+
normalized === GEOGRAPHIC_CRS ||
|
|
35
|
+
normalized === 'EPSG:4490' ||
|
|
36
|
+
normalized === 'EPSG:4979' ||
|
|
37
|
+
normalized === 'OGC:CRS84' ||
|
|
38
|
+
normalized.includes('+PROJ=LONGLAT') ||
|
|
39
|
+
normalized.includes('+PROJ=LATLONG') ||
|
|
40
|
+
/^(?:GEOGCS|GEOGCRS|GEOGRAPHICCRS|GEOGRAPHIC2DCRS|GEOGRAPHIC3DCRS)\s*[\[(]/.test(normalized)
|
|
41
|
+
) {
|
|
42
|
+
return 'geographic';
|
|
43
|
+
}
|
|
44
|
+
if (/^(?:GEODCRS|GEODETICCRS)\s*[\[(]/.test(normalized)) {
|
|
45
|
+
return /CS\s*[\[(]\s*CARTESIAN/.test(normalized) ? 'geocentric' : 'geographic';
|
|
46
|
+
}
|
|
47
|
+
return 'projected';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const projJsonDefinition = definition as {
|
|
51
|
+
type?: string;
|
|
52
|
+
source_crs?: ReadonlyCRSDefinition;
|
|
53
|
+
components?: readonly ReadonlyCRSDefinition[];
|
|
54
|
+
coordinate_system?: {subtype?: string};
|
|
55
|
+
};
|
|
56
|
+
if (projJsonDefinition.type === 'BoundCRS' && projJsonDefinition.source_crs) {
|
|
57
|
+
return getSpatialCoordinateFrame(projJsonDefinition.source_crs);
|
|
58
|
+
}
|
|
59
|
+
if (projJsonDefinition.type === 'CompoundCRS' && projJsonDefinition.components?.[0]) {
|
|
60
|
+
return getSpatialCoordinateFrame(projJsonDefinition.components[0]);
|
|
61
|
+
}
|
|
62
|
+
if (String(projJsonDefinition.type).includes('Projected')) {
|
|
63
|
+
return 'projected';
|
|
64
|
+
}
|
|
65
|
+
if (String(projJsonDefinition.type).includes('Geographic')) {
|
|
66
|
+
return 'geographic';
|
|
67
|
+
}
|
|
68
|
+
if (String(projJsonDefinition.type).includes('Geodetic')) {
|
|
69
|
+
return projJsonDefinition.coordinate_system?.subtype === 'Cartesian'
|
|
70
|
+
? 'geocentric'
|
|
71
|
+
: 'geographic';
|
|
72
|
+
}
|
|
73
|
+
return 'projected';
|
|
74
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// loaders.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
const LINEAR_UNIT_TO_METERS: Readonly<Record<string, number>> = Object.freeze({
|
|
6
|
+
meter: 1,
|
|
7
|
+
millimeter: 0.001,
|
|
8
|
+
centimeter: 0.01,
|
|
9
|
+
decimeter: 0.1,
|
|
10
|
+
kilometer: 1000,
|
|
11
|
+
foot: 0.3048,
|
|
12
|
+
yard: 0.9144,
|
|
13
|
+
mile: 1609.344,
|
|
14
|
+
'us-foot': 1200 / 3937,
|
|
15
|
+
'us-inch': 0.0254000508001016,
|
|
16
|
+
'us-yard': 3600 / 3937,
|
|
17
|
+
'us-mile': 6336000 / 3937,
|
|
18
|
+
'clarke-foot': 0.3047972654,
|
|
19
|
+
'clarke-yard': 0.9143917962,
|
|
20
|
+
'clarke-link': 0.201166195164,
|
|
21
|
+
'sears-yard': 0.914398414616,
|
|
22
|
+
'sears-foot': 0.304799471539,
|
|
23
|
+
'sears-chain': 20.1167651216,
|
|
24
|
+
'sears-1922-truncated-chain': 20.116756,
|
|
25
|
+
'benoit-1895-b-chain': 20.1167824944,
|
|
26
|
+
'indian-yard': 0.914398530744,
|
|
27
|
+
'indian-1937-yard': 0.91439523,
|
|
28
|
+
'gold-coast-foot': 0.304799710181
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const LINEAR_UNIT_ALIASES: Readonly<Record<string, string>> = Object.freeze({
|
|
32
|
+
m: 'meter',
|
|
33
|
+
metre: 'meter',
|
|
34
|
+
meters: 'meter',
|
|
35
|
+
metres: 'meter',
|
|
36
|
+
mm: 'millimeter',
|
|
37
|
+
millimetre: 'millimeter',
|
|
38
|
+
millimeters: 'millimeter',
|
|
39
|
+
millimetres: 'millimeter',
|
|
40
|
+
cm: 'centimeter',
|
|
41
|
+
centimetre: 'centimeter',
|
|
42
|
+
centimeters: 'centimeter',
|
|
43
|
+
centimetres: 'centimeter',
|
|
44
|
+
dm: 'decimeter',
|
|
45
|
+
decimetre: 'decimeter',
|
|
46
|
+
decimeters: 'decimeter',
|
|
47
|
+
decimetres: 'decimeter',
|
|
48
|
+
km: 'kilometer',
|
|
49
|
+
kilometre: 'kilometer',
|
|
50
|
+
kilometers: 'kilometer',
|
|
51
|
+
kilometres: 'kilometer',
|
|
52
|
+
ft: 'foot',
|
|
53
|
+
feet: 'foot',
|
|
54
|
+
'us-survey-foot': 'us-foot',
|
|
55
|
+
'us-survey-feet': 'us-foot',
|
|
56
|
+
yd: 'yard',
|
|
57
|
+
yards: 'yard',
|
|
58
|
+
mi: 'mile',
|
|
59
|
+
miles: 'mile'
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Resolves an ArcGIS/I3S linear unit to meters.
|
|
64
|
+
*
|
|
65
|
+
* @param unit - I3S `heightUnit`, `elevationInfo.unit`, or provider unit.
|
|
66
|
+
* @returns Meters per unit, or `undefined` when the declaration is unsupported.
|
|
67
|
+
*/
|
|
68
|
+
export function getI3SLinearUnitScale(unit: string | undefined): number | undefined {
|
|
69
|
+
if (!unit) {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
const normalized = unit.trim().toLowerCase().replaceAll('_', '-');
|
|
73
|
+
const canonical = LINEAR_UNIT_ALIASES[normalized] || normalized;
|
|
74
|
+
return LINEAR_UNIT_TO_METERS[canonical];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Resolves the source-Z conversion declared by an I3S layer.
|
|
79
|
+
*
|
|
80
|
+
* Legacy `ZFactor` is an explicit conversion to meters and takes precedence over the descriptive
|
|
81
|
+
* `heightUnit`. This avoids applying the same feet-to-meters conversion twice on layers that
|
|
82
|
+
* publish both fields.
|
|
83
|
+
*
|
|
84
|
+
* @param heightUnit - Unit declared by `heightModelInfo.heightUnit`.
|
|
85
|
+
* @param zFactor - Legacy layer conversion factor.
|
|
86
|
+
* @returns Meters per stored Z unit, or `undefined` for an invalid declaration.
|
|
87
|
+
*/
|
|
88
|
+
export function getI3SVerticalUnitScale(
|
|
89
|
+
heightUnit: string | undefined,
|
|
90
|
+
zFactor: number | undefined
|
|
91
|
+
): number | undefined {
|
|
92
|
+
if (zFactor !== undefined) {
|
|
93
|
+
return Number.isFinite(zFactor) && zFactor > 0 ? zFactor : undefined;
|
|
94
|
+
}
|
|
95
|
+
return heightUnit ? getI3SLinearUnitScale(heightUnit) : 1;
|
|
96
|
+
}
|