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

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 (205) hide show
  1. package/LICENSE +14 -3
  2. package/README.md +26 -0
  3. package/dist/dist.dev.js +20271 -5055
  4. package/dist/dist.min.js +3 -1
  5. package/dist/index.cjs +7849 -2081
  6. package/dist/index.cjs.map +3 -4
  7. package/dist/index.d.ts +25 -2
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +11 -0
  10. package/dist/index.js.map +1 -1
  11. package/dist/point-cloud/point-cloud-tile.d.ts.map +1 -1
  12. package/dist/point-cloud/point-cloud-tile.js +3 -0
  13. package/dist/point-cloud/point-cloud-tile.js.map +1 -1
  14. package/dist/point-cloud/point-cloud-tileset.d.ts +4 -0
  15. package/dist/point-cloud/point-cloud-tileset.d.ts.map +1 -1
  16. package/dist/point-cloud/point-cloud-tileset.js +54 -12
  17. package/dist/point-cloud/point-cloud-tileset.js.map +1 -1
  18. package/dist/point-cloud/types.d.ts +17 -0
  19. package/dist/point-cloud/types.d.ts.map +1 -1
  20. package/dist/point-cloud/types.js +3 -0
  21. package/dist/point-cloud/types.js.map +1 -1
  22. package/dist/raster-set.d.ts +48 -31
  23. package/dist/raster-set.d.ts.map +1 -1
  24. package/dist/raster-set.js +104 -24
  25. package/dist/raster-set.js.map +1 -1
  26. package/dist/spatial/format-spatial-reference.d.ts +68 -0
  27. package/dist/spatial/format-spatial-reference.d.ts.map +1 -0
  28. package/dist/spatial/format-spatial-reference.js +291 -0
  29. package/dist/spatial/format-spatial-reference.js.map +1 -0
  30. package/dist/spatial/get-spatial-coordinate-frame.d.ts +20 -0
  31. package/dist/spatial/get-spatial-coordinate-frame.d.ts.map +1 -0
  32. package/dist/spatial/get-spatial-coordinate-frame.js +139 -0
  33. package/dist/spatial/get-spatial-coordinate-frame.js.map +1 -0
  34. package/dist/spatial/i3s-elevation.d.ts +20 -0
  35. package/dist/spatial/i3s-elevation.d.ts.map +1 -0
  36. package/dist/spatial/i3s-elevation.js +90 -0
  37. package/dist/spatial/i3s-elevation.js.map +1 -0
  38. package/dist/spatial/i3s-spatial-transformer.d.ts +189 -0
  39. package/dist/spatial/i3s-spatial-transformer.d.ts.map +1 -0
  40. package/dist/spatial/i3s-spatial-transformer.js +686 -0
  41. package/dist/spatial/i3s-spatial-transformer.js.map +1 -0
  42. package/dist/spatial/normalize-crs-identifier.d.ts +15 -0
  43. package/dist/spatial/normalize-crs-identifier.d.ts.map +1 -0
  44. package/dist/spatial/normalize-crs-identifier.js +22 -0
  45. package/dist/spatial/normalize-crs-identifier.js.map +1 -0
  46. package/dist/spatial/spatial-coordinate-transformer.d.ts +52 -0
  47. package/dist/spatial/spatial-coordinate-transformer.d.ts.map +1 -0
  48. package/dist/spatial/spatial-coordinate-transformer.js +295 -0
  49. package/dist/spatial/spatial-coordinate-transformer.js.map +1 -0
  50. package/dist/spatial/spatial-resource-registry.d.ts +44 -0
  51. package/dist/spatial/spatial-resource-registry.d.ts.map +1 -0
  52. package/dist/spatial/spatial-resource-registry.js +67 -0
  53. package/dist/spatial/spatial-resource-registry.js.map +1 -0
  54. package/dist/spatial/spatial-types.d.ts +190 -0
  55. package/dist/spatial/spatial-types.d.ts.map +1 -0
  56. package/dist/spatial/spatial-types.js +192 -0
  57. package/dist/spatial/spatial-types.js.map +1 -0
  58. package/dist/spatial/tiles-3d-spatial-transformer.d.ts +32 -0
  59. package/dist/spatial/tiles-3d-spatial-transformer.d.ts.map +1 -0
  60. package/dist/spatial/tiles-3d-spatial-transformer.js +175 -0
  61. package/dist/spatial/tiles-3d-spatial-transformer.js.map +1 -0
  62. package/dist/tileset-2d/adapter.d.ts +2 -3
  63. package/dist/tileset-2d/adapter.d.ts.map +1 -1
  64. package/dist/tileset-2d/tileset-2d.d.ts +2 -3
  65. package/dist/tileset-2d/tileset-2d.d.ts.map +1 -1
  66. package/dist/tileset-2d/tileset-2d.js.map +1 -1
  67. package/dist/tileset-3d/common/indexed-archive-tileset-source.d.ts +3 -3
  68. package/dist/tileset-3d/common/indexed-archive-tileset-source.d.ts.map +1 -1
  69. package/dist/tileset-3d/common/indexed-archive-tileset-source.js +9 -4
  70. package/dist/tileset-3d/common/indexed-archive-tileset-source.js.map +1 -1
  71. package/dist/tileset-3d/common/tile-3d-contracts.d.ts +63 -0
  72. package/dist/tileset-3d/common/tile-3d-contracts.d.ts.map +1 -0
  73. package/dist/tileset-3d/common/tile-3d-contracts.js +51 -0
  74. package/dist/tileset-3d/common/tile-3d-contracts.js.map +1 -0
  75. package/dist/tileset-3d/common/tile-3d.d.ts +176 -5
  76. package/dist/tileset-3d/common/tile-3d.d.ts.map +1 -1
  77. package/dist/tileset-3d/common/tile-3d.js +523 -89
  78. package/dist/tileset-3d/common/tile-3d.js.map +1 -1
  79. package/dist/tileset-3d/common/tileset-3d.d.ts +192 -9
  80. package/dist/tileset-3d/common/tileset-3d.d.ts.map +1 -1
  81. package/dist/tileset-3d/common/tileset-3d.js +303 -23
  82. package/dist/tileset-3d/common/tileset-3d.js.map +1 -1
  83. package/dist/tileset-3d/common/tileset-cache.d.ts +11 -1
  84. package/dist/tileset-3d/common/tileset-cache.d.ts.map +1 -1
  85. package/dist/tileset-3d/common/tileset-cache.js +11 -3
  86. package/dist/tileset-3d/common/tileset-cache.js.map +1 -1
  87. package/dist/tileset-3d/common/tileset-content-traversal.d.ts +37 -0
  88. package/dist/tileset-3d/common/tileset-content-traversal.d.ts.map +1 -0
  89. package/dist/tileset-3d/common/tileset-content-traversal.js +80 -0
  90. package/dist/tileset-3d/common/tileset-content-traversal.js.map +1 -0
  91. package/dist/tileset-3d/common/tileset-observability.d.ts +60 -0
  92. package/dist/tileset-3d/common/tileset-observability.d.ts.map +1 -0
  93. package/dist/tileset-3d/common/tileset-observability.js +39 -0
  94. package/dist/tileset-3d/common/tileset-observability.js.map +1 -0
  95. package/dist/tileset-3d/common/tileset-source.d.ts +51 -9
  96. package/dist/tileset-3d/common/tileset-source.d.ts.map +1 -1
  97. package/dist/tileset-3d/common/tileset-source.js.map +1 -1
  98. package/dist/tileset-3d/common/tileset-traverser.d.ts +33 -1
  99. package/dist/tileset-3d/common/tileset-traverser.d.ts.map +1 -1
  100. package/dist/tileset-3d/common/tileset-traverser.js +59 -11
  101. package/dist/tileset-3d/common/tileset-traverser.js.map +1 -1
  102. package/dist/tileset-3d/format-3d-tiles/implicit-tiling.d.ts +182 -0
  103. package/dist/tileset-3d/format-3d-tiles/implicit-tiling.d.ts.map +1 -0
  104. package/dist/tileset-3d/format-3d-tiles/implicit-tiling.js +681 -0
  105. package/dist/tileset-3d/format-3d-tiles/implicit-tiling.js.map +1 -0
  106. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.d.ts +127 -6
  107. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.d.ts.map +1 -1
  108. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.js +485 -27
  109. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.js.map +1 -1
  110. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.d.ts +103 -0
  111. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.d.ts.map +1 -1
  112. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.js +278 -0
  113. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.js.map +1 -1
  114. package/dist/tileset-3d/format-i3s/i3s-pending-tiles-register.d.ts.map +1 -1
  115. package/dist/tileset-3d/format-i3s/i3s-pending-tiles-register.js +3 -0
  116. package/dist/tileset-3d/format-i3s/i3s-pending-tiles-register.js.map +1 -1
  117. package/dist/tileset-3d/format-i3s/i3s-source.d.ts +23 -6
  118. package/dist/tileset-3d/format-i3s/i3s-source.d.ts.map +1 -1
  119. package/dist/tileset-3d/format-i3s/i3s-source.js +176 -25
  120. package/dist/tileset-3d/format-i3s/i3s-source.js.map +1 -1
  121. package/dist/tileset-3d/format-i3s/i3s-tile-manager.d.ts.map +1 -1
  122. package/dist/tileset-3d/format-i3s/i3s-tile-manager.js +3 -0
  123. package/dist/tileset-3d/format-i3s/i3s-tile-manager.js.map +1 -1
  124. package/dist/tileset-3d/format-i3s/i3s-tileset-traverser.d.ts.map +1 -1
  125. package/dist/tileset-3d/format-i3s/i3s-tileset-traverser.js +4 -1
  126. package/dist/tileset-3d/format-i3s/i3s-tileset-traverser.js.map +1 -1
  127. package/dist/tileset-3d/helpers/3d-tiles-options.d.ts.map +1 -1
  128. package/dist/tileset-3d/helpers/3d-tiles-options.js +3 -0
  129. package/dist/tileset-3d/helpers/3d-tiles-options.js.map +1 -1
  130. package/dist/tileset-3d/helpers/bounding-volume.d.ts +7 -3
  131. package/dist/tileset-3d/helpers/bounding-volume.d.ts.map +1 -1
  132. package/dist/tileset-3d/helpers/bounding-volume.js +20 -91
  133. package/dist/tileset-3d/helpers/bounding-volume.js.map +1 -1
  134. package/dist/tileset-3d/helpers/frame-state.d.ts +67 -2
  135. package/dist/tileset-3d/helpers/frame-state.d.ts.map +1 -1
  136. package/dist/tileset-3d/helpers/frame-state.js +92 -10
  137. package/dist/tileset-3d/helpers/frame-state.js.map +1 -1
  138. package/dist/tileset-3d/helpers/i3s-lod.d.ts.map +1 -1
  139. package/dist/tileset-3d/helpers/i3s-lod.js +20 -8
  140. package/dist/tileset-3d/helpers/i3s-lod.js.map +1 -1
  141. package/dist/tileset-3d/helpers/s2-bounding-volume.d.ts +17 -0
  142. package/dist/tileset-3d/helpers/s2-bounding-volume.d.ts.map +1 -0
  143. package/dist/tileset-3d/helpers/s2-bounding-volume.js +20 -0
  144. package/dist/tileset-3d/helpers/s2-bounding-volume.js.map +1 -0
  145. package/dist/tileset-3d/helpers/tiles-3d-lod.d.ts +61 -9
  146. package/dist/tileset-3d/helpers/tiles-3d-lod.d.ts.map +1 -1
  147. package/dist/tileset-3d/helpers/tiles-3d-lod.js +198 -93
  148. package/dist/tileset-3d/helpers/tiles-3d-lod.js.map +1 -1
  149. package/dist/tileset-3d/helpers/tiles-3d-request-priority.d.ts +134 -0
  150. package/dist/tileset-3d/helpers/tiles-3d-request-priority.d.ts.map +1 -0
  151. package/dist/tileset-3d/helpers/tiles-3d-request-priority.js +179 -0
  152. package/dist/tileset-3d/helpers/tiles-3d-request-priority.js.map +1 -0
  153. package/dist/types.d.ts +7 -0
  154. package/dist/types.d.ts.map +1 -1
  155. package/dist/types.js +3 -0
  156. package/dist/types.js.map +1 -1
  157. package/dist/utils/doubly-linked-list.d.ts.map +1 -1
  158. package/dist/utils/doubly-linked-list.js +1 -0
  159. package/dist/utils/doubly-linked-list.js.map +1 -1
  160. package/dist/utils/managed-array.d.ts.map +1 -1
  161. package/dist/utils/managed-array.js +1 -0
  162. package/dist/utils/managed-array.js.map +1 -1
  163. package/package.json +12 -10
  164. package/src/index.ts +78 -1
  165. package/src/point-cloud/point-cloud-tile.ts +4 -0
  166. package/src/point-cloud/point-cloud-tileset.ts +70 -12
  167. package/src/point-cloud/types.ts +21 -0
  168. package/src/raster-set.ts +197 -67
  169. package/src/spatial/format-spatial-reference.ts +385 -0
  170. package/src/spatial/get-spatial-coordinate-frame.ts +169 -0
  171. package/src/spatial/i3s-elevation.ts +96 -0
  172. package/src/spatial/i3s-spatial-transformer.ts +957 -0
  173. package/src/spatial/normalize-crs-identifier.ts +26 -0
  174. package/src/spatial/spatial-coordinate-transformer.ts +376 -0
  175. package/src/spatial/spatial-resource-registry.ts +79 -0
  176. package/src/spatial/spatial-types.ts +422 -0
  177. package/src/spatial/tiles-3d-spatial-transformer.ts +206 -0
  178. package/src/tileset-2d/adapter.ts +2 -3
  179. package/src/tileset-2d/tileset-2d.ts +2 -3
  180. package/src/tileset-3d/common/indexed-archive-tileset-source.ts +13 -13
  181. package/src/tileset-3d/common/tile-3d-contracts.ts +115 -0
  182. package/src/tileset-3d/common/tile-3d.ts +629 -98
  183. package/src/tileset-3d/common/tileset-3d.ts +459 -29
  184. package/src/tileset-3d/common/tileset-cache.ts +15 -7
  185. package/src/tileset-3d/common/tileset-content-traversal.ts +114 -0
  186. package/src/tileset-3d/common/tileset-observability.ts +100 -0
  187. package/src/tileset-3d/common/tileset-source.ts +63 -13
  188. package/src/tileset-3d/common/tileset-traverser.ts +71 -11
  189. package/src/tileset-3d/format-3d-tiles/implicit-tiling.ts +1059 -0
  190. package/src/tileset-3d/format-3d-tiles/tiles-3d-source.ts +674 -28
  191. package/src/tileset-3d/format-3d-tiles/tileset-3d-traverser.ts +316 -0
  192. package/src/tileset-3d/format-i3s/i3s-pending-tiles-register.ts +4 -0
  193. package/src/tileset-3d/format-i3s/i3s-source.ts +220 -29
  194. package/src/tileset-3d/format-i3s/i3s-tile-manager.ts +4 -0
  195. package/src/tileset-3d/format-i3s/i3s-tileset-traverser.ts +6 -1
  196. package/src/tileset-3d/helpers/3d-tiles-options.ts +4 -0
  197. package/src/tileset-3d/helpers/bounding-volume.ts +31 -114
  198. package/src/tileset-3d/helpers/frame-state.ts +152 -13
  199. package/src/tileset-3d/helpers/i3s-lod.ts +22 -8
  200. package/src/tileset-3d/helpers/s2-bounding-volume.ts +35 -0
  201. package/src/tileset-3d/helpers/tiles-3d-lod.ts +291 -112
  202. package/src/tileset-3d/helpers/tiles-3d-request-priority.ts +301 -0
  203. package/src/types.ts +11 -0
  204. package/src/utils/doubly-linked-list.ts +2 -0
  205. package/src/utils/managed-array.ts +2 -0
package/src/raster-set.ts CHANGED
@@ -1,66 +1,89 @@
1
1
  // loaders.gl
2
2
  // SPDX-License-Identifier: MIT
3
- // Copyright (c) vis.gl contributors
3
+ // SPDX-FileCopyrightText: Copyright (c) vis.gl contributors
4
4
 
5
5
  import type {
6
6
  GetRasterParameters,
7
+ NumericRasterData,
7
8
  RasterData,
8
9
  RasterSource,
9
10
  RasterSourceMetadata
10
11
  } from '@loaders.gl/loader-utils';
12
+ import {waitForPromiseWithSignals} from '@loaders.gl/loader-utils';
11
13
 
12
14
  /** Accepted raster request currently retained by {@link RasterSet}. */
13
- export type RasterSetRequest<DataT = RasterData> = {
15
+ export type RasterSetRequest<
16
+ DataT extends NumericRasterData = RasterData,
17
+ ParametersT extends object = GetRasterParameters
18
+ > = {
14
19
  /** Unique request id assigned by the manager. */
15
20
  requestId: number;
16
21
  /** Raster request parameters that produced the accepted raster payload. */
17
- parameters: GetRasterParameters;
22
+ parameters: ParametersT;
18
23
  /** Raster retained as the current output. */
19
24
  raster: DataT;
20
25
  };
21
26
 
22
27
  /** Arguments supplied to {@link RasterSetBaseProps.shouldRefetch}. */
23
- export type RasterSetShouldRefetchArgs<DataT = RasterData> = {
28
+ export type RasterSetShouldRefetchArgs<
29
+ DataT extends NumericRasterData = RasterData,
30
+ ParametersT extends object = GetRasterParameters,
31
+ MetadataT extends RasterSourceMetadata = RasterSourceMetadata
32
+ > = {
24
33
  /** Current accepted request retained by the raster manager, if any. */
25
- currentRequest: RasterSetRequest<DataT> | null;
34
+ currentRequest: RasterSetRequest<DataT, ParametersT> | null;
26
35
  /** Source metadata resolved by {@link RasterSet.loadMetadata}, if available. */
27
- metadata: RasterSourceMetadata | null;
36
+ metadata: MetadataT | null;
28
37
  /** Candidate raster request parameters under evaluation. */
29
- nextParameters: GetRasterParameters;
38
+ nextParameters: ParametersT;
30
39
  };
31
40
 
32
41
  /** Configuration shared by all {@link RasterSet} instances. */
33
- export type RasterSetBaseProps<DataT = RasterData> = {
42
+ export type RasterSetBaseProps<
43
+ DataT extends NumericRasterData = RasterData,
44
+ ParametersT extends object = GetRasterParameters,
45
+ MetadataT extends RasterSourceMetadata = RasterSourceMetadata
46
+ > = {
34
47
  /** Callback used to load source metadata. */
35
- getMetadata: () => Promise<RasterSourceMetadata>;
48
+ getMetadata: (options?: {
49
+ /** Metadata request cancellation. */ signal?: AbortSignal;
50
+ }) => Promise<MetadataT>;
36
51
  /** Callback used to load raster data for a viewport-derived request. */
37
- getRaster: (parameters: GetRasterParameters) => Promise<DataT>;
52
+ getRaster: (parameters: ParametersT) => Promise<DataT>;
38
53
  /** Debounce interval applied before issuing raster requests. */
39
54
  debounceTime?: number;
40
55
  /** Optional policy callback that can skip redundant raster requests. */
41
- shouldRefetch?: (args: RasterSetShouldRefetchArgs<DataT>) => boolean;
56
+ shouldRefetch?: (args: RasterSetShouldRefetchArgs<DataT, ParametersT, MetadataT>) => boolean;
42
57
  };
43
58
 
44
59
  /** Options for creating a {@link RasterSet}. */
45
- export type RasterSetProps<DataT = RasterData> = Partial<RasterSetBaseProps<DataT>> & {
60
+ export type RasterSetProps<
61
+ DataT extends NumericRasterData = RasterData,
62
+ ParametersT extends object = GetRasterParameters,
63
+ MetadataT extends RasterSourceMetadata = RasterSourceMetadata
64
+ > = Partial<RasterSetBaseProps<DataT, ParametersT, MetadataT>> & {
46
65
  /** Optional loaders.gl raster source backing this manager. */
47
- rasterSource?: RasterSource | null;
66
+ rasterSource?: RasterSource<DataT, ParametersT & GetRasterParameters, MetadataT> | null;
48
67
  };
49
68
 
50
69
  /** Subscription callbacks emitted by {@link RasterSet}. */
51
- export type RasterSetListener<DataT = RasterData> = {
70
+ export type RasterSetListener<
71
+ DataT extends NumericRasterData = RasterData,
72
+ ParametersT extends object = GetRasterParameters,
73
+ MetadataT extends RasterSourceMetadata = RasterSourceMetadata
74
+ > = {
52
75
  /** Fired when metadata/raster loading starts or stops. */
53
76
  onLoadingStateChange?: (isLoading: boolean) => void;
54
77
  /** Fired when source metadata resolves successfully. */
55
- onMetadataLoad?: (metadata: RasterSourceMetadata) => void;
78
+ onMetadataLoad?: (metadata: MetadataT) => void;
56
79
  /** Fired when metadata loading fails. */
57
80
  onMetadataLoadError?: (error: Error) => void;
58
81
  /** Fired when a new raster request is issued. */
59
- onRasterLoadStart?: (requestId: number, parameters: GetRasterParameters) => void;
82
+ onRasterLoadStart?: (requestId: number, parameters: ParametersT) => void;
60
83
  /** Fired when a raster request resolves and becomes current. */
61
- onRasterLoad?: (request: RasterSetRequest<DataT>) => void;
84
+ onRasterLoad?: (request: RasterSetRequest<DataT, ParametersT>) => void;
62
85
  /** Fired when a raster request fails. */
63
- onRasterLoadError?: (requestId: number, error: Error, parameters: GetRasterParameters) => void;
86
+ onRasterLoadError?: (requestId: number, error: Error, parameters: ParametersT) => void;
64
87
  /** Fired when metadata or the current raster changes. */
65
88
  onUpdate?: () => void;
66
89
  };
@@ -84,23 +107,38 @@ const DEFAULT_RASTERSET_PROPS: Required<Omit<RasterSetProps, 'rasterSource'>> =
84
107
  shouldRefetch: () => true
85
108
  };
86
109
 
87
- /** Shared raster request manager used by viewport-driven raster examples and layers. */
88
- export class RasterSet<DataT = RasterData> {
110
+ /**
111
+ * Shared raster request manager used by viewport-driven raster examples and layers.
112
+ *
113
+ * @typeParam DataT Raster payload retained by the manager.
114
+ * @typeParam ParametersT Request parameters accepted by the source.
115
+ * @typeParam MetadataT Metadata returned by the source.
116
+ */
117
+ export class RasterSet<
118
+ DataT extends NumericRasterData = RasterData,
119
+ ParametersT extends object = GetRasterParameters,
120
+ MetadataT extends RasterSourceMetadata = RasterSourceMetadata
121
+ > {
89
122
  /** Cached metadata returned by the backing source, if any. */
90
- metadata: RasterSourceMetadata | null = null;
123
+ metadata: MetadataT | null = null;
91
124
 
92
- private _opts: Required<RasterSetProps<DataT>>;
93
- private _listeners = new Set<RasterSetListener<DataT>>();
94
- private _currentRequest: RasterSetRequest<DataT> | null = null;
95
- private _lastAcceptedRequestId = -1;
125
+ private _opts: Required<RasterSetProps<DataT, ParametersT, MetadataT>>;
126
+ private _listeners = new Set<RasterSetListener<DataT, ParametersT, MetadataT>>();
127
+ private _currentRequest: RasterSetRequest<DataT, ParametersT> | null = null;
96
128
  private _nextRequestId = 0;
97
129
  private _loadCounter = 0;
98
130
  private _timeoutId: ReturnType<typeof setTimeout> | null = null;
99
131
  private _abortController: AbortController | null = null;
132
+ /** Controller owned by the newest metadata request. */
133
+ private _metadataAbortController: AbortController | null = null;
100
134
  private _finalized = false;
135
+ /** Generation invalidates metadata from replaced sources. */
136
+ private _sourceGeneration = 0;
137
+ /** Monotonic metadata request identity. */
138
+ private _metadataRequestId = 0;
101
139
 
102
140
  /** Creates a raster manager from a source or direct callbacks. */
103
- constructor(opts: RasterSetProps<DataT>) {
141
+ constructor(opts: RasterSetProps<DataT, ParametersT, MetadataT>) {
104
142
  this._opts = this._normalizeOptions(opts);
105
143
  if (!this._opts.rasterSource && (!opts.getRaster || !opts.getMetadata)) {
106
144
  throw new Error(
@@ -110,11 +148,29 @@ export class RasterSet<DataT = RasterData> {
110
148
  }
111
149
 
112
150
  /** 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});
151
+ static fromRasterSource<
152
+ DataT extends NumericRasterData = RasterData,
153
+ ParametersT extends GetRasterParameters = GetRasterParameters,
154
+ MetadataT extends RasterSourceMetadata = RasterSourceMetadata
155
+ >(
156
+ rasterSource: RasterSource<DataT, ParametersT, MetadataT>,
157
+ opts: Omit<
158
+ RasterSetProps<DataT, ParametersT, MetadataT>,
159
+ 'rasterSource' | 'getRaster' | 'getMetadata'
160
+ > = {}
161
+ ): RasterSet<DataT, ParametersT, MetadataT> {
162
+ return new RasterSet<DataT, ParametersT, MetadataT>({...opts, rasterSource});
163
+ }
164
+
165
+ /** Convenience factory for sources whose requests are not viewport-shaped. */
166
+ static fromCallbacks<
167
+ DataT extends NumericRasterData = RasterData,
168
+ ParametersT extends object = GetRasterParameters,
169
+ MetadataT extends RasterSourceMetadata = RasterSourceMetadata
170
+ >(
171
+ opts: RasterSetBaseProps<DataT, ParametersT, MetadataT>
172
+ ): RasterSet<DataT, ParametersT, MetadataT> {
173
+ return new RasterSet<DataT, ParametersT, MetadataT>(opts);
118
174
  }
119
175
 
120
176
  /** Whether the manager has no requests in flight and a current raster. */
@@ -128,25 +184,30 @@ export class RasterSet<DataT = RasterData> {
128
184
  }
129
185
 
130
186
  /** Current accepted request and raster payload, if any. */
131
- get currentRequest(): RasterSetRequest<DataT> | null {
187
+ get currentRequest(): RasterSetRequest<DataT, ParametersT> | null {
132
188
  return this._currentRequest;
133
189
  }
134
190
 
135
191
  /** Backing raster source when constructed from one. */
136
- get rasterSource(): RasterSource | null {
192
+ get rasterSource(): RasterSource<DataT, ParametersT & GetRasterParameters, MetadataT> | null {
137
193
  return this._opts.rasterSource;
138
194
  }
139
195
 
140
196
  /** Subscribes to metadata and raster lifecycle events. */
141
- subscribe(listener: RasterSetListener<DataT>): () => void {
197
+ subscribe(listener: RasterSetListener<DataT, ParametersT, MetadataT>): () => void {
142
198
  this._listeners.add(listener);
143
199
  return () => this._listeners.delete(listener);
144
200
  }
145
201
 
146
202
  /** Updates source callbacks or debounce settings, resetting state when the source changes. */
147
- setOptions(opts: RasterSetProps<DataT>): void {
203
+ setOptions(opts: RasterSetProps<DataT, ParametersT, MetadataT>): void {
148
204
  const previousRasterSource = this._opts.rasterSource;
149
- const nextOptions = this._normalizeOptions({...this._opts, ...opts});
205
+ const mergedOptions: RasterSetProps<DataT, ParametersT, MetadataT> = {...this._opts, ...opts};
206
+ if (opts.rasterSource && opts.rasterSource !== previousRasterSource) {
207
+ mergedOptions.getRaster = opts.getRaster;
208
+ mergedOptions.getMetadata = opts.getMetadata;
209
+ }
210
+ const nextOptions = this._normalizeOptions(mergedOptions);
150
211
  const sourceChanged =
151
212
  previousRasterSource !== nextOptions.rasterSource ||
152
213
  this._opts.getRaster !== nextOptions.getRaster ||
@@ -156,20 +217,42 @@ export class RasterSet<DataT = RasterData> {
156
217
 
157
218
  if (sourceChanged) {
158
219
  this._cancelScheduledRaster();
220
+ this._abortActiveRequest();
221
+ this._metadataAbortController?.abort();
222
+ this._sourceGeneration++;
159
223
  this.metadata = null;
160
224
  this._currentRequest = null;
161
- this._lastAcceptedRequestId = -1;
162
- this._nextRequestId = 0;
225
+
163
226
  this._emitUpdate();
164
227
  }
165
228
  }
166
229
 
167
230
  /** Loads metadata from the current raster source or callbacks. */
168
- async loadMetadata(): Promise<RasterSourceMetadata> {
169
- this._startLoading();
231
+ async loadMetadata(
232
+ options: {/** Caller cancellation for metadata. */ signal?: AbortSignal} = {}
233
+ ): Promise<MetadataT> {
234
+ if (this._finalized) throw new Error('RasterSet is finalized');
235
+ options.signal?.throwIfAborted();
236
+ this._metadataAbortController?.abort();
237
+ const abortController = new AbortController();
238
+ this._metadataAbortController = abortController;
239
+ const abortFromCaller = () => abortController.abort(options.signal?.reason);
240
+ options.signal?.addEventListener('abort', abortFromCaller, {once: true});
241
+ const metadataRequestId = ++this._metadataRequestId;
242
+ const generation = this._sourceGeneration;
170
243
  try {
171
- const metadata = await this._opts.getMetadata();
172
- if (this._finalized) {
244
+ this._startLoading();
245
+ abortController.signal.throwIfAborted();
246
+ const metadata = await waitForPromiseWithSignals(
247
+ signal => this._opts.getMetadata({signal}),
248
+ [abortController.signal]
249
+ );
250
+ abortController.signal.throwIfAborted();
251
+ if (
252
+ this._finalized ||
253
+ generation !== this._sourceGeneration ||
254
+ metadataRequestId !== this._metadataRequestId
255
+ ) {
173
256
  return metadata;
174
257
  }
175
258
  this.metadata = metadata;
@@ -179,26 +262,39 @@ export class RasterSet<DataT = RasterData> {
179
262
  this._emitUpdate();
180
263
  return metadata;
181
264
  } catch (error) {
265
+ if (abortController.signal.aborted) throw abortController.signal.reason;
266
+ if (isAbortError(error)) throw error;
182
267
  const normalizedError = normalizeRasterSetError(error, 'Raster metadata request failed');
183
- if (!this._finalized) {
268
+ if (
269
+ !this._finalized &&
270
+ generation === this._sourceGeneration &&
271
+ metadataRequestId === this._metadataRequestId
272
+ ) {
184
273
  for (const listener of this._listeners) {
185
274
  listener.onMetadataLoadError?.(normalizedError);
186
275
  }
187
276
  }
188
277
  throw normalizedError;
189
278
  } finally {
279
+ options.signal?.removeEventListener('abort', abortFromCaller);
280
+ if (this._metadataAbortController === abortController) this._metadataAbortController = null;
190
281
  this._finishLoading();
191
282
  }
192
283
  }
193
284
 
194
285
  /** Debounces and issues a new raster request for the supplied parameters. */
195
- requestRaster(parameters: GetRasterParameters, debounceTime = this._opts.debounceTime): number {
286
+ requestRaster(parameters: ParametersT, debounceTime = this._opts.debounceTime): number {
287
+ if (this._finalized) return -1;
196
288
  if (!this.shouldRefetchRaster(parameters)) {
289
+ this._cancelScheduledRaster();
290
+ this._abortActiveRequest();
291
+ this._nextRequestId++;
197
292
  return this._currentRequest?.requestId ?? -1;
198
293
  }
199
294
 
200
295
  const requestId = this._nextRequestId++;
201
296
  this._cancelScheduledRaster();
297
+ this._abortActiveRequest();
202
298
 
203
299
  if (debounceTime > 0) {
204
300
  this._timeoutId = setTimeout(() => {
@@ -213,7 +309,7 @@ export class RasterSet<DataT = RasterData> {
213
309
  }
214
310
 
215
311
  /** Returns `true` when the supplied parameters warrant a new raster fetch. */
216
- shouldRefetchRaster(parameters: GetRasterParameters): boolean {
312
+ shouldRefetchRaster(parameters: ParametersT): boolean {
217
313
  return this._opts.shouldRefetch({
218
314
  currentRequest: this._currentRequest,
219
315
  metadata: this.metadata,
@@ -226,33 +322,41 @@ export class RasterSet<DataT = RasterData> {
226
322
  this._finalized = true;
227
323
  this._cancelScheduledRaster();
228
324
  this._abortActiveRequest();
325
+ this._metadataAbortController?.abort();
326
+ this._metadataAbortController = null;
327
+ this._loadCounter = 0;
229
328
  this._listeners.clear();
230
329
  }
231
330
 
232
331
  /** 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> {
332
+ private async _loadRaster(requestId: number, parameters: ParametersT): Promise<void> {
234
333
  if (this._finalized) {
235
334
  return;
236
335
  }
237
336
 
337
+ const callerSignal = (parameters as {signal?: AbortSignal}).signal;
338
+ if (callerSignal?.aborted) return;
339
+ const abortController = new AbortController();
340
+ this._abortController = abortController;
341
+ const abortFromCaller = () => abortController.abort(callerSignal?.reason);
342
+ callerSignal?.addEventListener('abort', abortFromCaller, {once: true});
238
343
  this._startLoading();
239
- for (const listener of this._listeners) {
240
- listener.onRasterLoadStart?.(requestId, parameters);
241
- }
242
-
243
344
  try {
244
- const abortController = new AbortController();
245
- this._abortController = abortController;
246
- const raster = await this._opts.getRaster({...parameters, signal: abortController.signal});
345
+ abortController.signal.throwIfAborted();
346
+ for (const listener of this._listeners) listener.onRasterLoadStart?.(requestId, parameters);
347
+ abortController.signal.throwIfAborted();
348
+ const raster = await waitForPromiseWithSignals(
349
+ signal => this._opts.getRaster(withAbortSignal(parameters, signal)),
350
+ [abortController.signal]
351
+ );
247
352
  if (
248
353
  this._finalized ||
249
354
  abortController.signal.aborted ||
250
- requestId <= this._lastAcceptedRequestId
355
+ requestId !== this._nextRequestId - 1
251
356
  ) {
252
357
  return;
253
358
  }
254
359
 
255
- this._lastAcceptedRequestId = requestId;
256
360
  this._currentRequest = {requestId, parameters, raster};
257
361
 
258
362
  for (const listener of this._listeners) {
@@ -260,23 +364,26 @@ export class RasterSet<DataT = RasterData> {
260
364
  }
261
365
  this._emitUpdate();
262
366
  } catch (error) {
263
- if (isAbortError(error)) {
367
+ if (abortController.signal.aborted || isAbortError(error)) {
264
368
  return;
265
369
  }
266
370
  const normalizedError = normalizeRasterSetError(error, 'Raster request failed');
267
- if (!this._finalized) {
371
+ if (!this._finalized && requestId === this._nextRequestId - 1) {
268
372
  for (const listener of this._listeners) {
269
373
  listener.onRasterLoadError?.(requestId, normalizedError, parameters);
270
374
  }
271
375
  }
272
376
  } finally {
273
- this._abortController = null;
377
+ callerSignal?.removeEventListener('abort', abortFromCaller);
378
+ if (this._abortController === abortController) this._abortController = null;
274
379
  this._finishLoading();
275
380
  }
276
381
  }
277
382
 
278
383
  /** Resolves current options from either a source or direct callbacks. */
279
- private _normalizeOptions(opts: RasterSetProps<DataT>): Required<RasterSetProps<DataT>> {
384
+ private _normalizeOptions(
385
+ opts: RasterSetProps<DataT, ParametersT, MetadataT>
386
+ ): Required<RasterSetProps<DataT, ParametersT, MetadataT>> {
280
387
  const rasterSource = opts.rasterSource || null;
281
388
  return {
282
389
  ...DEFAULT_RASTERSET_PROPS,
@@ -284,32 +391,35 @@ export class RasterSet<DataT = RasterData> {
284
391
  rasterSource,
285
392
  getMetadata:
286
393
  opts.getMetadata ||
287
- (() => {
394
+ (options => {
288
395
  if (rasterSource) {
289
- return rasterSource.getMetadata();
396
+ return rasterSource.getMetadata(options);
290
397
  }
291
- return DEFAULT_RASTERSET_PROPS.getMetadata();
398
+ return DEFAULT_RASTERSET_PROPS.getMetadata() as Promise<MetadataT>;
292
399
  }),
293
400
  getRaster:
294
401
  opts.getRaster ||
295
- ((parameters: GetRasterParameters) => {
402
+ ((parameters: ParametersT) => {
296
403
  if (rasterSource) {
297
- return rasterSource.getRaster(parameters) as Promise<DataT>;
404
+ return rasterSource.getRaster(parameters as ParametersT & GetRasterParameters);
298
405
  }
299
- return DEFAULT_RASTERSET_PROPS.getRaster(parameters) as Promise<DataT>;
406
+ return DEFAULT_RASTERSET_PROPS.getRaster(
407
+ parameters as GetRasterParameters
408
+ ) as Promise<DataT>;
300
409
  }),
301
410
  debounceTime: opts.debounceTime ?? DEFAULT_RASTERSET_PROPS.debounceTime,
302
411
  shouldRefetch:
303
412
  (opts.shouldRefetch as
304
- | ((args: RasterSetShouldRefetchArgs<DataT>) => boolean)
413
+ | ((args: RasterSetShouldRefetchArgs<DataT, ParametersT, MetadataT>) => boolean)
305
414
  | undefined) ||
306
- ((args: RasterSetShouldRefetchArgs<DataT>) =>
415
+ ((args: RasterSetShouldRefetchArgs<DataT, ParametersT, MetadataT>) =>
307
416
  DEFAULT_RASTERSET_PROPS.shouldRefetch(args as RasterSetShouldRefetchArgs))
308
417
  };
309
418
  }
310
419
 
311
420
  /** Emits one generic update notification to all subscribers. */
312
421
  private _emitUpdate(): void {
422
+ if (this._finalized) return;
313
423
  for (const listener of this._listeners) {
314
424
  listener.onUpdate?.();
315
425
  }
@@ -317,6 +427,7 @@ export class RasterSet<DataT = RasterData> {
317
427
 
318
428
  /** Emits one loading-state transition to all subscribers. */
319
429
  private _emitLoadingStateChange(isLoading: boolean): void {
430
+ if (this._finalized) return;
320
431
  for (const listener of this._listeners) {
321
432
  listener.onLoadingStateChange?.(isLoading);
322
433
  }
@@ -357,6 +468,25 @@ export class RasterSet<DataT = RasterData> {
357
468
  }
358
469
  }
359
470
 
471
+ /** Adds cancellation to a request while preserving its array/prototype shape. */
472
+ function withAbortSignal<ParametersT extends object>(
473
+ parameters: ParametersT,
474
+ signal: AbortSignal
475
+ ): ParametersT {
476
+ const descriptors = Object.getOwnPropertyDescriptors(parameters);
477
+ delete descriptors.signal;
478
+ const request = Array.isArray(parameters)
479
+ ? Object.defineProperties(parameters.slice(), descriptors)
480
+ : Object.create(Object.getPrototypeOf(parameters), descriptors);
481
+ Object.defineProperty(request, 'signal', {
482
+ configurable: true,
483
+ enumerable: true,
484
+ value: signal,
485
+ writable: true
486
+ });
487
+ return request as ParametersT;
488
+ }
489
+
360
490
  /** Normalizes arbitrary thrown values to `Error` instances for raster-set callbacks. */
361
491
  function normalizeRasterSetError(error: unknown, message: string): Error {
362
492
  if (error instanceof Error) {