esri-gl 1.0.5 → 2.0.0
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/README.md +55 -1
- package/dist/{index-DczUYC4z.d.ts → index-Doq93o-G.d.ts} +252 -75
- package/dist/{IdentifyImage-DmyKcbAv.js → index-DsY1_0df.js} +825 -678
- package/dist/index-DsY1_0df.js.map +1 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +7181 -1017
- package/dist/index.umd.js.map +1 -1
- package/dist/package.json +5 -1
- package/dist/react-map-gl.d.ts +57 -10
- package/dist/react-map-gl.js +313 -290
- package/dist/react-map-gl.js.map +1 -1
- package/dist/react.d.ts +23 -28
- package/dist/react.js +202 -178
- package/dist/react.js.map +1 -1
- package/dist/{useFeatureService-E9PiUOLP.js → useFeatureService-BRY6PHUs.js} +50 -12
- package/dist/useFeatureService-BRY6PHUs.js.map +1 -0
- package/dist/{useFeatureService-QUqwJcet.d.ts → useFeatureService-CG70gjQy.d.ts} +29 -41
- package/package.json +10 -7
- package/dist/IdentifyImage-DmyKcbAv.js.map +0 -1
- package/dist/useFeatureService-E9PiUOLP.js.map +0 -1
package/dist/react-map-gl.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
export { a as Find, I as IdentifyFeatures, b as IdentifyImage, Q as Query, S as Service, T as Task, f as cleanTrailingSlash, g as find, h as getServiceDetails, i as identifyImage, q as query, u as updateAttribution } from './IdentifyImage-DmyKcbAv.js';
|
|
1
|
+
import { D as DynamicMapService, d as TiledMapService, c as ImageService, e as VectorTileService, g as esriRequest, V as VectorBasemapStyle, F as FeatureService, k as serviceFromPortalItem } from './index-DsY1_0df.js';
|
|
2
|
+
export { a as Find, I as IdentifyFeatures, b as IdentifyImage, Q as Query, S as Service, T as Task, f as cleanTrailingSlash, h as find, i as getServiceDetails, j as identifyImage, q as query, r as resolveAuthentication, s as searchPortalItems, l as servicesFromWebMap, u as updateAttribution } from './index-DsY1_0df.js';
|
|
4
3
|
import { useMap } from 'react-map-gl/mapbox';
|
|
5
4
|
import { useMap as useMap$1 } from 'react-map-gl/maplibre';
|
|
6
|
-
import {
|
|
5
|
+
import { useState, useEffect, useRef, useMemo } from 'react';
|
|
6
|
+
import { b as useFeatureService, e as useVectorTileService, c as useImageService, d as useTiledMapService, u as useDynamicMapService } from './useFeatureService-BRY6PHUs.js';
|
|
7
|
+
export { BasemapStyleSession } from '@esri/arcgis-rest-basemap-sessions';
|
|
8
|
+
export { ApiKeyManager, ApplicationCredentialsManager, ArcGISAuthError, ArcGISIdentityManager, ArcGISRequestError } from '@esri/arcgis-rest-request';
|
|
9
|
+
export { SearchQueryBuilder } from '@esri/arcgis-rest-portal';
|
|
10
|
+
import '@esri/arcgis-rest-feature-service';
|
|
7
11
|
import '@mapbox/tilebelt';
|
|
8
12
|
import 'arcgis-pbf-parser';
|
|
9
13
|
|
|
@@ -41,83 +45,154 @@ function useReactMapGL() {
|
|
|
41
45
|
}
|
|
42
46
|
|
|
43
47
|
/**
|
|
44
|
-
*
|
|
48
|
+
* Copy any defined auth/passthrough props from a layer component's props
|
|
49
|
+
* onto a service-options object. Undefined values are skipped so they
|
|
50
|
+
* do not override service defaults.
|
|
45
51
|
*/
|
|
46
|
-
function
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
52
|
+
function applyAuthOptions(options, props) {
|
|
53
|
+
const target = options;
|
|
54
|
+
if (props.token !== undefined) target.token = props.token;
|
|
55
|
+
if (props.apiKey !== undefined) target.apiKey = props.apiKey;
|
|
56
|
+
if (props.authentication !== undefined) target.authentication = props.authentication;
|
|
57
|
+
if (props.proxy !== undefined) target.proxy = props.proxy;
|
|
58
|
+
if (props.getAttributionFromService !== undefined) target.getAttributionFromService = props.getAttributionFromService;
|
|
59
|
+
if (props.requestParams !== undefined) target.requestParams = props.requestParams;
|
|
60
|
+
if (props.fetchOptions !== undefined) target.fetchOptions = props.fetchOptions;
|
|
61
|
+
return options;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Returns true once the underlying map's style has finished loading.
|
|
66
|
+
* Returns false when there is no map yet, the map does not expose the
|
|
67
|
+
* expected lifecycle API, or the style has not finished loading.
|
|
68
|
+
*/
|
|
69
|
+
function useMapLoaded(map) {
|
|
70
|
+
const [isLoaded, setIsLoaded] = useState(false);
|
|
53
71
|
useEffect(() => {
|
|
54
|
-
if (!map)
|
|
72
|
+
if (!map) {
|
|
73
|
+
setIsLoaded(false);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
55
76
|
const mapInstance = map.getMap?.();
|
|
56
|
-
|
|
57
|
-
|
|
77
|
+
if (!mapInstance || typeof mapInstance.isStyleLoaded !== 'function') {
|
|
78
|
+
setIsLoaded(false);
|
|
58
79
|
return;
|
|
59
80
|
}
|
|
60
|
-
if (
|
|
61
|
-
|
|
81
|
+
if (mapInstance.isStyleLoaded()) {
|
|
82
|
+
setIsLoaded(true);
|
|
62
83
|
return;
|
|
63
84
|
}
|
|
64
|
-
const handleLoad = () =>
|
|
65
|
-
|
|
85
|
+
const handleLoad = () => setIsLoaded(true);
|
|
86
|
+
mapInstance.once?.('load', handleLoad);
|
|
66
87
|
return () => {
|
|
67
|
-
|
|
88
|
+
mapInstance.off?.('load', handleLoad);
|
|
68
89
|
};
|
|
69
90
|
}, [map]);
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
91
|
+
return isLoaded;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Shared lifecycle for react-map-gl components that wrap an Esri raster
|
|
96
|
+
* service. Waits for the map style to load, then (re)builds the service and its
|
|
97
|
+
* raster layer in a single effect so that when `serviceDeps` change the old
|
|
98
|
+
* source is torn down *before* the new service recreates it — otherwise the
|
|
99
|
+
* service constructor would see the stale source and skip re-adding it.
|
|
100
|
+
*/
|
|
101
|
+
function useRasterLayer(options) {
|
|
102
|
+
const {
|
|
103
|
+
map,
|
|
104
|
+
layerId,
|
|
105
|
+
sourceId,
|
|
106
|
+
beforeId,
|
|
107
|
+
visible,
|
|
108
|
+
serviceDeps,
|
|
109
|
+
createService
|
|
110
|
+
} = options;
|
|
111
|
+
const isMapLoaded = useMapLoaded(map);
|
|
112
|
+
const [service, setService] = useState(null);
|
|
113
|
+
// Keep the latest createService without making it a dependency (callers pass
|
|
114
|
+
// a fresh closure each render; serviceDeps captures what actually matters).
|
|
115
|
+
const createServiceRef = useRef(createService);
|
|
116
|
+
createServiceRef.current = createService;
|
|
87
117
|
useEffect(() => {
|
|
88
|
-
if (!map || !
|
|
118
|
+
if (!map || !isMapLoaded) {
|
|
119
|
+
setService(null);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
89
122
|
const mapInstance = map.getMap?.();
|
|
90
|
-
if (!mapInstance || typeof mapInstance.
|
|
91
|
-
return
|
|
92
|
-
service.remove();
|
|
93
|
-
};
|
|
123
|
+
if (!mapInstance || typeof mapInstance.addLayer !== 'function') {
|
|
124
|
+
return;
|
|
94
125
|
}
|
|
95
|
-
|
|
96
|
-
|
|
126
|
+
const svc = createServiceRef.current(mapInstance, sourceId);
|
|
127
|
+
setService(svc);
|
|
128
|
+
// Add the raster layer once its source exists.
|
|
129
|
+
const sourceReady = typeof mapInstance.getSource !== 'function' || Boolean(mapInstance.getSource(sourceId));
|
|
130
|
+
if (!mapInstance.getLayer?.(layerId) && sourceReady) {
|
|
97
131
|
const layerConfig = {
|
|
98
|
-
id:
|
|
132
|
+
id: layerId,
|
|
99
133
|
type: 'raster',
|
|
100
134
|
source: sourceId,
|
|
101
135
|
layout: {
|
|
102
|
-
visibility:
|
|
136
|
+
visibility: visible !== false ? 'visible' : 'none'
|
|
103
137
|
}
|
|
104
138
|
};
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
139
|
+
try {
|
|
140
|
+
if (beforeId) {
|
|
141
|
+
mapInstance.addLayer?.(layerConfig, beforeId);
|
|
142
|
+
} else {
|
|
143
|
+
mapInstance.addLayer?.(layerConfig);
|
|
144
|
+
}
|
|
145
|
+
} catch (err) {
|
|
146
|
+
if (process.env?.NODE_ENV !== 'test') {
|
|
147
|
+
console.warn(`useRasterLayer: skipped adding layer "${layerId}"`, err);
|
|
148
|
+
}
|
|
109
149
|
}
|
|
110
150
|
}
|
|
111
|
-
// Cleanup function
|
|
112
151
|
return () => {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
152
|
+
// Remove the layer first, then the service (which removes its source), so
|
|
153
|
+
// a subsequent rebuild gets a clean slate for the same sourceId.
|
|
154
|
+
try {
|
|
155
|
+
if (mapInstance.getStyle?.() && mapInstance.getLayer?.(layerId)) {
|
|
156
|
+
mapInstance.removeLayer?.(layerId);
|
|
157
|
+
}
|
|
158
|
+
} catch {
|
|
159
|
+
// layer may already be gone
|
|
118
160
|
}
|
|
161
|
+
svc.remove();
|
|
119
162
|
};
|
|
120
|
-
|
|
163
|
+
// serviceDeps is spread so any change rebuilds the layer + source.
|
|
164
|
+
}, [map, isMapLoaded, sourceId, layerId, beforeId, visible, ...serviceDeps]);
|
|
165
|
+
return service;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* React Map GL component for Esri Dynamic Map Service
|
|
170
|
+
*/
|
|
171
|
+
function EsriDynamicLayer(props) {
|
|
172
|
+
const {
|
|
173
|
+
current: map
|
|
174
|
+
} = useReactMapGL();
|
|
175
|
+
const sourceId = props.sourceId || `esri-dynamic-${props.id}`;
|
|
176
|
+
useRasterLayer({
|
|
177
|
+
map,
|
|
178
|
+
layerId: props.id,
|
|
179
|
+
sourceId,
|
|
180
|
+
beforeId: props.beforeId,
|
|
181
|
+
visible: props.visible,
|
|
182
|
+
serviceDeps: [props.url, props.layers, props.layerDefs, props.format, props.dpi, props.transparent, props.token, props.apiKey, props.authentication, props.proxy, props.getAttributionFromService, props.requestParams, props.fetchOptions],
|
|
183
|
+
createService: (mapInstance, resolvedSourceId) => {
|
|
184
|
+
const options = {
|
|
185
|
+
url: props.url
|
|
186
|
+
};
|
|
187
|
+
if (props.layers !== undefined) options.layers = props.layers;
|
|
188
|
+
if (props.layerDefs !== undefined) options.layerDefs = props.layerDefs;
|
|
189
|
+
if (props.format !== undefined) options.format = props.format;
|
|
190
|
+
if (props.dpi !== undefined) options.dpi = props.dpi;
|
|
191
|
+
if (props.transparent !== undefined) options.transparent = props.transparent;
|
|
192
|
+
applyAuthOptions(options, props);
|
|
193
|
+
return new DynamicMapService(resolvedSourceId, mapInstance, options);
|
|
194
|
+
}
|
|
195
|
+
});
|
|
121
196
|
return null;
|
|
122
197
|
}
|
|
123
198
|
|
|
@@ -129,67 +204,21 @@ function EsriTiledLayer(props) {
|
|
|
129
204
|
current: map
|
|
130
205
|
} = useReactMapGL();
|
|
131
206
|
const sourceId = props.sourceId || `esri-tiled-${props.id}`;
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
setIsMapLoaded(true);
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
const handleLoad = () => setIsMapLoaded(true);
|
|
146
|
-
mi?.once?.('load', handleLoad);
|
|
147
|
-
return () => {
|
|
148
|
-
mi?.off?.('load', handleLoad);
|
|
149
|
-
};
|
|
150
|
-
}, [map]);
|
|
151
|
-
const service = useMemo(() => {
|
|
152
|
-
if (!map || !isMapLoaded) return null;
|
|
153
|
-
const mapInstance = map.getMap?.();
|
|
154
|
-
if (!mapInstance) return null;
|
|
155
|
-
return new TiledMapService(sourceId, mapInstance, {
|
|
156
|
-
url: props.url
|
|
157
|
-
});
|
|
158
|
-
}, [map, isMapLoaded, sourceId, props.url]);
|
|
159
|
-
useEffect(() => {
|
|
160
|
-
if (!map || !service) return;
|
|
161
|
-
const mapInstance = map.getMap?.();
|
|
162
|
-
if (!mapInstance || typeof mapInstance.getLayer !== 'function' || typeof mapInstance.addLayer !== 'function') {
|
|
163
|
-
return () => {
|
|
164
|
-
service.remove();
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
// Add raster layer
|
|
168
|
-
if (!mapInstance.getLayer(props.id)) {
|
|
169
|
-
const layerConfig = {
|
|
170
|
-
id: props.id,
|
|
171
|
-
type: 'raster',
|
|
172
|
-
source: sourceId,
|
|
173
|
-
layout: {
|
|
174
|
-
visibility: props.visible !== false ? 'visible' : 'none'
|
|
175
|
-
}
|
|
207
|
+
useRasterLayer({
|
|
208
|
+
map,
|
|
209
|
+
layerId: props.id,
|
|
210
|
+
sourceId,
|
|
211
|
+
beforeId: props.beforeId,
|
|
212
|
+
visible: props.visible,
|
|
213
|
+
serviceDeps: [props.url, props.token, props.apiKey, props.authentication, props.proxy, props.getAttributionFromService, props.requestParams, props.fetchOptions],
|
|
214
|
+
createService: (mapInstance, resolvedSourceId) => {
|
|
215
|
+
const options = {
|
|
216
|
+
url: props.url
|
|
176
217
|
};
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
} else {
|
|
180
|
-
mapInstance.addLayer(layerConfig);
|
|
181
|
-
}
|
|
218
|
+
applyAuthOptions(options, props);
|
|
219
|
+
return new TiledMapService(resolvedSourceId, mapInstance, options);
|
|
182
220
|
}
|
|
183
|
-
|
|
184
|
-
return () => {
|
|
185
|
-
if (mapInstance.getStyle?.() && mapInstance.getLayer?.(props.id)) {
|
|
186
|
-
mapInstance.removeLayer(props.id);
|
|
187
|
-
}
|
|
188
|
-
if (service) {
|
|
189
|
-
service.remove();
|
|
190
|
-
}
|
|
191
|
-
};
|
|
192
|
-
}, [map, service, props.id, props.beforeId, props.visible, sourceId]);
|
|
221
|
+
});
|
|
193
222
|
return null;
|
|
194
223
|
}
|
|
195
224
|
|
|
@@ -201,72 +230,24 @@ function EsriImageLayer(props) {
|
|
|
201
230
|
current: map
|
|
202
231
|
} = useReactMapGL();
|
|
203
232
|
const sourceId = props.sourceId || `esri-image-${props.id}`;
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
setIsMapLoaded(true);
|
|
215
|
-
return;
|
|
216
|
-
}
|
|
217
|
-
const handleLoad = () => setIsMapLoaded(true);
|
|
218
|
-
mi?.once?.('load', handleLoad);
|
|
219
|
-
return () => {
|
|
220
|
-
mi?.off?.('load', handleLoad);
|
|
221
|
-
};
|
|
222
|
-
}, [map]);
|
|
223
|
-
const service = useMemo(() => {
|
|
224
|
-
if (!map || !isMapLoaded) return null;
|
|
225
|
-
const mapInstance = map.getMap?.();
|
|
226
|
-
if (!mapInstance) return null;
|
|
227
|
-
// Only include defined properties to avoid overriding defaults with undefined
|
|
228
|
-
const options = {
|
|
229
|
-
url: props.url
|
|
230
|
-
};
|
|
231
|
-
if (props.renderingRule !== undefined) options.renderingRule = props.renderingRule;
|
|
232
|
-
if (props.mosaicRule !== undefined) options.mosaicRule = props.mosaicRule;
|
|
233
|
-
if (props.format !== undefined) options.format = props.format;
|
|
234
|
-
return new ImageService(sourceId, mapInstance, options);
|
|
235
|
-
}, [map, isMapLoaded, sourceId, props.url, props.renderingRule, props.mosaicRule, props.format]);
|
|
236
|
-
useEffect(() => {
|
|
237
|
-
if (!map || !service) return;
|
|
238
|
-
const mapInstance = map.getMap?.();
|
|
239
|
-
if (!mapInstance || typeof mapInstance.getLayer !== 'function' || typeof mapInstance.addLayer !== 'function') {
|
|
240
|
-
return () => {
|
|
241
|
-
service.remove();
|
|
242
|
-
};
|
|
243
|
-
}
|
|
244
|
-
// Add raster layer
|
|
245
|
-
if (!mapInstance.getLayer(props.id)) {
|
|
246
|
-
const layerConfig = {
|
|
247
|
-
id: props.id,
|
|
248
|
-
type: 'raster',
|
|
249
|
-
source: sourceId,
|
|
250
|
-
layout: {
|
|
251
|
-
visibility: props.visible !== false ? 'visible' : 'none'
|
|
252
|
-
}
|
|
233
|
+
useRasterLayer({
|
|
234
|
+
map,
|
|
235
|
+
layerId: props.id,
|
|
236
|
+
sourceId,
|
|
237
|
+
beforeId: props.beforeId,
|
|
238
|
+
visible: props.visible,
|
|
239
|
+
serviceDeps: [props.url, props.renderingRule, props.mosaicRule, props.format, props.token, props.apiKey, props.authentication, props.proxy, props.getAttributionFromService, props.requestParams, props.fetchOptions],
|
|
240
|
+
createService: (mapInstance, resolvedSourceId) => {
|
|
241
|
+
const options = {
|
|
242
|
+
url: props.url
|
|
253
243
|
};
|
|
254
|
-
if (props.
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
244
|
+
if (props.renderingRule !== undefined) options.renderingRule = props.renderingRule;
|
|
245
|
+
if (props.mosaicRule !== undefined) options.mosaicRule = props.mosaicRule;
|
|
246
|
+
if (props.format !== undefined) options.format = props.format;
|
|
247
|
+
applyAuthOptions(options, props);
|
|
248
|
+
return new ImageService(resolvedSourceId, mapInstance, options);
|
|
259
249
|
}
|
|
260
|
-
|
|
261
|
-
return () => {
|
|
262
|
-
if (mapInstance.getStyle?.() && mapInstance.getLayer?.(props.id)) {
|
|
263
|
-
mapInstance.removeLayer(props.id);
|
|
264
|
-
}
|
|
265
|
-
if (service) {
|
|
266
|
-
service.remove();
|
|
267
|
-
}
|
|
268
|
-
};
|
|
269
|
-
}, [map, service, props.id, props.beforeId, props.visible, sourceId]);
|
|
250
|
+
});
|
|
270
251
|
return null;
|
|
271
252
|
}
|
|
272
253
|
|
|
@@ -278,53 +259,38 @@ function EsriVectorTileLayer(props) {
|
|
|
278
259
|
current: map
|
|
279
260
|
} = useReactMapGL();
|
|
280
261
|
const sourceId = props.sourceId || `esri-vector-tile-${props.id}`;
|
|
281
|
-
const
|
|
262
|
+
const isMapLoaded = useMapLoaded(map);
|
|
282
263
|
const serviceRef = useRef(null);
|
|
283
264
|
const layerIdsRef = useRef([]);
|
|
284
|
-
// Wait for map to be loaded before creating service
|
|
285
|
-
useEffect(() => {
|
|
286
|
-
if (!map) return;
|
|
287
|
-
const mapInstance = map.getMap?.();
|
|
288
|
-
const mi = mapInstance;
|
|
289
|
-
if (!mi || typeof mi.isStyleLoaded !== 'function') return;
|
|
290
|
-
if (mi.isStyleLoaded()) {
|
|
291
|
-
setIsMapLoaded(true);
|
|
292
|
-
return;
|
|
293
|
-
}
|
|
294
|
-
const handleLoad = () => setIsMapLoaded(true);
|
|
295
|
-
mi?.once?.('load', handleLoad);
|
|
296
|
-
return () => {
|
|
297
|
-
mi?.off?.('load', handleLoad);
|
|
298
|
-
};
|
|
299
|
-
}, [map]);
|
|
300
|
-
// Create VectorTileService, fetch style, add layers, and clean up on unmount
|
|
301
265
|
useEffect(() => {
|
|
302
266
|
if (!map || !isMapLoaded) return;
|
|
303
267
|
const mapInstance = map.getMap?.();
|
|
304
268
|
if (!mapInstance) return;
|
|
305
|
-
const
|
|
269
|
+
const layerApi = mapInstance;
|
|
306
270
|
let cancelled = false;
|
|
307
|
-
const
|
|
271
|
+
const options = {
|
|
308
272
|
url: props.url
|
|
309
|
-
}
|
|
273
|
+
};
|
|
274
|
+
applyAuthOptions(options, props);
|
|
275
|
+
const service = new VectorTileService(sourceId, mapInstance, options);
|
|
310
276
|
serviceRef.current = service;
|
|
311
|
-
// Fetch the full style and add all layers from it
|
|
312
277
|
service.getStyle().then(() => {
|
|
313
|
-
if (cancelled) return;
|
|
314
|
-
// Fetch the full style document
|
|
315
|
-
|
|
316
|
-
return
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
278
|
+
if (cancelled) return undefined;
|
|
279
|
+
// Fetch the full style document through the shared request layer so
|
|
280
|
+
// auth (token / apiKey / authentication) is handled consistently.
|
|
281
|
+
return esriRequest(`${props.url}/resources/styles/root.json`, {
|
|
282
|
+
httpMethod: 'GET',
|
|
283
|
+
token: props.token,
|
|
284
|
+
apiKey: props.apiKey,
|
|
285
|
+
authentication: props.authentication
|
|
286
|
+
});
|
|
321
287
|
}).then(data => {
|
|
322
288
|
if (cancelled || !data?.layers) return;
|
|
323
289
|
const addedIds = [];
|
|
324
290
|
for (const layer of data.layers) {
|
|
325
291
|
if (!layer['source-layer']) continue;
|
|
326
292
|
const layerId = `${props.id}-${layer.id}`;
|
|
327
|
-
if (
|
|
293
|
+
if (layerApi.getLayer?.(layerId)) continue;
|
|
328
294
|
const layerConfig = {
|
|
329
295
|
id: layerId,
|
|
330
296
|
type: layer.type,
|
|
@@ -336,17 +302,16 @@ function EsriVectorTileLayer(props) {
|
|
|
336
302
|
if (layer.filter) layerConfig.filter = layer.filter;
|
|
337
303
|
if (layer.minzoom !== undefined) layerConfig.minzoom = layer.minzoom;
|
|
338
304
|
if (layer.maxzoom !== undefined) layerConfig.maxzoom = layer.maxzoom;
|
|
339
|
-
// Apply visibility from props
|
|
340
305
|
if (props.visible === false) {
|
|
341
306
|
layerConfig.layout = {
|
|
342
|
-
...layerConfig.layout,
|
|
307
|
+
...(layerConfig.layout || {}),
|
|
343
308
|
visibility: 'none'
|
|
344
309
|
};
|
|
345
310
|
}
|
|
346
311
|
if (props.beforeId) {
|
|
347
|
-
|
|
312
|
+
layerApi.addLayer?.(layerConfig, props.beforeId);
|
|
348
313
|
} else {
|
|
349
|
-
|
|
314
|
+
layerApi.addLayer?.(layerConfig);
|
|
350
315
|
}
|
|
351
316
|
addedIds.push(layerId);
|
|
352
317
|
}
|
|
@@ -356,11 +321,10 @@ function EsriVectorTileLayer(props) {
|
|
|
356
321
|
});
|
|
357
322
|
return () => {
|
|
358
323
|
cancelled = true;
|
|
359
|
-
|
|
360
|
-
if (mi.getStyle?.()) {
|
|
324
|
+
if (layerApi.getStyle?.()) {
|
|
361
325
|
for (const id of layerIdsRef.current) {
|
|
362
326
|
try {
|
|
363
|
-
if (
|
|
327
|
+
if (layerApi.getLayer?.(id)) layerApi.removeLayer?.(id);
|
|
364
328
|
} catch {
|
|
365
329
|
// layer may already be gone
|
|
366
330
|
}
|
|
@@ -370,7 +334,7 @@ function EsriVectorTileLayer(props) {
|
|
|
370
334
|
service.remove();
|
|
371
335
|
serviceRef.current = null;
|
|
372
336
|
};
|
|
373
|
-
}, [map, isMapLoaded, sourceId, props.url, props.id, props.beforeId, props.visible]);
|
|
337
|
+
}, [map, isMapLoaded, sourceId, props.url, props.id, props.beforeId, props.visible, props.token, props.apiKey, props.authentication, props.proxy, props.getAttributionFromService, props.requestParams, props.fetchOptions]);
|
|
374
338
|
return null;
|
|
375
339
|
}
|
|
376
340
|
|
|
@@ -382,10 +346,15 @@ function EsriVectorBasemapLayer(props) {
|
|
|
382
346
|
current: map
|
|
383
347
|
} = useReactMapGL();
|
|
384
348
|
const service = useMemo(() => {
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
}
|
|
388
|
-
|
|
349
|
+
// Forward all the auth/locale options VectorBasemapStyle supports (token or
|
|
350
|
+
// apiKey, plus language/worldview), only including the ones provided.
|
|
351
|
+
const auth = {};
|
|
352
|
+
if (props.token !== undefined) auth.token = props.token;
|
|
353
|
+
if (props.apiKey !== undefined) auth.apiKey = props.apiKey;
|
|
354
|
+
if (props.language !== undefined) auth.language = props.language;
|
|
355
|
+
if (props.worldview !== undefined) auth.worldview = props.worldview;
|
|
356
|
+
return new VectorBasemapStyle(props.basemapEnum, auth);
|
|
357
|
+
}, [props.basemapEnum, props.token, props.apiKey, props.language, props.worldview]);
|
|
389
358
|
useEffect(() => {
|
|
390
359
|
if (!map || !service) return;
|
|
391
360
|
// Vector basemap styles replace the entire map style
|
|
@@ -409,47 +378,27 @@ function EsriFeatureLayer(props) {
|
|
|
409
378
|
current: map
|
|
410
379
|
} = useReactMapGL();
|
|
411
380
|
const sourceId = props.sourceId || `esri-feature-${props.id}`;
|
|
412
|
-
const
|
|
381
|
+
const isMapLoaded = useMapLoaded(map);
|
|
413
382
|
const serviceRef = useRef(null);
|
|
414
383
|
// Keep stable refs for object props to avoid effect re-runs on every render
|
|
415
384
|
const paintRef = useRef(props.paint);
|
|
416
385
|
paintRef.current = props.paint;
|
|
417
386
|
const layoutRef = useRef(props.layout);
|
|
418
387
|
layoutRef.current = props.layout;
|
|
419
|
-
// Wait for map to be loaded before creating service
|
|
420
|
-
useEffect(() => {
|
|
421
|
-
if (!map) return;
|
|
422
|
-
const mapInstance = map.getMap?.();
|
|
423
|
-
const mi = mapInstance;
|
|
424
|
-
if (!mi || typeof mi.isStyleLoaded !== 'function') {
|
|
425
|
-
return;
|
|
426
|
-
}
|
|
427
|
-
if (mi.isStyleLoaded()) {
|
|
428
|
-
setIsMapLoaded(true);
|
|
429
|
-
return;
|
|
430
|
-
}
|
|
431
|
-
const handleLoad = () => setIsMapLoaded(true);
|
|
432
|
-
mi?.once?.('load', handleLoad);
|
|
433
|
-
return () => {
|
|
434
|
-
mi?.off?.('load', handleLoad);
|
|
435
|
-
};
|
|
436
|
-
}, [map]);
|
|
437
|
-
// Create FeatureService, add layer, and clean up on unmount
|
|
438
388
|
useEffect(() => {
|
|
439
389
|
if (!map || !isMapLoaded) return;
|
|
440
390
|
const mapInstance = map.getMap?.();
|
|
441
391
|
if (!mapInstance) return;
|
|
442
|
-
const
|
|
443
|
-
// Only include defined properties to avoid overriding defaults with undefined
|
|
392
|
+
const layerApi = mapInstance;
|
|
444
393
|
const options = {
|
|
445
394
|
url: props.url
|
|
446
395
|
};
|
|
447
396
|
if (props.where !== undefined) options.where = props.where;
|
|
448
397
|
if (props.outFields !== undefined) options.outFields = props.outFields;
|
|
398
|
+
applyAuthOptions(options, props);
|
|
449
399
|
const service = new FeatureService(sourceId, mapInstance, options);
|
|
450
400
|
serviceRef.current = service;
|
|
451
|
-
|
|
452
|
-
if (typeof mi.getLayer === 'function' && typeof mi.addLayer === 'function' && !mi.getLayer(props.id)) {
|
|
401
|
+
if (typeof layerApi.getLayer === 'function' && typeof layerApi.addLayer === 'function' && !layerApi.getLayer(props.id)) {
|
|
453
402
|
const layerType = props.type || 'fill';
|
|
454
403
|
const defaultPaint = layerType === 'circle' ? {
|
|
455
404
|
'circle-radius': 4,
|
|
@@ -469,87 +418,161 @@ function EsriFeatureLayer(props) {
|
|
|
469
418
|
}
|
|
470
419
|
};
|
|
471
420
|
if (props.beforeId) {
|
|
472
|
-
|
|
421
|
+
layerApi.addLayer(layerConfig, props.beforeId);
|
|
473
422
|
} else {
|
|
474
|
-
|
|
423
|
+
layerApi.addLayer(layerConfig);
|
|
475
424
|
}
|
|
476
425
|
}
|
|
477
426
|
return () => {
|
|
478
|
-
if (
|
|
479
|
-
|
|
427
|
+
if (layerApi.getStyle?.() && layerApi.getLayer?.(props.id)) {
|
|
428
|
+
layerApi.removeLayer?.(props.id);
|
|
480
429
|
}
|
|
481
430
|
service.remove();
|
|
482
431
|
serviceRef.current = null;
|
|
483
432
|
};
|
|
484
|
-
}, [map, isMapLoaded, sourceId, props.url, props.where, props.outFields, props.id, props.beforeId, props.visible, props.type]);
|
|
433
|
+
}, [map, isMapLoaded, sourceId, props.url, props.where, props.outFields, props.id, props.beforeId, props.visible, props.type, props.token, props.apiKey, props.authentication, props.proxy, props.getAttributionFromService, props.requestParams, props.fetchOptions]);
|
|
485
434
|
return null;
|
|
486
435
|
}
|
|
487
436
|
|
|
488
437
|
/**
|
|
489
|
-
*
|
|
438
|
+
* React Map GL component that resolves an ArcGIS portal item id to the matching
|
|
439
|
+
* esri-gl service and adds a renderer-appropriate layer for it.
|
|
490
440
|
*/
|
|
491
|
-
function
|
|
441
|
+
function EsriPortalLayer(props) {
|
|
492
442
|
const {
|
|
493
|
-
current:
|
|
494
|
-
} =
|
|
495
|
-
const
|
|
443
|
+
current: map
|
|
444
|
+
} = useReactMapGL();
|
|
445
|
+
const sourceId = props.sourceId || `esri-portal-${props.id}`;
|
|
446
|
+
const isMapLoaded = useMapLoaded(map);
|
|
447
|
+
const serviceRef = useRef(null);
|
|
448
|
+
useEffect(() => {
|
|
449
|
+
if (!map || !isMapLoaded || !props.itemId) return;
|
|
450
|
+
const mapInstance = map.getMap?.();
|
|
451
|
+
if (!mapInstance) return;
|
|
452
|
+
const layerApi = mapInstance;
|
|
453
|
+
const layerId = props.id;
|
|
454
|
+
let cancelled = false;
|
|
455
|
+
const options = {};
|
|
456
|
+
applyAuthOptions(options, props);
|
|
457
|
+
if (props.layerId !== undefined) options.layerId = props.layerId;
|
|
458
|
+
if (props.portal !== undefined) options.portal = props.portal;
|
|
459
|
+
serviceFromPortalItem(sourceId, mapInstance, props.itemId, options).then(async result => {
|
|
460
|
+
if (cancelled) {
|
|
461
|
+
try {
|
|
462
|
+
result.service.remove();
|
|
463
|
+
} catch {
|
|
464
|
+
/* ignore */
|
|
465
|
+
}
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
serviceRef.current = result.service;
|
|
469
|
+
if (layerApi.getLayer?.(layerId)) {
|
|
470
|
+
props.onResolve?.(result);
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
let layerConfig;
|
|
474
|
+
if (result.kind === 'feature' || result.kind === 'vector-tile') {
|
|
475
|
+
// These services expose a default style (source already set to sourceId).
|
|
476
|
+
const styled = result.service;
|
|
477
|
+
const style = await styled.getStyle();
|
|
478
|
+
if (cancelled || layerApi.getLayer?.(layerId)) return;
|
|
479
|
+
layerConfig = {
|
|
480
|
+
...style,
|
|
481
|
+
id: layerId,
|
|
482
|
+
source: sourceId
|
|
483
|
+
};
|
|
484
|
+
} else {
|
|
485
|
+
// dynamic / tiled / image → raster
|
|
486
|
+
layerConfig = {
|
|
487
|
+
id: layerId,
|
|
488
|
+
type: 'raster',
|
|
489
|
+
source: sourceId
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
if (props.visible === false) {
|
|
493
|
+
layerConfig.layout = {
|
|
494
|
+
...(layerConfig.layout || {}),
|
|
495
|
+
visibility: 'none'
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
layerApi.addLayer?.(layerConfig, props.beforeId);
|
|
499
|
+
props.onResolve?.(result);
|
|
500
|
+
}).catch(err => {
|
|
501
|
+
if (!cancelled) console.warn('EsriPortalLayer: failed to resolve portal item', err);
|
|
502
|
+
});
|
|
503
|
+
return () => {
|
|
504
|
+
cancelled = true;
|
|
505
|
+
try {
|
|
506
|
+
if (layerApi.getLayer?.(layerId)) layerApi.removeLayer?.(layerId);
|
|
507
|
+
} catch {
|
|
508
|
+
/* layer may already be gone */
|
|
509
|
+
}
|
|
510
|
+
if (serviceRef.current) {
|
|
511
|
+
try {
|
|
512
|
+
serviceRef.current.remove();
|
|
513
|
+
} catch {
|
|
514
|
+
/* ignore */
|
|
515
|
+
}
|
|
516
|
+
serviceRef.current = null;
|
|
517
|
+
}
|
|
518
|
+
};
|
|
519
|
+
// Note: onResolve is intentionally excluded from deps to avoid re-resolving
|
|
520
|
+
// when an inline callback identity changes.
|
|
521
|
+
}, [map, isMapLoaded, sourceId, props.id, props.itemId, props.layerId, props.portal, props.beforeId, props.visible, props.token, props.apiKey, props.authentication]);
|
|
522
|
+
return null;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* Build the `useEsri{Mapbox,Maplibre}Layer` hook result from a map collection
|
|
527
|
+
* returned by the corresponding react-map-gl `useMap` hook. Keeps the two
|
|
528
|
+
* flavors of the hook identical except for the map source.
|
|
529
|
+
*/
|
|
530
|
+
function createEsriLayerHooks(collection) {
|
|
531
|
+
// Preserve legacy return shape: `map` is undefined when no ref, the
|
|
532
|
+
// underlying map otherwise. Child hooks type `map: Map | null`, so we
|
|
533
|
+
// coerce for the injection below.
|
|
534
|
+
const map = collection?.current?.getMap?.();
|
|
535
|
+
const injectedMap = map ?? null;
|
|
496
536
|
return {
|
|
497
537
|
map,
|
|
498
538
|
useDynamicMapService: options => useDynamicMapService({
|
|
499
539
|
...options,
|
|
500
|
-
map
|
|
540
|
+
map: injectedMap
|
|
501
541
|
}),
|
|
502
542
|
useTiledMapService: options => useTiledMapService({
|
|
503
543
|
...options,
|
|
504
|
-
map
|
|
544
|
+
map: injectedMap
|
|
505
545
|
}),
|
|
506
546
|
useImageService: options => useImageService({
|
|
507
547
|
...options,
|
|
508
|
-
map
|
|
548
|
+
map: injectedMap
|
|
509
549
|
}),
|
|
510
550
|
useVectorTileService: options => useVectorTileService({
|
|
511
551
|
...options,
|
|
512
|
-
map
|
|
552
|
+
map: injectedMap
|
|
513
553
|
}),
|
|
514
554
|
useFeatureService: options => useFeatureService({
|
|
515
555
|
...options,
|
|
516
|
-
map
|
|
556
|
+
map: injectedMap
|
|
517
557
|
})
|
|
518
558
|
};
|
|
519
559
|
}
|
|
520
560
|
|
|
561
|
+
/**
|
|
562
|
+
* Hook for using Esri services with Mapbox GL JS (via react-map-gl)
|
|
563
|
+
*/
|
|
564
|
+
function useEsriMapboxLayer() {
|
|
565
|
+
const collection = useMap();
|
|
566
|
+
return createEsriLayerHooks(collection);
|
|
567
|
+
}
|
|
568
|
+
|
|
521
569
|
/**
|
|
522
570
|
* Hook for using Esri services with MapLibre GL JS (via react-map-gl/maplibre)
|
|
523
571
|
*/
|
|
524
572
|
function useEsriMaplibreLayer() {
|
|
525
|
-
const
|
|
526
|
-
|
|
527
|
-
} = useMap$1();
|
|
528
|
-
const map = mapRef?.getMap();
|
|
529
|
-
return {
|
|
530
|
-
map,
|
|
531
|
-
useDynamicMapService: options => useDynamicMapService({
|
|
532
|
-
...options,
|
|
533
|
-
map
|
|
534
|
-
}),
|
|
535
|
-
useTiledMapService: options => useTiledMapService({
|
|
536
|
-
...options,
|
|
537
|
-
map
|
|
538
|
-
}),
|
|
539
|
-
useImageService: options => useImageService({
|
|
540
|
-
...options,
|
|
541
|
-
map
|
|
542
|
-
}),
|
|
543
|
-
useVectorTileService: options => useVectorTileService({
|
|
544
|
-
...options,
|
|
545
|
-
map
|
|
546
|
-
}),
|
|
547
|
-
useFeatureService: options => useFeatureService({
|
|
548
|
-
...options,
|
|
549
|
-
map
|
|
550
|
-
})
|
|
551
|
-
};
|
|
573
|
+
const collection = useMap$1();
|
|
574
|
+
return createEsriLayerHooks(collection);
|
|
552
575
|
}
|
|
553
576
|
|
|
554
|
-
export { DynamicMapService, EsriDynamicLayer, EsriFeatureLayer, EsriImageLayer, EsriTiledLayer, EsriVectorBasemapLayer, EsriVectorTileLayer, FeatureService, ImageService, TiledMapService, VectorBasemapStyle, VectorTileService, useEsriMapboxLayer, useEsriMaplibreLayer };
|
|
577
|
+
export { DynamicMapService, EsriDynamicLayer, EsriFeatureLayer, EsriImageLayer, EsriPortalLayer, EsriTiledLayer, EsriVectorBasemapLayer, EsriVectorTileLayer, FeatureService, ImageService, TiledMapService, VectorBasemapStyle, VectorTileService, esriRequest, serviceFromPortalItem, useEsriMapboxLayer, useEsriMaplibreLayer };
|
|
555
578
|
//# sourceMappingURL=react-map-gl.js.map
|