@loaders.gl/arcgis 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.
- package/LICENSE +52 -0
- package/README.md +28 -0
- package/dist/arcgis/arcgis-capability-graph.d.ts +50 -0
- package/dist/arcgis/arcgis-capability-graph.d.ts.map +1 -0
- package/dist/arcgis/arcgis-capability-graph.js +221 -0
- package/dist/arcgis/arcgis-capability-graph.js.map +1 -0
- package/dist/arcgis/arcgis-feature-query.d.ts +53 -0
- package/dist/arcgis/arcgis-feature-query.d.ts.map +1 -0
- package/dist/arcgis/arcgis-feature-query.js +381 -0
- package/dist/arcgis/arcgis-feature-query.js.map +1 -0
- package/dist/arcgis/arcgis-feature-server-source-loader-types.d.ts +33 -0
- package/dist/arcgis/arcgis-feature-server-source-loader-types.d.ts.map +1 -0
- package/dist/arcgis/arcgis-feature-server-source-loader-types.js +21 -0
- package/dist/arcgis/arcgis-feature-server-source-loader-types.js.map +1 -0
- package/dist/arcgis/arcgis-feature-server-source-loader.d.ts +84 -0
- package/dist/arcgis/arcgis-feature-server-source-loader.d.ts.map +1 -0
- package/dist/arcgis/arcgis-feature-server-source-loader.js +280 -0
- package/dist/arcgis/arcgis-feature-server-source-loader.js.map +1 -0
- package/dist/arcgis/arcgis-image-server-source-loader-types.d.ts +31 -0
- package/dist/arcgis/arcgis-image-server-source-loader-types.d.ts.map +1 -0
- package/dist/arcgis/arcgis-image-server-source-loader-types.js +21 -0
- package/dist/arcgis/arcgis-image-server-source-loader-types.js.map +1 -0
- package/dist/arcgis/arcgis-image-server-source-loader.d.ts +60 -0
- package/dist/arcgis/arcgis-image-server-source-loader.d.ts.map +1 -0
- package/dist/arcgis/arcgis-image-server-source-loader.js +130 -0
- package/dist/arcgis/arcgis-image-server-source-loader.js.map +1 -0
- package/dist/arcgis/arcgis-image-tile-source-loader-types.d.ts +31 -0
- package/dist/arcgis/arcgis-image-tile-source-loader-types.d.ts.map +1 -0
- package/dist/arcgis/arcgis-image-tile-source-loader-types.js +21 -0
- package/dist/arcgis/arcgis-image-tile-source-loader-types.js.map +1 -0
- package/dist/arcgis/arcgis-image-tile-source-loader.d.ts +63 -0
- package/dist/arcgis/arcgis-image-tile-source-loader.d.ts.map +1 -0
- package/dist/arcgis/arcgis-image-tile-source-loader.js +147 -0
- package/dist/arcgis/arcgis-image-tile-source-loader.js.map +1 -0
- package/dist/arcgis/arcgis-map-tile-source-loader-types.d.ts +31 -0
- package/dist/arcgis/arcgis-map-tile-source-loader-types.d.ts.map +1 -0
- package/dist/arcgis/arcgis-map-tile-source-loader-types.js +21 -0
- package/dist/arcgis/arcgis-map-tile-source-loader-types.js.map +1 -0
- package/dist/arcgis/arcgis-map-tile-source-loader.d.ts +62 -0
- package/dist/arcgis/arcgis-map-tile-source-loader.d.ts.map +1 -0
- package/dist/arcgis/arcgis-map-tile-source-loader.js +196 -0
- package/dist/arcgis/arcgis-map-tile-source-loader.js.map +1 -0
- package/dist/arcgis/arcgis-scene-aggregation.d.ts +34 -0
- package/dist/arcgis/arcgis-scene-aggregation.d.ts.map +1 -0
- package/dist/arcgis/arcgis-scene-aggregation.js +57 -0
- package/dist/arcgis/arcgis-scene-aggregation.js.map +1 -0
- package/dist/arcgis/arcgis-scene-server-source-loader-types.d.ts +31 -0
- package/dist/arcgis/arcgis-scene-server-source-loader-types.d.ts.map +1 -0
- package/dist/arcgis/arcgis-scene-server-source-loader-types.js +21 -0
- package/dist/arcgis/arcgis-scene-server-source-loader-types.js.map +1 -0
- package/dist/arcgis/arcgis-scene-server-source-loader.d.ts +64 -0
- package/dist/arcgis/arcgis-scene-server-source-loader.d.ts.map +1 -0
- package/dist/arcgis/arcgis-scene-server-source-loader.js +159 -0
- package/dist/arcgis/arcgis-scene-server-source-loader.js.map +1 -0
- package/dist/arcgis/arcgis-server.d.ts +15 -0
- package/dist/arcgis/arcgis-server.d.ts.map +1 -0
- package/dist/arcgis/arcgis-server.js +41 -0
- package/dist/arcgis/arcgis-server.js.map +1 -0
- package/dist/arcgis/arcgis-url-utils.d.ts +3 -0
- package/dist/arcgis/arcgis-url-utils.d.ts.map +1 -0
- package/dist/arcgis/arcgis-url-utils.js +23 -0
- package/dist/arcgis/arcgis-url-utils.js.map +1 -0
- package/dist/arcgis/arcgis-vector-tile-server-source-loader-types.d.ts +31 -0
- package/dist/arcgis/arcgis-vector-tile-server-source-loader-types.d.ts.map +1 -0
- package/dist/arcgis/arcgis-vector-tile-server-source-loader-types.js +21 -0
- package/dist/arcgis/arcgis-vector-tile-server-source-loader-types.js.map +1 -0
- package/dist/arcgis/arcgis-vector-tile-server-source-loader.d.ts +66 -0
- package/dist/arcgis/arcgis-vector-tile-server-source-loader.d.ts.map +1 -0
- package/dist/arcgis/arcgis-vector-tile-server-source-loader.js +150 -0
- package/dist/arcgis/arcgis-vector-tile-server-source-loader.js.map +1 -0
- package/dist/arcgis-feature-query-types.d.ts +77 -0
- package/dist/arcgis-feature-query-types.d.ts.map +1 -0
- package/dist/arcgis-feature-query-types.js +5 -0
- package/dist/arcgis-feature-query-types.js.map +1 -0
- package/dist/arcgis-items.d.ts +64 -0
- package/dist/arcgis-items.d.ts.map +1 -0
- package/dist/arcgis-items.js +137 -0
- package/dist/arcgis-items.js.map +1 -0
- package/dist/arcgis-source-types.d.ts +372 -0
- package/dist/arcgis-source-types.d.ts.map +1 -0
- package/dist/arcgis-source-types.js +108 -0
- package/dist/arcgis-source-types.js.map +1 -0
- package/dist/authentication.d.ts +25 -0
- package/dist/authentication.d.ts.map +1 -0
- package/dist/authentication.js +94 -0
- package/dist/authentication.js.map +1 -0
- package/dist/bundled.d.ts +152 -0
- package/dist/bundled.d.ts.map +1 -0
- package/dist/bundled.js +34 -0
- package/dist/bundled.js.map +1 -0
- package/dist/index.cjs +25620 -0
- package/dist/index.cjs.map +6 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/service-registry.d.ts +18 -0
- package/dist/service-registry.d.ts.map +1 -0
- package/dist/service-registry.js +29 -0
- package/dist/service-registry.js.map +1 -0
- package/package.json +96 -0
- package/src/arcgis/arcgis-capability-graph.ts +297 -0
- package/src/arcgis/arcgis-feature-query.ts +442 -0
- package/src/arcgis/arcgis-feature-server-source-loader-types.ts +34 -0
- package/src/arcgis/arcgis-feature-server-source-loader.ts +373 -0
- package/src/arcgis/arcgis-image-server-source-loader-types.ts +34 -0
- package/src/arcgis/arcgis-image-server-source-loader.ts +186 -0
- package/src/arcgis/arcgis-image-tile-source-loader-types.ts +34 -0
- package/src/arcgis/arcgis-image-tile-source-loader.ts +192 -0
- package/src/arcgis/arcgis-map-tile-source-loader-types.ts +34 -0
- package/src/arcgis/arcgis-map-tile-source-loader.ts +248 -0
- package/src/arcgis/arcgis-scene-aggregation.ts +90 -0
- package/src/arcgis/arcgis-scene-server-source-loader-types.ts +36 -0
- package/src/arcgis/arcgis-scene-server-source-loader.ts +216 -0
- package/src/arcgis/arcgis-server.ts +61 -0
- package/src/arcgis/arcgis-url-utils.ts +26 -0
- package/src/arcgis/arcgis-vector-tile-server-source-loader-types.ts +36 -0
- package/src/arcgis/arcgis-vector-tile-server-source-loader.ts +196 -0
- package/src/arcgis-feature-query-types.ts +89 -0
- package/src/arcgis-items.ts +233 -0
- package/src/arcgis-source-types.ts +372 -0
- package/src/authentication.ts +118 -0
- package/src/bundled.ts +68 -0
- package/src/index.ts +70 -0
- package/src/service-registry.ts +43 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
// loaders.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {ArcGISAuthentication} from '../authentication';
|
|
6
|
+
import {ARCGIS_SCENE_SERVER_SOURCE_LOADER_METADATA} from '../arcgis-source-types';
|
|
7
|
+
import type {
|
|
8
|
+
ArcGISSceneQueryOptions,
|
|
9
|
+
ArcGISSceneQueryResult,
|
|
10
|
+
ArcGISSceneServerSourceOptions
|
|
11
|
+
} from '../arcgis-source-types';
|
|
12
|
+
|
|
13
|
+
import type {CoreAPI, DataSourceOptions, SourceLoader} from '@loaders.gl/loader-utils';
|
|
14
|
+
import {DataSource} from '@loaders.gl/loader-utils';
|
|
15
|
+
import {
|
|
16
|
+
createI3SLayerSource,
|
|
17
|
+
normalizeI3SServiceMetadata,
|
|
18
|
+
parseI3SSceneLayerMetadata
|
|
19
|
+
} from '@loaders.gl/i3s';
|
|
20
|
+
import type {I3SLayerSource, I3SServiceMetadata, SceneLayer3D} from '@loaders.gl/i3s';
|
|
21
|
+
import {buildArcGISResourceURL} from './arcgis-url-utils';
|
|
22
|
+
|
|
23
|
+
/** Error raised when a SceneServer query cannot be completed or decoded. */
|
|
24
|
+
export class ArcGISSceneServerQueryError extends Error {
|
|
25
|
+
/** HTTP status code, when the failure came from a response. */
|
|
26
|
+
readonly status?: number;
|
|
27
|
+
|
|
28
|
+
/** Creates a typed SceneServer query error. */
|
|
29
|
+
constructor(message: string, status?: number) {
|
|
30
|
+
super(message);
|
|
31
|
+
this.name = 'ArcGISSceneServerQueryError';
|
|
32
|
+
this.status = status;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Source facade for an ArcGIS SceneServer I3S layer. */
|
|
37
|
+
export class ArcGISSceneServerSource extends DataSource<string, ArcGISSceneServerSourceOptions> {
|
|
38
|
+
/** Cached normalized layer metadata. */
|
|
39
|
+
private metadataPromise: Promise<I3SServiceMetadata> | null = null;
|
|
40
|
+
/** Cached profile-specific tileset source. */
|
|
41
|
+
private sourcePromise: Promise<I3SLayerSource> | null = null;
|
|
42
|
+
|
|
43
|
+
/** Creates a SceneServer source. */
|
|
44
|
+
constructor(url: string, options: ArcGISSceneServerSourceOptions = {}, coreApi?: CoreAPI) {
|
|
45
|
+
super(
|
|
46
|
+
trimTrailingSlashes(url),
|
|
47
|
+
options,
|
|
48
|
+
ARCGIS_SCENE_SERVER_SOURCE_LOADER_METADATA.defaultOptions,
|
|
49
|
+
coreApi
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Returns normalized SceneServer layer metadata. */
|
|
54
|
+
async getMetadata(): Promise<I3SServiceMetadata> {
|
|
55
|
+
this.metadataPromise ||= this.loadMetadata();
|
|
56
|
+
return await this.metadataPromise;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Creates the existing mesh or Point Cloud source for this layer. */
|
|
60
|
+
async getTilesetSource(): Promise<I3SLayerSource> {
|
|
61
|
+
this.sourcePromise ||= this.createTilesetSource();
|
|
62
|
+
return await this.sourcePromise;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Queries feature attributes and geometry from a SceneServer layer. */
|
|
66
|
+
async query(options: ArcGISSceneQueryOptions = {}): Promise<ArcGISSceneQueryResult> {
|
|
67
|
+
const {signal, f = 'json', ...queryParameters} = options;
|
|
68
|
+
const queryURL = buildArcGISResourceURL(this.getLayerURL(), 'query', {
|
|
69
|
+
where: '1=1',
|
|
70
|
+
outFields: '*',
|
|
71
|
+
returnGeometry: true,
|
|
72
|
+
f,
|
|
73
|
+
...queryParameters,
|
|
74
|
+
token: this.getToken()
|
|
75
|
+
});
|
|
76
|
+
const response = await this.fetch(queryURL, signal ? {signal} : undefined);
|
|
77
|
+
if (!response.ok) {
|
|
78
|
+
throw new ArcGISSceneServerQueryError(
|
|
79
|
+
`ArcGIS SceneServer query failed: ${response.status} ${response.statusText}`,
|
|
80
|
+
response.status
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
const document = (await response.json()) as Record<string, unknown>;
|
|
84
|
+
if (document.error) {
|
|
85
|
+
throw new ArcGISSceneServerQueryError(
|
|
86
|
+
`ArcGIS SceneServer query returned an error: ${JSON.stringify(document.error)}`
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
features: Array.isArray(document.features) ? document.features : [],
|
|
91
|
+
fields: Array.isArray(document.fields) ? document.fields : undefined,
|
|
92
|
+
exceededTransferLimit:
|
|
93
|
+
typeof document.exceededTransferLimit === 'boolean'
|
|
94
|
+
? document.exceededTransferLimit
|
|
95
|
+
: undefined,
|
|
96
|
+
rawMetadata: document
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** Alias for query used by applications that consume generic feature sources. */
|
|
101
|
+
async getFeatures(options: ArcGISSceneQueryOptions = {}): Promise<ArcGISSceneQueryResult> {
|
|
102
|
+
return await this.query(options);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Returns the layer URL, resolving an explicit layer ID when needed. */
|
|
106
|
+
getLayerURL(): string {
|
|
107
|
+
const url = new URL(this.url);
|
|
108
|
+
url.pathname = trimTrailingSlashes(url.pathname);
|
|
109
|
+
if (/\/SceneServer\/layers\/[^/]+$/i.test(url.pathname)) {
|
|
110
|
+
url.search = '';
|
|
111
|
+
url.hash = '';
|
|
112
|
+
return url.toString();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (!/\/SceneServer$/i.test(url.pathname)) {
|
|
116
|
+
throw new Error(
|
|
117
|
+
'ArcGISSceneServerSource requires a /SceneServer/layers/{id} URL or a /SceneServer URL with arcgis-scene-server.layerId'
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const layerId = this.options['arcgis-scene-server']?.layerId;
|
|
122
|
+
if (layerId === undefined) {
|
|
123
|
+
throw new Error(
|
|
124
|
+
'ArcGISSceneServerSource requires a /SceneServer/layers/{id} URL or a /SceneServer URL with arcgis-scene-server.layerId'
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
url.pathname = `${url.pathname.replace(/\/$/, '')}/layers/${encodeURIComponent(String(layerId))}`;
|
|
128
|
+
url.search = '';
|
|
129
|
+
url.hash = '';
|
|
130
|
+
return url.toString();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Returns the metadata request URL with ArcGIS JSON and authentication parameters. */
|
|
134
|
+
metadataURL(): string {
|
|
135
|
+
const url = new URL(this.getLayerURL());
|
|
136
|
+
url.searchParams.set('f', 'pjson');
|
|
137
|
+
const token = this.getToken();
|
|
138
|
+
if (token && !url.searchParams.has('token')) {
|
|
139
|
+
url.searchParams.set('token', token);
|
|
140
|
+
}
|
|
141
|
+
return url.toString();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
private async loadMetadata(): Promise<I3SServiceMetadata> {
|
|
145
|
+
const configuredMetadata = this.options['arcgis-scene-server']?.metadata;
|
|
146
|
+
const layer = configuredMetadata
|
|
147
|
+
? parseI3SSceneLayerMetadata(configuredMetadata)
|
|
148
|
+
: await this.fetchLayerMetadata();
|
|
149
|
+
return normalizeI3SServiceMetadata(this.getLayerURL(), layer);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
private async fetchLayerMetadata(): Promise<SceneLayer3D> {
|
|
153
|
+
const response = await this.fetch(this.metadataURL());
|
|
154
|
+
if (!response.ok) {
|
|
155
|
+
throw new Error(
|
|
156
|
+
`ArcGIS SceneServer metadata request failed: ${response.status} ${response.statusText}`
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
return parseI3SSceneLayerMetadata(await response.json());
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
private async createTilesetSource(): Promise<I3SLayerSource> {
|
|
163
|
+
const metadata = await this.getMetadata();
|
|
164
|
+
return createI3SLayerSource(
|
|
165
|
+
metadata.url,
|
|
166
|
+
metadata.layer,
|
|
167
|
+
this.getSourceOptions(),
|
|
168
|
+
this.coreApi
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
private getSourceOptions(): DataSourceOptions {
|
|
173
|
+
const i3sOptions = (this.options['i3s'] as Record<string, unknown> | undefined) || {};
|
|
174
|
+
const token = this.getToken();
|
|
175
|
+
return {
|
|
176
|
+
...this.options,
|
|
177
|
+
i3s: token ? {...i3sOptions, token} : i3sOptions
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
private getToken(): string | undefined {
|
|
182
|
+
const serviceToken = this.options['arcgis-scene-server']?.token;
|
|
183
|
+
const i3sToken = (this.options['i3s'] as Record<string, unknown> | undefined)?.token;
|
|
184
|
+
if (serviceToken) {
|
|
185
|
+
return serviceToken;
|
|
186
|
+
}
|
|
187
|
+
if (typeof i3sToken === 'string' && i3sToken) {
|
|
188
|
+
return i3sToken;
|
|
189
|
+
}
|
|
190
|
+
return new URL(this.url).searchParams.get('token') || undefined;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** Runtime service loader for synchronous construction. */
|
|
195
|
+
export const ArcGISSceneServerSourceLoaderWithParser = {
|
|
196
|
+
...ARCGIS_SCENE_SERVER_SOURCE_LOADER_METADATA,
|
|
197
|
+
dataType: null as unknown as ArcGISSceneServerSource,
|
|
198
|
+
batchType: null as never,
|
|
199
|
+
preload: undefined,
|
|
200
|
+
/** Supplies ArcGIS credentials after loading the selected implementation. */
|
|
201
|
+
getAuthentications: () => [ArcGISAuthentication],
|
|
202
|
+
createDataSource: (
|
|
203
|
+
url: string,
|
|
204
|
+
options: ArcGISSceneServerSourceOptions = {},
|
|
205
|
+
coreApi?: CoreAPI
|
|
206
|
+
) => new ArcGISSceneServerSource(url, options, coreApi)
|
|
207
|
+
} as const satisfies SourceLoader<ArcGISSceneServerSource>;
|
|
208
|
+
|
|
209
|
+
/** Removes trailing URL separators in linear time, including for untrusted service URLs. */
|
|
210
|
+
function trimTrailingSlashes(value: string): string {
|
|
211
|
+
let endIndex = value.length;
|
|
212
|
+
while (endIndex > 0 && value[endIndex - 1] === '/') {
|
|
213
|
+
endIndex--;
|
|
214
|
+
}
|
|
215
|
+
return value.slice(0, endIndex);
|
|
216
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// loaders.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
export type Service = {name: string; type: string; url: string};
|
|
6
|
+
|
|
7
|
+
type FetchLike = typeof fetch;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* (Recursively) load the service directory from an ArcGIS Server URL
|
|
11
|
+
* @param url
|
|
12
|
+
* @param fetchFile= Optional fetch function override
|
|
13
|
+
* @returns
|
|
14
|
+
*/
|
|
15
|
+
export async function getArcGISServices(
|
|
16
|
+
url: string,
|
|
17
|
+
fetchFile: FetchLike = fetch
|
|
18
|
+
): Promise<Service[] | null> {
|
|
19
|
+
if (url.includes('rest/services')) {
|
|
20
|
+
const serverUrl = url.replace(/rest\/services.*$/i, 'rest/services');
|
|
21
|
+
return loadServiceDirectory(serverUrl, fetchFile, []);
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async function loadServiceDirectory(
|
|
27
|
+
serverUrl: string,
|
|
28
|
+
fetch: FetchLike,
|
|
29
|
+
path: string[]
|
|
30
|
+
): Promise<Service[]> {
|
|
31
|
+
const serviceUrl = path.length ? `${serverUrl}/${path.join('/')}` : serverUrl;
|
|
32
|
+
|
|
33
|
+
const response = await fetch(`${serviceUrl}?f=pjson`);
|
|
34
|
+
const directory = await response.json();
|
|
35
|
+
|
|
36
|
+
const services = extractServices(directory, serviceUrl, serverUrl);
|
|
37
|
+
|
|
38
|
+
const folders = (directory.folders || []) as string[];
|
|
39
|
+
const promises = folders.map(folder =>
|
|
40
|
+
loadServiceDirectory(`${serverUrl}`, fetch, [...path, folder])
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
for (const folderServices of await Promise.all(promises)) {
|
|
44
|
+
services.push(...folderServices);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return services;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function extractServices(directory: unknown, url: string, serverUrl: string): Service[] {
|
|
51
|
+
const arcgisServices = ((directory as any).services || []) as {name: string; type: string}[];
|
|
52
|
+
const services: Service[] = [];
|
|
53
|
+
for (const service of arcgisServices) {
|
|
54
|
+
services.push({
|
|
55
|
+
name: service.name,
|
|
56
|
+
type: `arcgis-${service.type.toLocaleLowerCase().replace('server', '-server')}`,
|
|
57
|
+
url: `${service.name.includes('/') ? serverUrl : url}/${service.name}/${service.type}`
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
return services;
|
|
61
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// loaders.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
/** Builds an ArcGIS REST resource URL while preserving authentication query parameters. */
|
|
6
|
+
export function buildArcGISResourceURL(
|
|
7
|
+
serviceURL: string,
|
|
8
|
+
resourcePath: string,
|
|
9
|
+
parameters: Record<string, unknown>
|
|
10
|
+
): string {
|
|
11
|
+
const url = new URL(serviceURL);
|
|
12
|
+
if (resourcePath) {
|
|
13
|
+
url.pathname = `${url.pathname.replace(/\/$/, '')}/${resourcePath.replace(/^\//, '')}`;
|
|
14
|
+
}
|
|
15
|
+
for (const [key, value] of Object.entries(parameters)) {
|
|
16
|
+
if (value === undefined || value === null) continue;
|
|
17
|
+
url.searchParams.set(key, encodeArcGISParameter(value));
|
|
18
|
+
}
|
|
19
|
+
return url.toString();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Converts a generic request value to ArcGIS REST query syntax. */
|
|
23
|
+
function encodeArcGISParameter(value: unknown): string {
|
|
24
|
+
if (Array.isArray(value)) return value.join(',');
|
|
25
|
+
return typeof value === 'object' ? JSON.stringify(value) : String(value);
|
|
26
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// loaders.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {ARCGIS_VECTOR_TILE_SERVER_SOURCE_LOADER_METADATA} from '../arcgis-source-types';
|
|
6
|
+
import type {CoreAPI, SourceLoader} from '@loaders.gl/loader-utils';
|
|
7
|
+
import type {ArcGISVectorTileServerSource} from './arcgis-vector-tile-server-source-loader';
|
|
8
|
+
import type {ArcGISVectorTileServerSourceLoaderOptions} from '../arcgis-source-types';
|
|
9
|
+
|
|
10
|
+
/** Loads the ArcGISVectorTileServerSourceLoader implementation on first asynchronous use. */
|
|
11
|
+
async function preloadArcGISVectorTileServerSourceLoader(): Promise<
|
|
12
|
+
SourceLoader<ArcGISVectorTileServerSource>
|
|
13
|
+
> {
|
|
14
|
+
const {ArcGISVectorTileServerSourceLoaderWithParser} = await import(
|
|
15
|
+
'@loaders.gl/arcgis/arcgis-vector-tile-server-source-loader'
|
|
16
|
+
);
|
|
17
|
+
return ArcGISVectorTileServerSourceLoaderWithParser;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Lightweight service descriptor; use load() or the synchronous /bundled entrypoint. */
|
|
21
|
+
export const ArcGISVectorTileServerSourceLoader = {
|
|
22
|
+
...ARCGIS_VECTOR_TILE_SERVER_SOURCE_LOADER_METADATA,
|
|
23
|
+
dataType: null as unknown as ArcGISVectorTileServerSource,
|
|
24
|
+
batchType: null as never,
|
|
25
|
+
preload: preloadArcGISVectorTileServerSourceLoader,
|
|
26
|
+
/** Requires the runtime loader when constructing a source synchronously. */
|
|
27
|
+
createDataSource(
|
|
28
|
+
_url: string,
|
|
29
|
+
_options: ArcGISVectorTileServerSourceLoaderOptions,
|
|
30
|
+
_coreApi?: CoreAPI
|
|
31
|
+
): ArcGISVectorTileServerSource {
|
|
32
|
+
throw new Error(
|
|
33
|
+
'ArcGISVectorTileServerSourceLoader requires async load() or an @loaders.gl/arcgis/bundled import'
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
} as const satisfies SourceLoader<ArcGISVectorTileServerSource>;
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
// loaders.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {ArcGISAuthentication} from '../authentication';
|
|
6
|
+
import {ARCGIS_VECTOR_TILE_SERVER_SOURCE_LOADER_METADATA} from '../arcgis-source-types';
|
|
7
|
+
import type {
|
|
8
|
+
ArcGISVectorTileServiceMetadata,
|
|
9
|
+
ArcGISVectorTileServerSourceLoaderOptions
|
|
10
|
+
} from '../arcgis-source-types';
|
|
11
|
+
|
|
12
|
+
import type {
|
|
13
|
+
CoreAPI,
|
|
14
|
+
GetTileDataParameters,
|
|
15
|
+
GetTileParameters,
|
|
16
|
+
SourceLoader,
|
|
17
|
+
TileSourceMetadata,
|
|
18
|
+
VectorTile,
|
|
19
|
+
VectorTileSource
|
|
20
|
+
} from '@loaders.gl/loader-utils';
|
|
21
|
+
import {DataSource} from '@loaders.gl/loader-utils';
|
|
22
|
+
import {MVTLoader, type MVTLoaderOptions} from '@loaders.gl/mvt';
|
|
23
|
+
import type {Schema} from '@loaders.gl/schema';
|
|
24
|
+
|
|
25
|
+
/** A source for ArcGIS VectorTileServer metadata and PBF tiles. */
|
|
26
|
+
export class ArcGISVectorTileServerSource
|
|
27
|
+
extends DataSource<string, ArcGISVectorTileServerSourceLoaderOptions>
|
|
28
|
+
implements VectorTileSource
|
|
29
|
+
{
|
|
30
|
+
/** Decoded tile family consumed by generic tile renderers. */
|
|
31
|
+
readonly mimeType = 'application/vnd.mapbox-vector-tile';
|
|
32
|
+
/** ArcGIS tiles are decoded to WGS84 coordinates before rendering. */
|
|
33
|
+
readonly localCoordinates = false;
|
|
34
|
+
/** Cached service metadata request. */
|
|
35
|
+
private serviceMetadata: Promise<ArcGISVectorTileServiceMetadata> | null = null;
|
|
36
|
+
/** Query parameters supplied with the service URL, such as an access token. */
|
|
37
|
+
private readonly serviceQueryParameters: URLSearchParams;
|
|
38
|
+
|
|
39
|
+
/** Creates an ArcGIS VectorTileServer source. */
|
|
40
|
+
constructor(
|
|
41
|
+
url: string,
|
|
42
|
+
options: ArcGISVectorTileServerSourceLoaderOptions = {},
|
|
43
|
+
coreApi?: CoreAPI
|
|
44
|
+
) {
|
|
45
|
+
const serviceURL = new URL(url);
|
|
46
|
+
serviceURL.pathname = serviceURL.pathname.replace(/\/$/, '');
|
|
47
|
+
super(
|
|
48
|
+
`${serviceURL.origin}${serviceURL.pathname}`,
|
|
49
|
+
options,
|
|
50
|
+
ARCGIS_VECTOR_TILE_SERVER_SOURCE_LOADER_METADATA.defaultOptions,
|
|
51
|
+
coreApi
|
|
52
|
+
);
|
|
53
|
+
this.serviceQueryParameters = new URLSearchParams(serviceURL.search);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Returns normalized service and tile-grid metadata. */
|
|
57
|
+
async getMetadata(): Promise<TileSourceMetadata> {
|
|
58
|
+
const metadata = await this.getServiceMetadata();
|
|
59
|
+
const tileInfo = metadata.tileInfo;
|
|
60
|
+
const lods = tileInfo?.lods || [];
|
|
61
|
+
const extent = metadata.fullExtent || metadata.initialExtent;
|
|
62
|
+
return {
|
|
63
|
+
name: metadata.mapName || this.url.split('/').pop() || '',
|
|
64
|
+
title: metadata.mapName,
|
|
65
|
+
abstract: metadata.serviceDescription,
|
|
66
|
+
format: 'application/vnd.mapbox-vector-tile',
|
|
67
|
+
minZoom: lods[0]?.level,
|
|
68
|
+
maxZoom: lods.at(-1)?.level,
|
|
69
|
+
boundingBox: extent
|
|
70
|
+
? [
|
|
71
|
+
[extent.xmin, extent.ymin],
|
|
72
|
+
[extent.xmax, extent.ymax]
|
|
73
|
+
]
|
|
74
|
+
: undefined,
|
|
75
|
+
layer: {
|
|
76
|
+
name: metadata.mapName || '',
|
|
77
|
+
srs: tileInfo?.spatialReference ? [getSpatialReference(tileInfo.spatialReference)] : [],
|
|
78
|
+
layers: []
|
|
79
|
+
},
|
|
80
|
+
formatHeader: {
|
|
81
|
+
tileInfo,
|
|
82
|
+
styleURL: this.getStyleURL(),
|
|
83
|
+
spriteURL: this.getSpriteURL()
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Fetches one raw PBF tile from the ArcGIS tile endpoint. */
|
|
89
|
+
async getTile(parameters: GetTileParameters, signal?: AbortSignal): Promise<ArrayBuffer | null> {
|
|
90
|
+
const response = await this.fetch(this.getTileURL(parameters), {
|
|
91
|
+
signal,
|
|
92
|
+
headers: {Accept: 'application/vnd.mapbox-vector-tile, application/x-protobuf'}
|
|
93
|
+
});
|
|
94
|
+
if (!response.ok) return null;
|
|
95
|
+
return response.arrayBuffer();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Returns the schema advertised by decoded vector tiles. */
|
|
99
|
+
async getSchema(): Promise<Schema> {
|
|
100
|
+
return {fields: [], metadata: {}};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Fetches and decodes one ArcGIS vector tile to geographic coordinates. */
|
|
104
|
+
async getVectorTile(parameters: GetTileParameters): Promise<VectorTile | null> {
|
|
105
|
+
const arrayBuffer = await this.getTile(parameters, parameters.signal);
|
|
106
|
+
if (!arrayBuffer) {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
const inheritedOptions = (this.loadOptions as MVTLoaderOptions).mvt;
|
|
110
|
+
const sourceOptions = this.options['arcgis-vector-tile-server']?.mvt;
|
|
111
|
+
return (await this.coreApi.parse(arrayBuffer, MVTLoader, {
|
|
112
|
+
...this.loadOptions,
|
|
113
|
+
mvt: {
|
|
114
|
+
...inheritedOptions,
|
|
115
|
+
...sourceOptions,
|
|
116
|
+
coordinates: 'wgs84',
|
|
117
|
+
tileIndex: {x: parameters.x, y: parameters.y, z: parameters.z},
|
|
118
|
+
layers:
|
|
119
|
+
normalizeLayers(parameters.layers) || sourceOptions?.layers || inheritedOptions?.layers
|
|
120
|
+
}
|
|
121
|
+
})) as VectorTile;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Provides decoded vector tiles through the deck.gl source interface. */
|
|
125
|
+
async getTileData(parameters: GetTileDataParameters): Promise<VectorTile | null> {
|
|
126
|
+
return this.getVectorTile({...parameters.index, signal: parameters.signal});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Builds the ArcGIS cached vector tile URL. */
|
|
130
|
+
getTileURL(parameters: GetTileParameters): string {
|
|
131
|
+
return this.getResourceURL(`/tile/${parameters.z}/${parameters.y}/${parameters.x}.pbf`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** Returns the service metadata URL. */
|
|
135
|
+
getMetadataURL(): string {
|
|
136
|
+
const url = new URL(this.getResourceURL(''));
|
|
137
|
+
url.searchParams.set('f', 'pjson');
|
|
138
|
+
return url.toString();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Returns the ArcGIS Mapbox style resource URL. */
|
|
142
|
+
getStyleURL(): string {
|
|
143
|
+
return this.getResourceURL('/resources/styles/root.json');
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Returns the ArcGIS sprite resource base URL. */
|
|
147
|
+
getSpriteURL(): string {
|
|
148
|
+
return this.getResourceURL('/resources/sprites/sprite');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Loads and caches the ArcGIS VectorTileServer metadata document. */
|
|
152
|
+
private async getServiceMetadata(): Promise<ArcGISVectorTileServiceMetadata> {
|
|
153
|
+
this.serviceMetadata ||= this.fetch(this.getMetadataURL()).then(async response => {
|
|
154
|
+
if (!response.ok)
|
|
155
|
+
throw new Error(`ArcGIS VectorTileServer request failed: ${response.status}`);
|
|
156
|
+
return (await response.json()) as ArcGISVectorTileServiceMetadata;
|
|
157
|
+
});
|
|
158
|
+
return this.serviceMetadata;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** Builds a resource URL while retaining service query parameters. */
|
|
162
|
+
private getResourceURL(resourcePath: string): string {
|
|
163
|
+
const url = new URL(this.url);
|
|
164
|
+
url.pathname = `${url.pathname.replace(/\/$/, '')}${resourcePath}`;
|
|
165
|
+
for (const [key, value] of this.serviceQueryParameters) {
|
|
166
|
+
url.searchParams.set(key, value);
|
|
167
|
+
}
|
|
168
|
+
return url.toString();
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Runtime service loader for synchronous construction. */
|
|
173
|
+
export const ArcGISVectorTileServerSourceLoaderWithParser = {
|
|
174
|
+
...ARCGIS_VECTOR_TILE_SERVER_SOURCE_LOADER_METADATA,
|
|
175
|
+
dataType: null as unknown as ArcGISVectorTileServerSource,
|
|
176
|
+
batchType: null as never,
|
|
177
|
+
preload: undefined,
|
|
178
|
+
/** Supplies ArcGIS credentials after loading the selected implementation. */
|
|
179
|
+
getAuthentications: () => [ArcGISAuthentication],
|
|
180
|
+
createDataSource: (
|
|
181
|
+
url: string,
|
|
182
|
+
options: ArcGISVectorTileServerSourceLoaderOptions = {},
|
|
183
|
+
coreApi?: CoreAPI
|
|
184
|
+
) => new ArcGISVectorTileServerSource(url, options, coreApi)
|
|
185
|
+
} as const satisfies SourceLoader<ArcGISVectorTileServerSource>;
|
|
186
|
+
|
|
187
|
+
/** Converts an ArcGIS spatial reference to an EPSG identifier. */
|
|
188
|
+
function getSpatialReference(spatialReference: {wkid?: number; latestWkid?: number}): string {
|
|
189
|
+
return `EPSG:${spatialReference.latestWkid || spatialReference.wkid}`;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** Normalizes generic tile layer selection for the MVT parser. */
|
|
193
|
+
function normalizeLayers(layers?: string | string[]): string[] | undefined {
|
|
194
|
+
if (!layers) return undefined;
|
|
195
|
+
return Array.isArray(layers) ? layers : [layers];
|
|
196
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// loaders.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import type {GeoJSONTable} from '@loaders.gl/schema';
|
|
6
|
+
import type {GetFeaturesParameters} from '@loaders.gl/loader-utils';
|
|
7
|
+
import type {ArcGISFeatureServiceQueryOptions} from './arcgis-source-types';
|
|
8
|
+
|
|
9
|
+
/** Parameters shared by feature, table and MapServer layer queries. */
|
|
10
|
+
export type ArcGISFeatureQueryParameters = Partial<GetFeaturesParameters> & {
|
|
11
|
+
/** Per-request filters, overriding source query defaults. */
|
|
12
|
+
query?: ArcGISFeatureServiceQueryOptions;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/** Controls bounded retrieval of a complete query. */
|
|
16
|
+
export type ArcGISFeatureQueryOptions = ArcGISFeatureQueryParameters & {
|
|
17
|
+
/** Maximum records per page, capped by the advertised service limit. Default 1000. */
|
|
18
|
+
pageSize?: number;
|
|
19
|
+
/** Maximum retained records. Default 100000; reaching it can produce an incomplete result. */
|
|
20
|
+
maxFeatures?: number;
|
|
21
|
+
/** Concurrent object-ID batches, between 1 and 8. Default 4; offset pages are serial. */
|
|
22
|
+
concurrency?: number;
|
|
23
|
+
/** Auto uses ordered pagination when advertised, otherwise object-ID batches. */
|
|
24
|
+
strategy?: 'auto' | 'offset' | 'object-ids';
|
|
25
|
+
/** Reports each yielded page, including the final completion assessment. */
|
|
26
|
+
onProgress?: (progress: ArcGISFeatureQueryProgress) => void;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/** Completion evidence for the selected query, never a transactional snapshot guarantee. */
|
|
30
|
+
export type ArcGISFeatureQueryProgress = {
|
|
31
|
+
/** Number of unique records retained so far. */
|
|
32
|
+
loaded: number;
|
|
33
|
+
/** Count observed before retrieval; absent for a single-page request. */
|
|
34
|
+
expectedCount?: number;
|
|
35
|
+
/** Number of data pages processed. */
|
|
36
|
+
pages: number;
|
|
37
|
+
/** Duplicate records discarded while retrieving pages. */
|
|
38
|
+
duplicates: number;
|
|
39
|
+
/** True only after traversal finishes and its unique count matches the initial server count. */
|
|
40
|
+
complete: boolean;
|
|
41
|
+
/** Whether the query includes a viewport bounding box. */
|
|
42
|
+
scope: 'viewport' | 'query';
|
|
43
|
+
/** Retrieval strategy used for this result. */
|
|
44
|
+
strategy: 'offset' | 'object-ids' | 'page';
|
|
45
|
+
/** Why retrieval is in progress, finished, or incomplete. */
|
|
46
|
+
reason:
|
|
47
|
+
| 'loading'
|
|
48
|
+
| 'complete'
|
|
49
|
+
| 'single-page'
|
|
50
|
+
| 'feature-limit'
|
|
51
|
+
| 'id-limit'
|
|
52
|
+
| 'count-mismatch'
|
|
53
|
+
| 'no-progress';
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/** One page with cumulative progress, or the collected result of queryFeatures(). */
|
|
57
|
+
export type ArcGISFeatureQueryResult = ArcGISFeatureQueryProgress & {
|
|
58
|
+
/** Spatial features or table records represented as features with null geometry. */
|
|
59
|
+
data: GeoJSONTable;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/** Result of an IDs-only request; this alone does not prove query completeness. */
|
|
63
|
+
export type ArcGISFeatureObjectIds = {
|
|
64
|
+
/** Stable object ID field advertised by the response. */
|
|
65
|
+
objectIdFieldName: string;
|
|
66
|
+
/** Unique safe-integer or string identifiers returned by the service. */
|
|
67
|
+
objectIds: (number | string)[];
|
|
68
|
+
/** True when the service explicitly reports truncated identifiers. */
|
|
69
|
+
exceededTransferLimit: boolean;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/** Extent-only response, retaining the service's explicit spatial reference. */
|
|
73
|
+
export type ArcGISFeatureExtent = {
|
|
74
|
+
/** Matching record count when supplied by the server. */
|
|
75
|
+
count?: number;
|
|
76
|
+
/** Null for an empty or nonspatial query. */
|
|
77
|
+
extent: {
|
|
78
|
+
/** Minimum x coordinate. */
|
|
79
|
+
xmin: number;
|
|
80
|
+
/** Minimum y coordinate. */
|
|
81
|
+
ymin: number;
|
|
82
|
+
/** Maximum x coordinate. */
|
|
83
|
+
xmax: number;
|
|
84
|
+
/** Maximum y coordinate. */
|
|
85
|
+
ymax: number;
|
|
86
|
+
/** Coordinate reference of the returned bounds. */
|
|
87
|
+
spatialReference: {wkid?: number; latestWkid?: number; wkt?: string};
|
|
88
|
+
} | null;
|
|
89
|
+
};
|