@loaders.gl/tiles 5.0.0-alpha.1 → 5.0.0-alpha.3

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 (161) hide show
  1. package/LICENSE +14 -3
  2. package/dist/dist.dev.js +17482 -3685
  3. package/dist/dist.min.js +4 -1
  4. package/dist/index.cjs +4594 -422
  5. package/dist/index.cjs.map +3 -4
  6. package/dist/index.d.ts +16 -2
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +7 -0
  9. package/dist/index.js.map +1 -1
  10. package/dist/point-cloud/point-cloud-tile.d.ts.map +1 -1
  11. package/dist/point-cloud/point-cloud-tile.js +3 -0
  12. package/dist/point-cloud/point-cloud-tile.js.map +1 -1
  13. package/dist/point-cloud/point-cloud-tileset.d.ts +4 -0
  14. package/dist/point-cloud/point-cloud-tileset.d.ts.map +1 -1
  15. package/dist/point-cloud/point-cloud-tileset.js +54 -12
  16. package/dist/point-cloud/point-cloud-tileset.js.map +1 -1
  17. package/dist/point-cloud/types.d.ts +17 -0
  18. package/dist/point-cloud/types.d.ts.map +1 -1
  19. package/dist/point-cloud/types.js +3 -0
  20. package/dist/point-cloud/types.js.map +1 -1
  21. package/dist/raster-set.d.ts +37 -29
  22. package/dist/raster-set.d.ts.map +1 -1
  23. package/dist/raster-set.js +25 -2
  24. package/dist/raster-set.js.map +1 -1
  25. package/dist/spatial/format-spatial-reference.d.ts +67 -0
  26. package/dist/spatial/format-spatial-reference.d.ts.map +1 -0
  27. package/dist/spatial/format-spatial-reference.js +233 -0
  28. package/dist/spatial/format-spatial-reference.js.map +1 -0
  29. package/dist/spatial/get-spatial-coordinate-frame.d.ts +9 -0
  30. package/dist/spatial/get-spatial-coordinate-frame.d.ts.map +1 -0
  31. package/dist/spatial/get-spatial-coordinate-frame.js +58 -0
  32. package/dist/spatial/get-spatial-coordinate-frame.js.map +1 -0
  33. package/dist/spatial/i3s-elevation.d.ts +20 -0
  34. package/dist/spatial/i3s-elevation.d.ts.map +1 -0
  35. package/dist/spatial/i3s-elevation.js +90 -0
  36. package/dist/spatial/i3s-elevation.js.map +1 -0
  37. package/dist/spatial/i3s-spatial-transformer.d.ts +189 -0
  38. package/dist/spatial/i3s-spatial-transformer.d.ts.map +1 -0
  39. package/dist/spatial/i3s-spatial-transformer.js +686 -0
  40. package/dist/spatial/i3s-spatial-transformer.js.map +1 -0
  41. package/dist/spatial/spatial-coordinate-transformer.d.ts +48 -0
  42. package/dist/spatial/spatial-coordinate-transformer.d.ts.map +1 -0
  43. package/dist/spatial/spatial-coordinate-transformer.js +211 -0
  44. package/dist/spatial/spatial-coordinate-transformer.js.map +1 -0
  45. package/dist/spatial/spatial-resource-registry.d.ts +44 -0
  46. package/dist/spatial/spatial-resource-registry.d.ts.map +1 -0
  47. package/dist/spatial/spatial-resource-registry.js +67 -0
  48. package/dist/spatial/spatial-resource-registry.js.map +1 -0
  49. package/dist/spatial/spatial-types.d.ts +190 -0
  50. package/dist/spatial/spatial-types.d.ts.map +1 -0
  51. package/dist/spatial/spatial-types.js +192 -0
  52. package/dist/spatial/spatial-types.js.map +1 -0
  53. package/dist/tileset-3d/common/tile-3d.d.ts +147 -4
  54. package/dist/tileset-3d/common/tile-3d.d.ts.map +1 -1
  55. package/dist/tileset-3d/common/tile-3d.js +358 -76
  56. package/dist/tileset-3d/common/tile-3d.js.map +1 -1
  57. package/dist/tileset-3d/common/tileset-3d.d.ts +168 -3
  58. package/dist/tileset-3d/common/tileset-3d.d.ts.map +1 -1
  59. package/dist/tileset-3d/common/tileset-3d.js +306 -23
  60. package/dist/tileset-3d/common/tileset-3d.js.map +1 -1
  61. package/dist/tileset-3d/common/tileset-cache.d.ts +11 -1
  62. package/dist/tileset-3d/common/tileset-cache.d.ts.map +1 -1
  63. package/dist/tileset-3d/common/tileset-cache.js +11 -3
  64. package/dist/tileset-3d/common/tileset-cache.js.map +1 -1
  65. package/dist/tileset-3d/common/tileset-source.d.ts +32 -0
  66. package/dist/tileset-3d/common/tileset-source.d.ts.map +1 -1
  67. package/dist/tileset-3d/common/tileset-source.js.map +1 -1
  68. package/dist/tileset-3d/common/tileset-traverser.d.ts +19 -1
  69. package/dist/tileset-3d/common/tileset-traverser.d.ts.map +1 -1
  70. package/dist/tileset-3d/common/tileset-traverser.js +39 -7
  71. package/dist/tileset-3d/common/tileset-traverser.js.map +1 -1
  72. package/dist/tileset-3d/format-3d-tiles/implicit-tiling.d.ts +136 -0
  73. package/dist/tileset-3d/format-3d-tiles/implicit-tiling.d.ts.map +1 -0
  74. package/dist/tileset-3d/format-3d-tiles/implicit-tiling.js +384 -0
  75. package/dist/tileset-3d/format-3d-tiles/implicit-tiling.js.map +1 -0
  76. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.d.ts +86 -3
  77. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.d.ts.map +1 -1
  78. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.js +196 -21
  79. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.js.map +1 -1
  80. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.d.ts +36 -0
  81. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.d.ts.map +1 -1
  82. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.js +66 -0
  83. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.js.map +1 -1
  84. package/dist/tileset-3d/format-i3s/i3s-pending-tiles-register.d.ts.map +1 -1
  85. package/dist/tileset-3d/format-i3s/i3s-pending-tiles-register.js +3 -0
  86. package/dist/tileset-3d/format-i3s/i3s-pending-tiles-register.js.map +1 -1
  87. package/dist/tileset-3d/format-i3s/i3s-source.d.ts +14 -1
  88. package/dist/tileset-3d/format-i3s/i3s-source.d.ts.map +1 -1
  89. package/dist/tileset-3d/format-i3s/i3s-source.js +107 -17
  90. package/dist/tileset-3d/format-i3s/i3s-source.js.map +1 -1
  91. package/dist/tileset-3d/format-i3s/i3s-tile-manager.d.ts.map +1 -1
  92. package/dist/tileset-3d/format-i3s/i3s-tile-manager.js +3 -0
  93. package/dist/tileset-3d/format-i3s/i3s-tile-manager.js.map +1 -1
  94. package/dist/tileset-3d/format-i3s/i3s-tileset-traverser.d.ts.map +1 -1
  95. package/dist/tileset-3d/format-i3s/i3s-tileset-traverser.js +3 -0
  96. package/dist/tileset-3d/format-i3s/i3s-tileset-traverser.js.map +1 -1
  97. package/dist/tileset-3d/helpers/3d-tiles-options.d.ts.map +1 -1
  98. package/dist/tileset-3d/helpers/3d-tiles-options.js +3 -0
  99. package/dist/tileset-3d/helpers/3d-tiles-options.js.map +1 -1
  100. package/dist/tileset-3d/helpers/bounding-volume.d.ts.map +1 -1
  101. package/dist/tileset-3d/helpers/bounding-volume.js +4 -33
  102. package/dist/tileset-3d/helpers/bounding-volume.js.map +1 -1
  103. package/dist/tileset-3d/helpers/frame-state.d.ts +62 -2
  104. package/dist/tileset-3d/helpers/frame-state.d.ts.map +1 -1
  105. package/dist/tileset-3d/helpers/frame-state.js +85 -9
  106. package/dist/tileset-3d/helpers/frame-state.js.map +1 -1
  107. package/dist/tileset-3d/helpers/i3s-lod.d.ts.map +1 -1
  108. package/dist/tileset-3d/helpers/i3s-lod.js +20 -8
  109. package/dist/tileset-3d/helpers/i3s-lod.js.map +1 -1
  110. package/dist/tileset-3d/helpers/tiles-3d-lod.d.ts +61 -9
  111. package/dist/tileset-3d/helpers/tiles-3d-lod.d.ts.map +1 -1
  112. package/dist/tileset-3d/helpers/tiles-3d-lod.js +198 -93
  113. package/dist/tileset-3d/helpers/tiles-3d-lod.js.map +1 -1
  114. package/dist/tileset-3d/helpers/tiles-3d-request-priority.d.ts +134 -0
  115. package/dist/tileset-3d/helpers/tiles-3d-request-priority.d.ts.map +1 -0
  116. package/dist/tileset-3d/helpers/tiles-3d-request-priority.js +179 -0
  117. package/dist/tileset-3d/helpers/tiles-3d-request-priority.js.map +1 -0
  118. package/dist/types.d.ts +5 -0
  119. package/dist/types.d.ts.map +1 -1
  120. package/dist/types.js +3 -0
  121. package/dist/types.js.map +1 -1
  122. package/dist/utils/doubly-linked-list.d.ts.map +1 -1
  123. package/dist/utils/doubly-linked-list.js +1 -0
  124. package/dist/utils/doubly-linked-list.js.map +1 -1
  125. package/dist/utils/managed-array.d.ts.map +1 -1
  126. package/dist/utils/managed-array.js +1 -0
  127. package/dist/utils/managed-array.js.map +1 -1
  128. package/package.json +12 -9
  129. package/src/index.ts +61 -1
  130. package/src/point-cloud/point-cloud-tile.ts +4 -0
  131. package/src/point-cloud/point-cloud-tileset.ts +70 -12
  132. package/src/point-cloud/types.ts +21 -0
  133. package/src/raster-set.ts +118 -45
  134. package/src/spatial/format-spatial-reference.ts +311 -0
  135. package/src/spatial/get-spatial-coordinate-frame.ts +74 -0
  136. package/src/spatial/i3s-elevation.ts +96 -0
  137. package/src/spatial/i3s-spatial-transformer.ts +957 -0
  138. package/src/spatial/spatial-coordinate-transformer.ts +276 -0
  139. package/src/spatial/spatial-resource-registry.ts +79 -0
  140. package/src/spatial/spatial-types.ts +422 -0
  141. package/src/tileset-3d/common/tile-3d.ts +435 -84
  142. package/src/tileset-3d/common/tileset-3d.ts +445 -23
  143. package/src/tileset-3d/common/tileset-cache.ts +15 -7
  144. package/src/tileset-3d/common/tileset-source.ts +36 -0
  145. package/src/tileset-3d/common/tileset-traverser.ts +40 -7
  146. package/src/tileset-3d/format-3d-tiles/implicit-tiling.ts +612 -0
  147. package/src/tileset-3d/format-3d-tiles/tiles-3d-source.ts +254 -21
  148. package/src/tileset-3d/format-3d-tiles/tileset-3d-traverser.ts +77 -0
  149. package/src/tileset-3d/format-i3s/i3s-pending-tiles-register.ts +4 -0
  150. package/src/tileset-3d/format-i3s/i3s-source.ts +119 -18
  151. package/src/tileset-3d/format-i3s/i3s-tile-manager.ts +4 -0
  152. package/src/tileset-3d/format-i3s/i3s-tileset-traverser.ts +4 -0
  153. package/src/tileset-3d/helpers/3d-tiles-options.ts +4 -0
  154. package/src/tileset-3d/helpers/bounding-volume.ts +5 -55
  155. package/src/tileset-3d/helpers/frame-state.ts +142 -11
  156. package/src/tileset-3d/helpers/i3s-lod.ts +22 -8
  157. package/src/tileset-3d/helpers/tiles-3d-lod.ts +291 -112
  158. package/src/tileset-3d/helpers/tiles-3d-request-priority.ts +301 -0
  159. package/src/types.ts +9 -0
  160. package/src/utils/doubly-linked-list.ts +2 -0
  161. package/src/utils/managed-array.ts +2 -0
package/src/raster-set.ts CHANGED
@@ -10,57 +10,76 @@ import type {
10
10
  } from '@loaders.gl/loader-utils';
11
11
 
12
12
  /** Accepted raster request currently retained by {@link RasterSet}. */
13
- export type RasterSetRequest<DataT = RasterData> = {
13
+ export type RasterSetRequest<
14
+ DataT extends RasterData = RasterData,
15
+ ParametersT extends object = GetRasterParameters
16
+ > = {
14
17
  /** Unique request id assigned by the manager. */
15
18
  requestId: number;
16
19
  /** Raster request parameters that produced the accepted raster payload. */
17
- parameters: GetRasterParameters;
20
+ parameters: ParametersT;
18
21
  /** Raster retained as the current output. */
19
22
  raster: DataT;
20
23
  };
21
24
 
22
25
  /** Arguments supplied to {@link RasterSetBaseProps.shouldRefetch}. */
23
- export type RasterSetShouldRefetchArgs<DataT = RasterData> = {
26
+ export type RasterSetShouldRefetchArgs<
27
+ DataT extends RasterData = RasterData,
28
+ ParametersT extends object = GetRasterParameters,
29
+ MetadataT extends RasterSourceMetadata = RasterSourceMetadata
30
+ > = {
24
31
  /** Current accepted request retained by the raster manager, if any. */
25
- currentRequest: RasterSetRequest<DataT> | null;
32
+ currentRequest: RasterSetRequest<DataT, ParametersT> | null;
26
33
  /** Source metadata resolved by {@link RasterSet.loadMetadata}, if available. */
27
- metadata: RasterSourceMetadata | null;
34
+ metadata: MetadataT | null;
28
35
  /** Candidate raster request parameters under evaluation. */
29
- nextParameters: GetRasterParameters;
36
+ nextParameters: ParametersT;
30
37
  };
31
38
 
32
39
  /** Configuration shared by all {@link RasterSet} instances. */
33
- export type RasterSetBaseProps<DataT = RasterData> = {
40
+ export type RasterSetBaseProps<
41
+ DataT extends RasterData = RasterData,
42
+ ParametersT extends object = GetRasterParameters,
43
+ MetadataT extends RasterSourceMetadata = RasterSourceMetadata
44
+ > = {
34
45
  /** Callback used to load source metadata. */
35
- getMetadata: () => Promise<RasterSourceMetadata>;
46
+ getMetadata: () => Promise<MetadataT>;
36
47
  /** Callback used to load raster data for a viewport-derived request. */
37
- getRaster: (parameters: GetRasterParameters) => Promise<DataT>;
48
+ getRaster: (parameters: ParametersT) => Promise<DataT>;
38
49
  /** Debounce interval applied before issuing raster requests. */
39
50
  debounceTime?: number;
40
51
  /** Optional policy callback that can skip redundant raster requests. */
41
- shouldRefetch?: (args: RasterSetShouldRefetchArgs<DataT>) => boolean;
52
+ shouldRefetch?: (args: RasterSetShouldRefetchArgs<DataT, ParametersT, MetadataT>) => boolean;
42
53
  };
43
54
 
44
55
  /** Options for creating a {@link RasterSet}. */
45
- export type RasterSetProps<DataT = RasterData> = Partial<RasterSetBaseProps<DataT>> & {
56
+ export type RasterSetProps<
57
+ DataT extends RasterData = RasterData,
58
+ ParametersT extends object = GetRasterParameters,
59
+ MetadataT extends RasterSourceMetadata = RasterSourceMetadata
60
+ > = Partial<RasterSetBaseProps<DataT, ParametersT, MetadataT>> & {
46
61
  /** Optional loaders.gl raster source backing this manager. */
47
- rasterSource?: RasterSource | null;
62
+ rasterSource?: RasterSource<DataT, ParametersT & GetRasterParameters, MetadataT> | null;
48
63
  };
49
64
 
50
65
  /** Subscription callbacks emitted by {@link RasterSet}. */
51
- export type RasterSetListener<DataT = RasterData> = {
66
+ export type RasterSetListener<
67
+ DataT extends RasterData = RasterData,
68
+ ParametersT extends object = GetRasterParameters,
69
+ MetadataT extends RasterSourceMetadata = RasterSourceMetadata
70
+ > = {
52
71
  /** Fired when metadata/raster loading starts or stops. */
53
72
  onLoadingStateChange?: (isLoading: boolean) => void;
54
73
  /** Fired when source metadata resolves successfully. */
55
- onMetadataLoad?: (metadata: RasterSourceMetadata) => void;
74
+ onMetadataLoad?: (metadata: MetadataT) => void;
56
75
  /** Fired when metadata loading fails. */
57
76
  onMetadataLoadError?: (error: Error) => void;
58
77
  /** Fired when a new raster request is issued. */
59
- onRasterLoadStart?: (requestId: number, parameters: GetRasterParameters) => void;
78
+ onRasterLoadStart?: (requestId: number, parameters: ParametersT) => void;
60
79
  /** Fired when a raster request resolves and becomes current. */
61
- onRasterLoad?: (request: RasterSetRequest<DataT>) => void;
80
+ onRasterLoad?: (request: RasterSetRequest<DataT, ParametersT>) => void;
62
81
  /** Fired when a raster request fails. */
63
- onRasterLoadError?: (requestId: number, error: Error, parameters: GetRasterParameters) => void;
82
+ onRasterLoadError?: (requestId: number, error: Error, parameters: ParametersT) => void;
64
83
  /** Fired when metadata or the current raster changes. */
65
84
  onUpdate?: () => void;
66
85
  };
@@ -84,14 +103,24 @@ const DEFAULT_RASTERSET_PROPS: Required<Omit<RasterSetProps, 'rasterSource'>> =
84
103
  shouldRefetch: () => true
85
104
  };
86
105
 
87
- /** Shared raster request manager used by viewport-driven raster examples and layers. */
88
- export class RasterSet<DataT = RasterData> {
106
+ /**
107
+ * Shared raster request manager used by viewport-driven raster examples and layers.
108
+ *
109
+ * @typeParam DataT Raster payload retained by the manager.
110
+ * @typeParam ParametersT Request parameters accepted by the source.
111
+ * @typeParam MetadataT Metadata returned by the source.
112
+ */
113
+ export class RasterSet<
114
+ DataT extends RasterData = RasterData,
115
+ ParametersT extends object = GetRasterParameters,
116
+ MetadataT extends RasterSourceMetadata = RasterSourceMetadata
117
+ > {
89
118
  /** Cached metadata returned by the backing source, if any. */
90
- metadata: RasterSourceMetadata | null = null;
119
+ metadata: MetadataT | null = null;
91
120
 
92
- private _opts: Required<RasterSetProps<DataT>>;
93
- private _listeners = new Set<RasterSetListener<DataT>>();
94
- private _currentRequest: RasterSetRequest<DataT> | null = null;
121
+ private _opts: Required<RasterSetProps<DataT, ParametersT, MetadataT>>;
122
+ private _listeners = new Set<RasterSetListener<DataT, ParametersT, MetadataT>>();
123
+ private _currentRequest: RasterSetRequest<DataT, ParametersT> | null = null;
95
124
  private _lastAcceptedRequestId = -1;
96
125
  private _nextRequestId = 0;
97
126
  private _loadCounter = 0;
@@ -100,7 +129,7 @@ export class RasterSet<DataT = RasterData> {
100
129
  private _finalized = false;
101
130
 
102
131
  /** Creates a raster manager from a source or direct callbacks. */
103
- constructor(opts: RasterSetProps<DataT>) {
132
+ constructor(opts: RasterSetProps<DataT, ParametersT, MetadataT>) {
104
133
  this._opts = this._normalizeOptions(opts);
105
134
  if (!this._opts.rasterSource && (!opts.getRaster || !opts.getMetadata)) {
106
135
  throw new Error(
@@ -110,11 +139,29 @@ export class RasterSet<DataT = RasterData> {
110
139
  }
111
140
 
112
141
  /** Convenience factory for wrapping a loaders.gl {@link RasterSource}. */
113
- static fromRasterSource<DataT = RasterData>(
114
- rasterSource: RasterSource,
115
- opts: Omit<RasterSetProps<DataT>, 'rasterSource' | 'getRaster' | 'getMetadata'> = {}
116
- ): RasterSet<DataT> {
117
- return new RasterSet<DataT>({...opts, rasterSource});
142
+ static fromRasterSource<
143
+ DataT extends RasterData = RasterData,
144
+ ParametersT extends GetRasterParameters = GetRasterParameters,
145
+ MetadataT extends RasterSourceMetadata = RasterSourceMetadata
146
+ >(
147
+ rasterSource: RasterSource<DataT, ParametersT, MetadataT>,
148
+ opts: Omit<
149
+ RasterSetProps<DataT, ParametersT, MetadataT>,
150
+ 'rasterSource' | 'getRaster' | 'getMetadata'
151
+ > = {}
152
+ ): RasterSet<DataT, ParametersT, MetadataT> {
153
+ return new RasterSet<DataT, ParametersT, MetadataT>({...opts, rasterSource});
154
+ }
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);
118
165
  }
119
166
 
120
167
  /** Whether the manager has no requests in flight and a current raster. */
@@ -128,23 +175,23 @@ export class RasterSet<DataT = RasterData> {
128
175
  }
129
176
 
130
177
  /** Current accepted request and raster payload, if any. */
131
- get currentRequest(): RasterSetRequest<DataT> | null {
178
+ get currentRequest(): RasterSetRequest<DataT, ParametersT> | null {
132
179
  return this._currentRequest;
133
180
  }
134
181
 
135
182
  /** Backing raster source when constructed from one. */
136
- get rasterSource(): RasterSource | null {
183
+ get rasterSource(): RasterSource<DataT, ParametersT & GetRasterParameters, MetadataT> | null {
137
184
  return this._opts.rasterSource;
138
185
  }
139
186
 
140
187
  /** Subscribes to metadata and raster lifecycle events. */
141
- subscribe(listener: RasterSetListener<DataT>): () => void {
188
+ subscribe(listener: RasterSetListener<DataT, ParametersT, MetadataT>): () => void {
142
189
  this._listeners.add(listener);
143
190
  return () => this._listeners.delete(listener);
144
191
  }
145
192
 
146
193
  /** Updates source callbacks or debounce settings, resetting state when the source changes. */
147
- setOptions(opts: RasterSetProps<DataT>): void {
194
+ setOptions(opts: RasterSetProps<DataT, ParametersT, MetadataT>): void {
148
195
  const previousRasterSource = this._opts.rasterSource;
149
196
  const nextOptions = this._normalizeOptions({...this._opts, ...opts});
150
197
  const sourceChanged =
@@ -165,7 +212,7 @@ export class RasterSet<DataT = RasterData> {
165
212
  }
166
213
 
167
214
  /** Loads metadata from the current raster source or callbacks. */
168
- async loadMetadata(): Promise<RasterSourceMetadata> {
215
+ async loadMetadata(): Promise<MetadataT> {
169
216
  this._startLoading();
170
217
  try {
171
218
  const metadata = await this._opts.getMetadata();
@@ -192,7 +239,7 @@ export class RasterSet<DataT = RasterData> {
192
239
  }
193
240
 
194
241
  /** Debounces and issues a new raster request for the supplied parameters. */
195
- requestRaster(parameters: GetRasterParameters, debounceTime = this._opts.debounceTime): number {
242
+ requestRaster(parameters: ParametersT, debounceTime = this._opts.debounceTime): number {
196
243
  if (!this.shouldRefetchRaster(parameters)) {
197
244
  return this._currentRequest?.requestId ?? -1;
198
245
  }
@@ -213,7 +260,7 @@ export class RasterSet<DataT = RasterData> {
213
260
  }
214
261
 
215
262
  /** Returns `true` when the supplied parameters warrant a new raster fetch. */
216
- shouldRefetchRaster(parameters: GetRasterParameters): boolean {
263
+ shouldRefetchRaster(parameters: ParametersT): boolean {
217
264
  return this._opts.shouldRefetch({
218
265
  currentRequest: this._currentRequest,
219
266
  metadata: this.metadata,
@@ -230,7 +277,7 @@ export class RasterSet<DataT = RasterData> {
230
277
  }
231
278
 
232
279
  /** Loads a raster immediately and accepts it only if it is the newest completed request. */
233
- private async _loadRaster(requestId: number, parameters: GetRasterParameters): Promise<void> {
280
+ private async _loadRaster(requestId: number, parameters: ParametersT): Promise<void> {
234
281
  if (this._finalized) {
235
282
  return;
236
283
  }
@@ -243,7 +290,9 @@ export class RasterSet<DataT = RasterData> {
243
290
  try {
244
291
  const abortController = new AbortController();
245
292
  this._abortController = abortController;
246
- const raster = await this._opts.getRaster({...parameters, signal: abortController.signal});
293
+ const raster = await this._opts.getRaster(
294
+ withAbortSignal(parameters, abortController.signal)
295
+ );
247
296
  if (
248
297
  this._finalized ||
249
298
  abortController.signal.aborted ||
@@ -276,7 +325,9 @@ export class RasterSet<DataT = RasterData> {
276
325
  }
277
326
 
278
327
  /** Resolves current options from either a source or direct callbacks. */
279
- private _normalizeOptions(opts: RasterSetProps<DataT>): Required<RasterSetProps<DataT>> {
328
+ private _normalizeOptions(
329
+ opts: RasterSetProps<DataT, ParametersT, MetadataT>
330
+ ): Required<RasterSetProps<DataT, ParametersT, MetadataT>> {
280
331
  const rasterSource = opts.rasterSource || null;
281
332
  return {
282
333
  ...DEFAULT_RASTERSET_PROPS,
@@ -288,22 +339,24 @@ export class RasterSet<DataT = RasterData> {
288
339
  if (rasterSource) {
289
340
  return rasterSource.getMetadata();
290
341
  }
291
- return DEFAULT_RASTERSET_PROPS.getMetadata();
342
+ return DEFAULT_RASTERSET_PROPS.getMetadata() as Promise<MetadataT>;
292
343
  }),
293
344
  getRaster:
294
345
  opts.getRaster ||
295
- ((parameters: GetRasterParameters) => {
346
+ ((parameters: ParametersT) => {
296
347
  if (rasterSource) {
297
- return rasterSource.getRaster(parameters) as Promise<DataT>;
348
+ return rasterSource.getRaster(parameters as ParametersT & GetRasterParameters);
298
349
  }
299
- return DEFAULT_RASTERSET_PROPS.getRaster(parameters) as Promise<DataT>;
350
+ return DEFAULT_RASTERSET_PROPS.getRaster(
351
+ parameters as GetRasterParameters
352
+ ) as Promise<DataT>;
300
353
  }),
301
354
  debounceTime: opts.debounceTime ?? DEFAULT_RASTERSET_PROPS.debounceTime,
302
355
  shouldRefetch:
303
356
  (opts.shouldRefetch as
304
- | ((args: RasterSetShouldRefetchArgs<DataT>) => boolean)
357
+ | ((args: RasterSetShouldRefetchArgs<DataT, ParametersT, MetadataT>) => boolean)
305
358
  | undefined) ||
306
- ((args: RasterSetShouldRefetchArgs<DataT>) =>
359
+ ((args: RasterSetShouldRefetchArgs<DataT, ParametersT, MetadataT>) =>
307
360
  DEFAULT_RASTERSET_PROPS.shouldRefetch(args as RasterSetShouldRefetchArgs))
308
361
  };
309
362
  }
@@ -357,6 +410,26 @@ export class RasterSet<DataT = RasterData> {
357
410
  }
358
411
  }
359
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
+
360
433
  /** Normalizes arbitrary thrown values to `Error` instances for raster-set callbacks. */
361
434
  function normalizeRasterSetError(error: unknown, message: string): Error {
362
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
+ }