@loaders.gl/arcgis 5.0.0-alpha.8

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 (119) hide show
  1. package/README.md +26 -0
  2. package/dist/arcgis/arcgis-capability-graph.d.ts +50 -0
  3. package/dist/arcgis/arcgis-capability-graph.d.ts.map +1 -0
  4. package/dist/arcgis/arcgis-capability-graph.js +221 -0
  5. package/dist/arcgis/arcgis-capability-graph.js.map +1 -0
  6. package/dist/arcgis/arcgis-feature-query.d.ts +53 -0
  7. package/dist/arcgis/arcgis-feature-query.d.ts.map +1 -0
  8. package/dist/arcgis/arcgis-feature-query.js +381 -0
  9. package/dist/arcgis/arcgis-feature-query.js.map +1 -0
  10. package/dist/arcgis/arcgis-feature-server-source-loader-types.d.ts +33 -0
  11. package/dist/arcgis/arcgis-feature-server-source-loader-types.d.ts.map +1 -0
  12. package/dist/arcgis/arcgis-feature-server-source-loader-types.js +21 -0
  13. package/dist/arcgis/arcgis-feature-server-source-loader-types.js.map +1 -0
  14. package/dist/arcgis/arcgis-feature-server-source-loader.d.ts +84 -0
  15. package/dist/arcgis/arcgis-feature-server-source-loader.d.ts.map +1 -0
  16. package/dist/arcgis/arcgis-feature-server-source-loader.js +280 -0
  17. package/dist/arcgis/arcgis-feature-server-source-loader.js.map +1 -0
  18. package/dist/arcgis/arcgis-image-server-source-loader-types.d.ts +31 -0
  19. package/dist/arcgis/arcgis-image-server-source-loader-types.d.ts.map +1 -0
  20. package/dist/arcgis/arcgis-image-server-source-loader-types.js +21 -0
  21. package/dist/arcgis/arcgis-image-server-source-loader-types.js.map +1 -0
  22. package/dist/arcgis/arcgis-image-server-source-loader.d.ts +60 -0
  23. package/dist/arcgis/arcgis-image-server-source-loader.d.ts.map +1 -0
  24. package/dist/arcgis/arcgis-image-server-source-loader.js +130 -0
  25. package/dist/arcgis/arcgis-image-server-source-loader.js.map +1 -0
  26. package/dist/arcgis/arcgis-image-tile-source-loader-types.d.ts +31 -0
  27. package/dist/arcgis/arcgis-image-tile-source-loader-types.d.ts.map +1 -0
  28. package/dist/arcgis/arcgis-image-tile-source-loader-types.js +21 -0
  29. package/dist/arcgis/arcgis-image-tile-source-loader-types.js.map +1 -0
  30. package/dist/arcgis/arcgis-image-tile-source-loader.d.ts +63 -0
  31. package/dist/arcgis/arcgis-image-tile-source-loader.d.ts.map +1 -0
  32. package/dist/arcgis/arcgis-image-tile-source-loader.js +147 -0
  33. package/dist/arcgis/arcgis-image-tile-source-loader.js.map +1 -0
  34. package/dist/arcgis/arcgis-map-tile-source-loader-types.d.ts +31 -0
  35. package/dist/arcgis/arcgis-map-tile-source-loader-types.d.ts.map +1 -0
  36. package/dist/arcgis/arcgis-map-tile-source-loader-types.js +21 -0
  37. package/dist/arcgis/arcgis-map-tile-source-loader-types.js.map +1 -0
  38. package/dist/arcgis/arcgis-map-tile-source-loader.d.ts +62 -0
  39. package/dist/arcgis/arcgis-map-tile-source-loader.d.ts.map +1 -0
  40. package/dist/arcgis/arcgis-map-tile-source-loader.js +196 -0
  41. package/dist/arcgis/arcgis-map-tile-source-loader.js.map +1 -0
  42. package/dist/arcgis/arcgis-scene-aggregation.d.ts +34 -0
  43. package/dist/arcgis/arcgis-scene-aggregation.d.ts.map +1 -0
  44. package/dist/arcgis/arcgis-scene-aggregation.js +57 -0
  45. package/dist/arcgis/arcgis-scene-aggregation.js.map +1 -0
  46. package/dist/arcgis/arcgis-scene-server-source-loader-types.d.ts +31 -0
  47. package/dist/arcgis/arcgis-scene-server-source-loader-types.d.ts.map +1 -0
  48. package/dist/arcgis/arcgis-scene-server-source-loader-types.js +21 -0
  49. package/dist/arcgis/arcgis-scene-server-source-loader-types.js.map +1 -0
  50. package/dist/arcgis/arcgis-scene-server-source-loader.d.ts +64 -0
  51. package/dist/arcgis/arcgis-scene-server-source-loader.d.ts.map +1 -0
  52. package/dist/arcgis/arcgis-scene-server-source-loader.js +159 -0
  53. package/dist/arcgis/arcgis-scene-server-source-loader.js.map +1 -0
  54. package/dist/arcgis/arcgis-server.d.ts +15 -0
  55. package/dist/arcgis/arcgis-server.d.ts.map +1 -0
  56. package/dist/arcgis/arcgis-server.js +41 -0
  57. package/dist/arcgis/arcgis-server.js.map +1 -0
  58. package/dist/arcgis/arcgis-url-utils.d.ts +3 -0
  59. package/dist/arcgis/arcgis-url-utils.d.ts.map +1 -0
  60. package/dist/arcgis/arcgis-url-utils.js +23 -0
  61. package/dist/arcgis/arcgis-url-utils.js.map +1 -0
  62. package/dist/arcgis/arcgis-vector-tile-server-source-loader-types.d.ts +31 -0
  63. package/dist/arcgis/arcgis-vector-tile-server-source-loader-types.d.ts.map +1 -0
  64. package/dist/arcgis/arcgis-vector-tile-server-source-loader-types.js +21 -0
  65. package/dist/arcgis/arcgis-vector-tile-server-source-loader-types.js.map +1 -0
  66. package/dist/arcgis/arcgis-vector-tile-server-source-loader.d.ts +66 -0
  67. package/dist/arcgis/arcgis-vector-tile-server-source-loader.d.ts.map +1 -0
  68. package/dist/arcgis/arcgis-vector-tile-server-source-loader.js +150 -0
  69. package/dist/arcgis/arcgis-vector-tile-server-source-loader.js.map +1 -0
  70. package/dist/arcgis-feature-query-types.d.ts +77 -0
  71. package/dist/arcgis-feature-query-types.d.ts.map +1 -0
  72. package/dist/arcgis-feature-query-types.js +5 -0
  73. package/dist/arcgis-feature-query-types.js.map +1 -0
  74. package/dist/arcgis-source-types.d.ts +372 -0
  75. package/dist/arcgis-source-types.d.ts.map +1 -0
  76. package/dist/arcgis-source-types.js +108 -0
  77. package/dist/arcgis-source-types.js.map +1 -0
  78. package/dist/authentication.d.ts +19 -0
  79. package/dist/authentication.d.ts.map +1 -0
  80. package/dist/authentication.js +24 -0
  81. package/dist/authentication.js.map +1 -0
  82. package/dist/bundled.d.ts +151 -0
  83. package/dist/bundled.d.ts.map +1 -0
  84. package/dist/bundled.js +33 -0
  85. package/dist/bundled.js.map +1 -0
  86. package/dist/index.cjs +24932 -0
  87. package/dist/index.cjs.map +6 -0
  88. package/dist/index.d.ts +23 -0
  89. package/dist/index.d.ts.map +1 -0
  90. package/dist/index.js +11 -0
  91. package/dist/index.js.map +1 -0
  92. package/dist/service-registry.d.ts +18 -0
  93. package/dist/service-registry.d.ts.map +1 -0
  94. package/dist/service-registry.js +29 -0
  95. package/dist/service-registry.js.map +1 -0
  96. package/package.json +91 -0
  97. package/src/arcgis/arcgis-capability-graph.ts +297 -0
  98. package/src/arcgis/arcgis-feature-query.ts +442 -0
  99. package/src/arcgis/arcgis-feature-server-source-loader-types.ts +34 -0
  100. package/src/arcgis/arcgis-feature-server-source-loader.ts +373 -0
  101. package/src/arcgis/arcgis-image-server-source-loader-types.ts +34 -0
  102. package/src/arcgis/arcgis-image-server-source-loader.ts +186 -0
  103. package/src/arcgis/arcgis-image-tile-source-loader-types.ts +34 -0
  104. package/src/arcgis/arcgis-image-tile-source-loader.ts +192 -0
  105. package/src/arcgis/arcgis-map-tile-source-loader-types.ts +34 -0
  106. package/src/arcgis/arcgis-map-tile-source-loader.ts +248 -0
  107. package/src/arcgis/arcgis-scene-aggregation.ts +90 -0
  108. package/src/arcgis/arcgis-scene-server-source-loader-types.ts +36 -0
  109. package/src/arcgis/arcgis-scene-server-source-loader.ts +216 -0
  110. package/src/arcgis/arcgis-server.ts +61 -0
  111. package/src/arcgis/arcgis-url-utils.ts +26 -0
  112. package/src/arcgis/arcgis-vector-tile-server-source-loader-types.ts +36 -0
  113. package/src/arcgis/arcgis-vector-tile-server-source-loader.ts +196 -0
  114. package/src/arcgis-feature-query-types.ts +89 -0
  115. package/src/arcgis-source-types.ts +372 -0
  116. package/src/authentication.ts +35 -0
  117. package/src/bundled.ts +66 -0
  118. package/src/index.ts +63 -0
  119. package/src/service-registry.ts +43 -0
@@ -0,0 +1,192 @@
1
+ // loaders.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ import {ArcGISAuthentication} from '../authentication';
6
+ import {ARCGIS_IMAGE_TILE_SOURCE_LOADER_METADATA} from '../arcgis-source-types';
7
+ import type {ArcGISImageTileSourceLoaderOptions} from '../arcgis-source-types';
8
+
9
+ import type {ImageType} from '@loaders.gl/images';
10
+ import {ImageLoader} from '@loaders.gl/images';
11
+ import type {
12
+ CoreAPI,
13
+ GetTileDataParameters,
14
+ GetTileParameters,
15
+ SourceLoader,
16
+ TileSource,
17
+ TileSourceMetadata
18
+ } from '@loaders.gl/loader-utils';
19
+ import {DataSource} from '@loaders.gl/loader-utils';
20
+ import type {LERCData} from '@loaders.gl/lerc';
21
+ import {LERCLoader} from '@loaders.gl/lerc';
22
+
23
+ /** A tile source that renders ArcGIS ImageServer exports as deck.gl tiles. */
24
+ export class ArcGISImageTileSource
25
+ extends DataSource<string, ArcGISImageTileSourceLoaderOptions>
26
+ implements TileSource
27
+ {
28
+ /** MIME type represented by the tile response. */
29
+ readonly mimeType: string;
30
+
31
+ /** Cached ImageServer metadata. */
32
+ private _metadata: any | null = null;
33
+ /** In-flight metadata request shared by concurrent callers. */
34
+ private _metadataPromise: Promise<any> | null = null;
35
+ /** Parameters applied to subsequent export requests. */
36
+ private _runtimeParameters: Record<string, string | number | boolean> = {};
37
+
38
+ /** Creates an ArcGIS ImageServer tile source. */
39
+ constructor(url: string, options: ArcGISImageTileSourceLoaderOptions = {}, coreApi?: CoreAPI) {
40
+ super(
41
+ url.replace(/\/$/, ''),
42
+ options,
43
+ ARCGIS_IMAGE_TILE_SOURCE_LOADER_METADATA.defaultOptions,
44
+ coreApi
45
+ );
46
+ this.mimeType = this._getResponseFormat() === 'lerc' ? 'application/octet-stream' : 'image/png';
47
+ this.getTileData = this.getTileData.bind(this);
48
+ }
49
+
50
+ /** Returns normalized ImageServer metadata. */
51
+ async getMetadata(): Promise<TileSourceMetadata> {
52
+ const metadata = await this._getMetadata();
53
+ const extent = metadata.fullExtent || metadata.extent;
54
+ const spatialReference = metadata.spatialReference || extent?.spatialReference;
55
+ return {
56
+ name: metadata.name || metadata.serviceDescription || '',
57
+ title: metadata.name || metadata.serviceDescription || '',
58
+ abstract: metadata.description || metadata.serviceDescription || '',
59
+ attributions: metadata.copyrightText ? [metadata.copyrightText] : undefined,
60
+ boundingBox: extent
61
+ ? [
62
+ [extent.xmin, extent.ymin],
63
+ [extent.xmax, extent.ymax]
64
+ ]
65
+ : undefined,
66
+ layer: {
67
+ name: metadata.name || '',
68
+ srs: spatialReference?.wkid ? [`EPSG:${spatialReference.wkid}`] : [],
69
+ layers: []
70
+ }
71
+ };
72
+ }
73
+
74
+ /** Fetches one ImageServer export tile. */
75
+ async getTile(
76
+ parameters: GetTileParameters,
77
+ signal?: AbortSignal
78
+ ): Promise<ImageType | LERCData | null> {
79
+ const response = await this.fetch(this.getTileURL(parameters), signal ? {signal} : undefined);
80
+ if (!response.ok) {
81
+ throw new Error(
82
+ `ArcGIS ImageServer tile request failed: ${response.status} ${response.statusText}`
83
+ );
84
+ }
85
+ const loader = this._getResponseFormat() === 'lerc' ? LERCLoader : ImageLoader;
86
+ return (await this.coreApi.parse(
87
+ await response.arrayBuffer(),
88
+ loader,
89
+ this.loadOptions
90
+ )) as ImageType;
91
+ }
92
+
93
+ /** Fetches a tile using the deck.gl-compatible request shape. */
94
+ async getTileData(parameters: GetTileDataParameters): Promise<ImageType | LERCData | null> {
95
+ return this.getTile(parameters.index, parameters.signal);
96
+ }
97
+
98
+ /** Updates parameters applied to subsequent ImageServer export requests. */
99
+ updateParameters(parameters: Record<string, string | number | boolean>): void {
100
+ this._runtimeParameters = {...this._runtimeParameters, ...parameters};
101
+ }
102
+
103
+ /** Builds an ImageServer exportImage URL for one web-mercator tile. */
104
+ getTileURL(parameters: GetTileParameters, tileSize?: number): string {
105
+ const options = this.options['arcgis-image-server-tiles'] || {};
106
+ const resolvedTileSize = tileSize || options.tileSize || 256;
107
+ const [west, south, east, north] = getWebMercatorTileBounds(parameters);
108
+ const url = new URL(this.getServiceURL(parameters));
109
+ url.pathname = `${url.pathname.replace(/\/$/, '')}/exportImage`;
110
+ const requestParameters = {
111
+ f: 'image',
112
+ bbox: `${west},${south},${east},${north}`,
113
+ bboxSR: 3857,
114
+ imageSR: 3857,
115
+ size: `${resolvedTileSize},${resolvedTileSize}`,
116
+ format: this._getResponseFormat(),
117
+ transparent: true,
118
+ ...options.parameters,
119
+ ...this._runtimeParameters
120
+ };
121
+ for (const [key, value] of Object.entries(requestParameters)) {
122
+ url.searchParams.set(key, String(value));
123
+ }
124
+ return url.toString();
125
+ }
126
+
127
+ /** Fetches the ImageServer metadata document. */
128
+ private async _loadMetadata(): Promise<any> {
129
+ const url = new URL(this.url);
130
+ url.searchParams.set('f', 'pjson');
131
+ const response = await this.fetch(url.toString());
132
+ if (!response.ok) {
133
+ throw new Error(`ArcGIS ImageServer metadata request failed: ${response.status}`);
134
+ }
135
+ return response.json();
136
+ }
137
+
138
+ /** Returns cached metadata and shares a single request among concurrent callers. */
139
+ private async _getMetadata(): Promise<any> {
140
+ if (this._metadata) return this._metadata;
141
+ if (!this._metadataPromise) {
142
+ this._metadataPromise = this._loadMetadata();
143
+ }
144
+ try {
145
+ const metadata = await this._metadataPromise;
146
+ this._metadata = metadata;
147
+ return metadata;
148
+ } finally {
149
+ this._metadataPromise = null;
150
+ }
151
+ }
152
+
153
+ /** Selects a service endpoint for a tile using a stable URL-pool mapping. */
154
+ private getServiceURL(parameters: GetTileParameters): string {
155
+ const urls = this.options['arcgis-image-server-tiles']?.urls;
156
+ return urls?.length ? urls[(parameters.x + parameters.y) % urls.length] : this.url;
157
+ }
158
+
159
+ /** Returns the response format after applying configured and runtime overrides. */
160
+ private _getResponseFormat(): 'png32' | 'lerc' {
161
+ const options = this.options['arcgis-image-server-tiles'] || {};
162
+ const format = this._runtimeParameters.format || options.parameters?.format || options.format;
163
+ return format === 'lerc' ? 'lerc' : 'png32';
164
+ }
165
+ }
166
+
167
+ /** Runtime service loader for synchronous construction. */
168
+ export const ArcGISImageTileSourceLoaderWithParser = {
169
+ ...ARCGIS_IMAGE_TILE_SOURCE_LOADER_METADATA,
170
+ dataType: null as unknown as ArcGISImageTileSource,
171
+ batchType: null as never,
172
+ preload: undefined,
173
+ /** Supplies ArcGIS credentials after loading the selected implementation. */
174
+ getAuthentications: () => [ArcGISAuthentication],
175
+ createDataSource: (
176
+ url: string,
177
+ options: ArcGISImageTileSourceLoaderOptions = {},
178
+ coreApi?: CoreAPI
179
+ ) => new ArcGISImageTileSource(url, options, coreApi)
180
+ } as const satisfies SourceLoader<ArcGISImageTileSource>;
181
+
182
+ /** Calculates the Web Mercator extent represented by an XYZ tile. */
183
+ function getWebMercatorTileBounds(parameters: GetTileParameters): [number, number, number, number] {
184
+ const worldSize = 20037508.342789244;
185
+ const tileCount = 2 ** parameters.z;
186
+ const tileSize = (worldSize * 2) / tileCount;
187
+ const west = -worldSize + parameters.x * tileSize;
188
+ const east = west + tileSize;
189
+ const north = worldSize - parameters.y * tileSize;
190
+ const south = north - tileSize;
191
+ return [west, south, east, north];
192
+ }
@@ -0,0 +1,34 @@
1
+ // loaders.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ import {ARCGIS_MAP_TILE_SOURCE_LOADER_METADATA} from '../arcgis-source-types';
6
+ import type {CoreAPI, SourceLoader} from '@loaders.gl/loader-utils';
7
+ import type {ArcGISMapTileSource} from './arcgis-map-tile-source-loader';
8
+ import type {ArcGISMapTileSourceLoaderOptions} from '../arcgis-source-types';
9
+
10
+ /** Loads the ArcGISMapTileSourceLoader implementation on first asynchronous use. */
11
+ async function preloadArcGISMapTileSourceLoader(): Promise<SourceLoader<ArcGISMapTileSource>> {
12
+ const {ArcGISMapTileSourceLoaderWithParser} = await import(
13
+ '@loaders.gl/arcgis/arcgis-map-tile-source-loader'
14
+ );
15
+ return ArcGISMapTileSourceLoaderWithParser;
16
+ }
17
+
18
+ /** Lightweight service descriptor; use load() or the synchronous /bundled entrypoint. */
19
+ export const ArcGISMapTileSourceLoader = {
20
+ ...ARCGIS_MAP_TILE_SOURCE_LOADER_METADATA,
21
+ dataType: null as unknown as ArcGISMapTileSource,
22
+ batchType: null as never,
23
+ preload: preloadArcGISMapTileSourceLoader,
24
+ /** Requires the runtime loader when constructing a source synchronously. */
25
+ createDataSource(
26
+ _url: string,
27
+ _options: ArcGISMapTileSourceLoaderOptions,
28
+ _coreApi?: CoreAPI
29
+ ): ArcGISMapTileSource {
30
+ throw new Error(
31
+ 'ArcGISMapTileSourceLoader requires async load() or an @loaders.gl/arcgis/bundled import'
32
+ );
33
+ }
34
+ } as const satisfies SourceLoader<ArcGISMapTileSource>;
@@ -0,0 +1,248 @@
1
+ // loaders.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ import {ArcGISAuthentication} from '../authentication';
6
+ import {ARCGIS_MAP_TILE_SOURCE_LOADER_METADATA} from '../arcgis-source-types';
7
+ import type {
8
+ ArcGISMapTileSourceLoaderOptions,
9
+ ArcGISMapServerMetadata,
10
+ ArcGISMapTileParameters
11
+ } from '../arcgis-source-types';
12
+
13
+ import type {ImageType} from '@loaders.gl/images';
14
+ import {ImageLoader} from '@loaders.gl/images';
15
+ import type {
16
+ CoreAPI,
17
+ GetTileDataParameters,
18
+ GetTileParameters,
19
+ SourceLoader,
20
+ TileSource,
21
+ TileSourceMetadata
22
+ } from '@loaders.gl/loader-utils';
23
+ import {DataSource} from '@loaders.gl/loader-utils';
24
+
25
+ /** ArcGIS MapServer source for cached `/tile/{z}/{y}/{x}` image tiles. */
26
+ export class ArcGISMapTileSource
27
+ extends DataSource<string, ArcGISMapTileSourceLoaderOptions>
28
+ implements TileSource
29
+ {
30
+ /** MIME type rendered by the generic deck.gl tile adapter. */
31
+ readonly mimeType = 'image/png';
32
+
33
+ /** Cached service metadata. */
34
+ private _metadata: ArcGISMapServerMetadata | null = null;
35
+ /** In-flight metadata request shared by concurrent callers. */
36
+ private _metadataPromise: Promise<ArcGISMapServerMetadata> | null = null;
37
+ /** Parameters applied to subsequent dynamic exports. */
38
+ private _runtimeParameters: ArcGISMapTileParameters = {};
39
+
40
+ /** Creates an ArcGIS MapServer tile source. */
41
+ constructor(url: string, options: ArcGISMapTileSourceLoaderOptions = {}, coreApi?: CoreAPI) {
42
+ super(
43
+ url.replace(/\/$/, ''),
44
+ options,
45
+ ARCGIS_MAP_TILE_SOURCE_LOADER_METADATA.defaultOptions,
46
+ coreApi
47
+ );
48
+ this.getTileData = this.getTileData.bind(this);
49
+ }
50
+
51
+ /** Loads and normalizes ArcGIS service metadata. */
52
+ async getMetadata(): Promise<TileSourceMetadata> {
53
+ const metadata = await this._getMetadata();
54
+ const extent = metadata.fullExtent;
55
+ const spatialReference = metadata.spatialReference || extent?.spatialReference;
56
+ const spatialReferenceWkid = (spatialReference as {wkid?: number} | undefined)?.wkid;
57
+ return {
58
+ name: metadata.name || '',
59
+ title: metadata.name || '',
60
+ abstract: metadata.description || metadata.serviceDescription || '',
61
+ attributions: metadata.copyrightText ? [metadata.copyrightText] : undefined,
62
+ minZoom: 0,
63
+ maxZoom: metadata.tileInfo?.lods?.length
64
+ ? Math.max(...metadata.tileInfo.lods.map(lod => lod.level))
65
+ : undefined,
66
+ boundingBox: extent
67
+ ? [
68
+ [extent.xmin, extent.ymin],
69
+ [extent.xmax, extent.ymax]
70
+ ]
71
+ : undefined,
72
+ layer: {
73
+ name: metadata.name || '',
74
+ srs: spatialReferenceWkid ? [`EPSG:${spatialReferenceWkid}`] : [],
75
+ layers: []
76
+ },
77
+ tileGrid: metadata.tileInfo
78
+ ? {
79
+ crs: (metadata.tileInfo.spatialReference as {wkid?: number} | undefined)?.wkid
80
+ ? `EPSG:${(metadata.tileInfo.spatialReference as {wkid: number}).wkid}`
81
+ : undefined,
82
+ tileSize:
83
+ metadata.tileInfo.rows && metadata.tileInfo.cols
84
+ ? [metadata.tileInfo.cols, metadata.tileInfo.rows]
85
+ : undefined,
86
+ origin: metadata.tileInfo.origin
87
+ ? [metadata.tileInfo.origin.x, metadata.tileInfo.origin.y]
88
+ : undefined,
89
+ matrixIds: metadata.tileInfo.lods?.map(lod => String(lod.level))
90
+ }
91
+ : undefined
92
+ };
93
+ }
94
+
95
+ /** Fetches and decodes one cached ArcGIS tile. */
96
+ async getTile(parameters: GetTileParameters, signal?: AbortSignal): Promise<ImageType | null> {
97
+ const options = this.options['arcgis-map-server'] || {};
98
+ const mode = options.mode || 'auto';
99
+ const metadata = mode === 'auto' ? await this._getMetadata() : null;
100
+ const useCachedTiles = mode === 'cached' || (mode === 'auto' && isCompatibleCache(metadata));
101
+ const tileURL = useCachedTiles
102
+ ? this.getTileURL(parameters)
103
+ : this.getExportTileURL(parameters, options.tileSize || 256);
104
+ const response = await this.fetch(tileURL, signal ? {signal} : undefined);
105
+ if (!response.ok) {
106
+ throw new Error(
107
+ `ArcGIS MapServer tile request failed: ${response.status} ${response.statusText}`
108
+ );
109
+ }
110
+ return (await this.coreApi.parse(
111
+ await response.arrayBuffer(),
112
+ ImageLoader,
113
+ this.loadOptions
114
+ )) as ImageType;
115
+ }
116
+
117
+ /** Fetches a tile using the deck.gl-compatible request shape. */
118
+ async getTileData(parameters: GetTileDataParameters): Promise<ImageType | null> {
119
+ return this.getTile(parameters.index, parameters.signal);
120
+ }
121
+
122
+ /** Builds the standard ArcGIS cached tile URL. */
123
+ getTileURL(parameters: GetTileParameters): string {
124
+ const template = this.options['arcgis-map-server']?.urlTemplate;
125
+ if (template) {
126
+ const templateURL = new URL(
127
+ template
128
+ .replaceAll('{z}', String(parameters.z))
129
+ .replaceAll('{y}', String(parameters.y))
130
+ .replaceAll('{x}', String(parameters.x))
131
+ );
132
+ return templateURL.toString();
133
+ }
134
+ const url = new URL(this.getServiceURL(parameters));
135
+ url.pathname = `${url.pathname.replace(/\/$/, '')}/tile/${parameters.z}/${parameters.y}/${parameters.x}`;
136
+ return url.toString();
137
+ }
138
+
139
+ /** Updates parameters applied to subsequent dynamic MapServer export requests. */
140
+ updateParameters(parameters: ArcGISMapTileParameters): void {
141
+ this._runtimeParameters = {...this._runtimeParameters, ...parameters};
142
+ }
143
+
144
+ /** Builds a dynamic MapServer `export` request for one web-mercator tile. */
145
+ getExportTileURL(parameters: GetTileParameters, tileSize?: number): string {
146
+ const options = this.options['arcgis-map-server'] || {};
147
+ const resolvedTileSize = tileSize || options.tileSize || 256;
148
+ const [west, south, east, north] = getWebMercatorTileBounds(parameters);
149
+ const url = new URL(this.getServiceURL(parameters));
150
+ url.pathname = `${url.pathname.replace(/\/$/, '')}/export`;
151
+ const searchParameters: ArcGISMapTileParameters = {
152
+ f: 'image',
153
+ bbox: `${west},${south},${east},${north}`,
154
+ bboxSR: 3857,
155
+ imageSR: 3857,
156
+ size: `${resolvedTileSize},${resolvedTileSize}`,
157
+ format: 'png32',
158
+ transparent: true,
159
+ ...options.exportParameters,
160
+ ...this._runtimeParameters
161
+ };
162
+ for (const [key, value] of Object.entries(searchParameters)) {
163
+ url.searchParams.set(key, String(value));
164
+ }
165
+ return url.toString();
166
+ }
167
+
168
+ /** Fetches the MapServer metadata document and applies configured parameters. */
169
+ private async _loadMetadata(): Promise<ArcGISMapServerMetadata> {
170
+ const configuredMetadata = this.options['arcgis-map-server']?.metadata;
171
+ if (configuredMetadata) return configuredMetadata;
172
+ const url = new URL(this.url);
173
+ url.searchParams.set('f', 'pjson');
174
+ for (const [key, value] of Object.entries(
175
+ this.options['arcgis-map-server']?.parameters || {}
176
+ )) {
177
+ url.searchParams.set(key, value);
178
+ }
179
+ const response = await this.fetch(url.toString());
180
+ if (!response.ok) {
181
+ throw new Error(
182
+ `ArcGIS MapServer metadata request failed: ${response.status} ${response.statusText}`
183
+ );
184
+ }
185
+ return response.json();
186
+ }
187
+
188
+ /** Returns cached metadata and shares a single request among concurrent callers. */
189
+ private async _getMetadata(): Promise<ArcGISMapServerMetadata> {
190
+ if (this._metadata) return this._metadata;
191
+ if (!this._metadataPromise) {
192
+ this._metadataPromise = this._loadMetadata();
193
+ }
194
+ try {
195
+ const metadata = await this._metadataPromise;
196
+ this._metadata = metadata;
197
+ return metadata;
198
+ } finally {
199
+ this._metadataPromise = null;
200
+ }
201
+ }
202
+
203
+ /** Selects a service endpoint for a tile using a stable URL-pool mapping. */
204
+ private getServiceURL(parameters: GetTileParameters): string {
205
+ const urls = this.options['arcgis-map-server']?.urls;
206
+ return urls?.length ? urls[(parameters.x + parameters.y) % urls.length] : this.url;
207
+ }
208
+ }
209
+
210
+ function isCompatibleCache(metadata: ArcGISMapServerMetadata | null): boolean {
211
+ const tileInfo = metadata?.tileInfo;
212
+ if (!tileInfo || tileInfo.rows !== 256 || tileInfo.cols !== 256) return false;
213
+ const spatialReference = tileInfo.spatialReference || metadata?.spatialReference;
214
+ const wkid = (spatialReference as {wkid?: number; latestWkid?: number} | undefined)?.wkid;
215
+ if (wkid !== 3857 && wkid !== 102100 && wkid !== 102113) return false;
216
+ const origin = tileInfo.origin;
217
+ return (
218
+ !origin ||
219
+ (Math.abs(origin.x + 20037508.342789244) < 1 && Math.abs(origin.y - 20037508.342789244) < 1)
220
+ );
221
+ }
222
+
223
+ /** Calculates the Web Mercator extent represented by an XYZ tile. */
224
+ function getWebMercatorTileBounds(parameters: GetTileParameters): [number, number, number, number] {
225
+ const worldSize = 20037508.342789244;
226
+ const tileCount = 2 ** parameters.z;
227
+ const tileSize = (worldSize * 2) / tileCount;
228
+ const west = -worldSize + parameters.x * tileSize;
229
+ const east = west + tileSize;
230
+ const north = worldSize - parameters.y * tileSize;
231
+ const south = north - tileSize;
232
+ return [west, south, east, north];
233
+ }
234
+
235
+ /** Runtime service loader for synchronous construction. */
236
+ export const ArcGISMapTileSourceLoaderWithParser = {
237
+ ...ARCGIS_MAP_TILE_SOURCE_LOADER_METADATA,
238
+ dataType: null as unknown as ArcGISMapTileSource,
239
+ batchType: null as never,
240
+ preload: undefined,
241
+ /** Supplies ArcGIS credentials after loading the selected implementation. */
242
+ getAuthentications: () => [ArcGISAuthentication],
243
+ createDataSource: (
244
+ url: string,
245
+ options: ArcGISMapTileSourceLoaderOptions = {},
246
+ coreApi?: CoreAPI
247
+ ) => new ArcGISMapTileSource(url, options, coreApi)
248
+ } as const satisfies SourceLoader<ArcGISMapTileSource>;
@@ -0,0 +1,90 @@
1
+ // loaders.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ /** Aggregation operation supported by the SceneServer client helper. */
6
+ export type ArcGISSceneAggregationOperation = 'count' | 'sum' | 'min' | 'max' | 'average';
7
+
8
+ /** A single aggregation specification. */
9
+ export type ArcGISSceneAggregationSpec = {
10
+ /** Output field name. */
11
+ name: string;
12
+ /** Input property used by numeric operations. */
13
+ field?: string;
14
+ /** Operation to apply. */
15
+ operation: ArcGISSceneAggregationOperation;
16
+ };
17
+
18
+ /** Options for deterministic client-side feature aggregation. */
19
+ export type ArcGISSceneAggregationOptions = {
20
+ /** Features represented as ArcGIS feature objects or plain attribute records. */
21
+ features: unknown[];
22
+ /** Optional property used to create groups. */
23
+ groupBy?: string;
24
+ /** Aggregations to compute for each group. */
25
+ aggregations: ArcGISSceneAggregationSpec[];
26
+ };
27
+
28
+ /** Result of one group in a client-side aggregation. */
29
+ export type ArcGISSceneAggregationGroup = {
30
+ /** Group key, or `undefined` when no grouping was requested. */
31
+ group: unknown;
32
+ /** Aggregated values keyed by specification name. */
33
+ values: Record<string, number>;
34
+ };
35
+
36
+ /**
37
+ * Aggregates SceneServer features without evaluating renderer or Arcade expressions.
38
+ * Missing and non-numeric values are ignored by numeric operations; `count` counts
39
+ * records regardless of field presence.
40
+ */
41
+ export function aggregateArcGISSceneFeatures(
42
+ options: ArcGISSceneAggregationOptions
43
+ ): ArcGISSceneAggregationGroup[] {
44
+ const groups = new Map<unknown, unknown[]>();
45
+ for (const feature of options.features) {
46
+ const attributes = getFeatureAttributes(feature);
47
+ const key = options.groupBy ? attributes[options.groupBy] : undefined;
48
+ const group = groups.get(key);
49
+ if (group) group.push(feature);
50
+ else groups.set(key, [feature]);
51
+ }
52
+
53
+ return [...groups.entries()].map(([group, features]) => ({
54
+ group,
55
+ values: Object.fromEntries(
56
+ options.aggregations.map(spec => [spec.name, aggregateValues(features, spec)])
57
+ )
58
+ }));
59
+ }
60
+
61
+ function aggregateValues(features: unknown[], specification: ArcGISSceneAggregationSpec): number {
62
+ if (specification.operation === 'count') return features.length;
63
+ const values = features
64
+ .map(feature => getFeatureAttributes(feature)[specification.field || ''])
65
+ .filter(value => value !== null && value !== undefined && value !== '')
66
+ .map(value => (typeof value === 'number' ? value : Number(value)))
67
+ .filter(Number.isFinite);
68
+ if (!values.length) return 0;
69
+ switch (specification.operation) {
70
+ case 'sum':
71
+ return values.reduce((sum, value) => sum + value, 0);
72
+ case 'min':
73
+ return Math.min(...values);
74
+ case 'max':
75
+ return Math.max(...values);
76
+ case 'average':
77
+ return values.reduce((sum, value) => sum + value, 0) / values.length;
78
+ default:
79
+ return 0;
80
+ }
81
+ }
82
+
83
+ function getFeatureAttributes(feature: unknown): Record<string, unknown> {
84
+ if (!feature || typeof feature !== 'object') return {};
85
+ const candidate = feature as {attributes?: unknown};
86
+ if (candidate.attributes && typeof candidate.attributes === 'object') {
87
+ return candidate.attributes as Record<string, unknown>;
88
+ }
89
+ return feature as Record<string, unknown>;
90
+ }
@@ -0,0 +1,36 @@
1
+ // loaders.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ import {ARCGIS_SCENE_SERVER_SOURCE_LOADER_METADATA} from '../arcgis-source-types';
6
+ import type {CoreAPI, SourceLoader} from '@loaders.gl/loader-utils';
7
+ import type {ArcGISSceneServerSource} from './arcgis-scene-server-source-loader';
8
+ import type {ArcGISSceneServerSourceOptions} from '../arcgis-source-types';
9
+
10
+ /** Loads the ArcGISSceneServerSourceLoader implementation on first asynchronous use. */
11
+ async function preloadArcGISSceneServerSourceLoader(): Promise<
12
+ SourceLoader<ArcGISSceneServerSource>
13
+ > {
14
+ const {ArcGISSceneServerSourceLoaderWithParser} = await import(
15
+ '@loaders.gl/arcgis/arcgis-scene-server-source-loader'
16
+ );
17
+ return ArcGISSceneServerSourceLoaderWithParser;
18
+ }
19
+
20
+ /** Lightweight service descriptor; use load() or the synchronous /bundled entrypoint. */
21
+ export const ArcGISSceneServerSourceLoader = {
22
+ ...ARCGIS_SCENE_SERVER_SOURCE_LOADER_METADATA,
23
+ dataType: null as unknown as ArcGISSceneServerSource,
24
+ batchType: null as never,
25
+ preload: preloadArcGISSceneServerSourceLoader,
26
+ /** Requires the runtime loader when constructing a source synchronously. */
27
+ createDataSource(
28
+ _url: string,
29
+ _options: ArcGISSceneServerSourceOptions,
30
+ _coreApi?: CoreAPI
31
+ ): ArcGISSceneServerSource {
32
+ throw new Error(
33
+ 'ArcGISSceneServerSourceLoader requires async load() or an @loaders.gl/arcgis/bundled import'
34
+ );
35
+ }
36
+ } as const satisfies SourceLoader<ArcGISSceneServerSource>;