esri-gl 1.0.1 โ†’ 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,805 +1,216 @@
1
1
  # esri-gl
2
2
 
3
- A TypeScript library that bridges Esri ArcGIS REST services with MapLibre GL JS and Mapbox GL JS. It replicates Esri Leaflet's architecture patterns while being compatible with modern WebGL mapping libraries.
3
+ A TypeScript library that bridges Esri ArcGIS REST services with MapLibre GL JS and Mapbox GL JS.
4
4
 
5
5
  [![npm version](https://badge.fury.io/js/esri-gl.svg)](https://badge.fury.io/js/esri-gl)
6
+ [![CI](https://github.com/muimsd/esri-gl/actions/workflows/ci.yml/badge.svg)](https://github.com/muimsd/esri-gl/actions/workflows/ci.yml)
6
7
  [![TypeScript](https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg)](http://www.typescriptlang.org/)
7
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
9
 
9
- ## ๐Ÿ”— Links
10
+ **[Documentation](https://esri-gl.pages.dev/)** ยท **[Live Demos](https://esri-gl-demo.pages.dev/)** ยท **[npm](https://www.npmjs.com/package/esri-gl)**
10
11
 
11
- - **๐Ÿ“š [Documentation](https://esri-gl.netlify.app/)** - Complete API reference and guides
12
- - **๐ŸŽฎ [Live Demos](https://esri-gl-demo.netlify.app/)** - Interactive examples and code samples
12
+ ## Features
13
13
 
14
- **Note**: This library is compatible with both **MapLibre GL JS** and **Mapbox GL JS**.
14
+ ### Services
15
+ - **DynamicMapService** โ€” Server-rendered raster tiles with dynamic styling, filtering, labeling, and identify
16
+ - **TiledMapService** โ€” Pre-cached tiles for fast basemaps
17
+ - **ImageService** โ€” Analytical raster data with rendering rules and temporal support
18
+ - **FeatureService** โ€” Vector data with smart vector tile detection, GeoJSON fallback, editing, and attachments
19
+ - **VectorTileService** โ€” Client-rendered vector tiles
20
+ - **VectorBasemapStyle** โ€” Esri's professional basemap styles
15
21
 
16
- ## Features
22
+ ### Tasks
23
+ - **IdentifyFeatures / IdentifyImage** โ€” Query features and pixel values at map locations
24
+ - **Query** โ€” Spatial and attribute queries with automatic pagination
25
+ - **Find** โ€” Text-based feature search across layers
17
26
 
18
- ### Supported Services
19
-
20
- - **Dynamic Map Services** - Server-rendered raster tiles from ArcGIS Map Services
21
- - **Tiled Map Services** - Pre-cached tile services for optimal performance
22
- - **Image Services** - Analytical raster data with server-side processing
23
- - **Vector Tile Services** - Client-rendered vector tiles for fast styling
24
- - **Vector Basemap Styles** - Esri's vector basemap styles with custom styling
25
- - **Feature Services** - Vector data with smart vector tile detection and GeoJSON fallback
26
-
27
- ### Advanced Features
28
-
29
- - **Smart Vector Tile Detection** - Automatically detects vector tile endpoints with GeoJSON fallback
30
- - **Dynamic Layer Management** - Add/remove layers dynamically with proper cleanup
31
- - **Identify Tasks** - Query features and raster data at specific locations
32
- - **Bounding Box Filtering** - Optimize performance with viewport-based data loading
33
- - **Layer Definitions** - Filter data server-side using SQL-like expressions
34
- - **Time-aware Services** - Support for temporal data visualization
35
- - **Attribution Management** - Automatic service attribution handling
36
- - **React Integration** - Hooks and components for React applications
37
- - **React Map GL Support** - Direct integration with react-map-gl
38
- - **TypeScript Support** - Full type safety with comprehensive interfaces
39
- - **ArcGIS Online (AGOL) Support** - Proper JSON error handling, token auth, and API key support
40
- - **Feature Editing** - Add, update, delete features and apply batch edits
41
- - **Attachments** - Query, add, and delete feature attachments
42
- - **Query Pagination** - Automatic pagination through large result sets
27
+ ### Integrations
28
+ - **React Hooks** โ€” `useDynamicMapService`, `useFeatureService`, `useQuery`, etc.
29
+ - **react-map-gl** โ€” Declarative `<EsriDynamicLayer>`, `<EsriFeatureLayer>`, etc.
30
+ - **TypeScript** โ€” Full type safety with comprehensive interfaces
43
31
 
44
32
  ## Installation
45
33
 
46
- ### Stable Release
47
34
  ```bash
48
35
  npm install esri-gl
49
36
  ```
50
37
 
51
38
  ### Entry Points
52
39
 
53
- esri-gl provides three entry points for different use cases:
54
-
55
- | Entry Point | Import | Use Case |
56
- |-------------|--------|----------|
57
- | `esri-gl` | `import { DynamicMapService, Query, ... } from 'esri-gl'` | Core services and tasks (vanilla JS/TS) |
58
- | `esri-gl/react` | `import { useDynamicMapService, useQuery, ... } from 'esri-gl/react'` | React hooks for service lifecycle management |
59
- | `esri-gl/react-map-gl` | `import { EsriDynamicLayer, ... } from 'esri-gl/react-map-gl'` | Declarative react-map-gl components |
40
+ | Import | Use Case |
41
+ |--------|----------|
42
+ | `esri-gl` | Core services and tasks |
43
+ | `esri-gl/react` | React hooks |
44
+ | `esri-gl/react-map-gl` | react-map-gl components |
60
45
 
61
- ## Quick Start
62
-
63
- ### Basic Usage with MapLibre GL JS
46
+ ### CDN (UMD)
64
47
 
65
- ```typescript
66
- import { Map } from 'maplibre-gl';
67
- import { DynamicMapService, VectorTileService, FeatureService } from 'esri-gl';
68
-
69
- const map = new Map({
70
- container: 'map',
71
- style: 'https://demotiles.maplibre.org/style.json',
72
- center: [-95, 37],
73
- zoom: 4
74
- });
75
-
76
- map.on('load', () => {
77
- // Add a dynamic map service
78
- const dynamicService = new DynamicMapService('census-source', map, {
79
- url: 'https://sampleserver6.arcgisonline.com/arcgis/rest/services/Census/MapServer'
80
- });
81
-
82
- map.addLayer({
83
- id: 'census-layer',
84
- type: 'raster',
85
- source: 'census-source'
86
- });
87
-
88
- // Add a feature service with smart vector tile detection
89
- const featureService = new FeatureService('parcels-source', map, {
90
- url: 'https://services.arcgis.com/.../FeatureServer/0',
91
- useVectorTiles: true, // Automatically detects and falls back to GeoJSON if needed
92
- useBoundingBox: true, // Optimize with viewport filtering
93
- where: "STATUS = 'Active'", // Server-side filtering
94
- outFields: '*'
95
- });
96
-
97
- map.addLayer({
98
- id: 'parcels-layer',
99
- type: 'fill',
100
- source: 'parcels-source',
101
- paint: {
102
- 'fill-color': '#007cbf',
103
- 'fill-opacity': 0.5
104
- }
105
- });
106
- });
48
+ ```html
49
+ <script src="https://unpkg.com/esri-gl@latest/dist/index.umd.js"></script>
50
+ <script>
51
+ const service = new esrigl.DynamicMapService('source', map, { url: '...' });
52
+ </script>
107
53
  ```
108
54
 
109
- ### Using with Mapbox GL JS
55
+ ## Quick Start
110
56
 
111
57
  ```typescript
112
- import mapboxgl from 'mapbox-gl';
113
- import { VectorBasemapStyle, ImageService } from 'esri-gl';
114
-
115
- mapboxgl.accessToken = 'your-mapbox-token';
58
+ import { DynamicMapService } from 'esri-gl';
116
59
 
117
- const map = new mapboxgl.Map({
60
+ const map = new maplibregl.Map({
118
61
  container: 'map',
62
+ style: 'https://demotiles.maplibre.org/style.json',
119
63
  center: [-95, 37],
120
64
  zoom: 4
121
65
  });
122
66
 
123
67
  map.on('load', () => {
124
- // Add Esri vector basemap style
125
- const basemapService = new VectorBasemapStyle('basemap-source', map, {
126
- style: 'arcgis/streets'
127
- });
128
-
129
- // Add image service for analytical raster data
130
- const imageService = new ImageService('elevation-source', map, {
131
- url: 'https://sampleserver6.arcgisonline.com/arcgis/rest/services/Elevation/ImageServer',
132
- renderingRule: {
133
- rasterFunction: 'Hillshade'
134
- }
135
- });
136
-
137
- map.addLayer({
138
- id: 'elevation-layer',
139
- type: 'raster',
140
- source: 'elevation-source'
68
+ const service = new DynamicMapService('usa-source', map, {
69
+ url: 'https://sampleserver6.arcgisonline.com/arcgis/rest/services/USA/MapServer',
70
+ layers: [0, 1, 2],
71
+ transparent: true
141
72
  });
142
- });
143
- ```
144
-
145
- ## Service Classes
146
-
147
- ### DynamicMapService
148
- Server-rendered raster tiles from ArcGIS Map Services.
149
73
 
150
- ```typescript
151
- const service = new DynamicMapService('source-id', map, {
152
- url: 'https://example.com/arcgis/rest/services/MyService/MapServer',
153
- layers: [0, 1, 2], // Specific layers to display
154
- layerDefs: {
155
- 0: "POP2000 > 100000", // Filter layer 0
156
- 1: "STATE_NAME = 'California'" // Filter layer 1
157
- },
158
- transparent: true,
159
- format: 'png32'
74
+ map.addLayer({ id: 'usa-layer', type: 'raster', source: 'usa-source' });
160
75
  });
161
76
  ```
162
77
 
163
- ### TiledMapService
164
- Pre-cached tile services for optimal performance.
165
-
166
- ```typescript
167
- const service = new TiledMapService('source-id', map, {
168
- url: 'https://example.com/arcgis/rest/services/MyTiledService/MapServer'
169
- });
170
- ```
78
+ ## Service Examples
171
79
 
172
80
  ### FeatureService
173
- Efficient tile-based feature loading from ArcGIS FeatureServers with PBF (Protocol Buffer) support for minimal payload size.
174
-
175
- ```typescript
176
- const service = new FeatureService('source-id', map, {
177
- url: 'https://example.com/arcgis/rest/services/MyService/FeatureServer/0',
178
- where: '1=1',
179
- outFields: '*',
180
- // PBF options (requires ArcGIS Server 10.7+)
181
- minZoom: 2, // Minimum zoom level for data requests
182
- simplifyFactor: 0.3, // Geometry simplification (0-1)
183
- precision: 8, // Coordinate decimal precision
184
- useServiceBounds: true, // Limit requests to service extent
185
- setAttributionFromService: true // Auto-set attribution
186
- });
187
-
188
- // Update filters dynamically
189
- service.setWhere("STATUS = 'Active'");
190
- service.setDate(new Date(), new Date('2024-01-01'));
191
- service.setToken('your-token');
192
-
193
- // Query features by location
194
- const features = await service.getFeaturesByLonLat({ lng: -118, lat: 34 }, 100);
195
-
196
- // Query by object IDs
197
- const specific = await service.getFeaturesByObjectIds([1, 2, 3], true);
198
- ```
199
-
200
- #### Feature Editing (AGOL)
201
81
 
202
82
  ```typescript
203
- // Edit features on an AGOL Feature Service
204
- const service = new FeatureService('editable-source', map, {
83
+ const service = new FeatureService('features-source', map, {
205
84
  url: 'https://services.arcgis.com/.../FeatureServer/0',
206
- token: 'your-agol-token'
85
+ where: "STATUS = 'Active'",
86
+ outFields: 'NAME,STATUS',
87
+ token: 'your-token' // optional
207
88
  });
208
89
 
209
- // Add features
210
- const addResults = await service.addFeatures([
211
- { type: 'Feature', geometry: { type: 'Point', coordinates: [-95, 37] }, properties: { name: 'New Point' } }
212
- ]);
213
-
214
- // Update features
215
- const updateResults = await service.updateFeatures([
216
- { type: 'Feature', geometry: { type: 'Point', coordinates: [-95, 37] }, properties: { OBJECTID: 1, name: 'Updated' } }
217
- ]);
218
-
219
- // Delete features
220
- const deleteResults = await service.deleteFeatures({ objectIds: [1, 2, 3] });
221
-
222
- // Batch edits
223
- const batchResults = await service.applyEdits({
224
- adds: [newFeature],
225
- updates: [updatedFeature],
226
- deletes: [4, 5]
90
+ map.addLayer({
91
+ id: 'features-layer',
92
+ type: 'circle',
93
+ source: 'features-source',
94
+ paint: { 'circle-radius': 5, 'circle-color': '#007cbf' }
227
95
  });
228
- ```
229
-
230
- ### VectorTileService
231
- Client-rendered vector tiles for fast styling and interaction.
232
96
 
233
- ```typescript
234
- const service = new VectorTileService('source-id', map, {
235
- url: 'https://example.com/arcgis/rest/services/MyService/VectorTileServer'
236
- });
97
+ // Edit features
98
+ await service.addFeatures([feature]);
99
+ await service.applyEdits({ adds: [], updates: [], deletes: [1, 2] });
237
100
  ```
238
101
 
239
102
  ### ImageService
240
- Analytical raster data with server-side processing capabilities.
241
103
 
242
104
  ```typescript
243
- const service = new ImageService('source-id', map, {
244
- url: 'https://example.com/arcgis/rest/services/MyImageService/ImageServer',
245
- renderingRule: {
246
- rasterFunction: 'Stretch',
247
- rasterFunctionArguments: {
248
- StretchType: 6,
249
- NumberOfStandardDeviations: 2
250
- }
251
- },
252
- mosaicRule: {
253
- mosaicMethod: 'esriMosaicLockRaster',
254
- lockRasterIds: [1, 2, 3]
255
- }
105
+ const service = new ImageService('landsat-source', map, {
106
+ url: 'https://landsat2.arcgis.com/arcgis/rest/services/Landsat/MS/ImageServer',
107
+ renderingRule: { rasterFunction: 'Natural Color' }
256
108
  });
257
109
  ```
258
110
 
259
111
  ### VectorBasemapStyle
260
- Esri's vector basemap styles with customization options.
261
-
262
- ```typescript
263
- const service = new VectorBasemapStyle('source-id', map, {
264
- style: 'arcgis/streets', // or 'arcgis/navigation', 'arcgis/topographic', etc.
265
- language: 'en',
266
- worldview: 'USA'
267
- });
268
- ```
269
112
 
270
113
  ```typescript
271
- // Custom portal item style
272
- const customBasemap = new VectorBasemapStyle('source-id', map, {
273
- style: 'arcgis/streets',
274
- itemId: 'your-portal-item-id',
275
- token: 'your-token'
276
- });
114
+ VectorBasemapStyle.applyStyle(map, 'arcgis/streets', { apiKey: 'YOUR_KEY' });
277
115
  ```
278
116
 
279
- ## Task-Based Operations
280
-
281
- Modeled after Esri Leaflet's chainable task pattern for querying and identifying features.
282
-
283
- ### IdentifyFeatures
284
- Query vector features at specific locations.
117
+ ## Tasks
285
118
 
286
119
  ```typescript
287
- import { IdentifyFeatures } from 'esri-gl';
120
+ import { IdentifyFeatures, query } from 'esri-gl';
288
121
 
289
- // Identify features at a point using the fluent API
290
- const results = await new IdentifyFeatures('https://example.com/arcgis/rest/services/MyService/MapServer')
122
+ // Identify features at a point
123
+ const results = await new IdentifyFeatures('https://.../MapServer')
291
124
  .at({ lng: -95, lat: 37 })
292
125
  .on(map)
293
126
  .layers('all')
294
127
  .tolerance(5)
295
- .returnGeometry(true)
296
128
  .run();
297
129
 
298
- console.log('Identified features:', results.features);
299
- ```
300
-
301
- ### IdentifyImage
302
- Query raster values from image services.
303
-
304
- ```typescript
305
- import { IdentifyImage } from 'esri-gl';
306
-
307
- const results = await new IdentifyImage('https://example.com/arcgis/rest/services/Elevation/ImageServer')
308
- .at({ lng: -120, lat: 40 })
309
- .run();
310
-
311
- console.log('Pixel value:', results.results[0]?.value);
312
- ```
313
-
314
- ### Query
315
- Advanced feature querying with spatial and attribute filters.
316
-
317
- ```typescript
318
- import { query } from 'esri-gl';
319
-
320
- const results = await query({
321
- url: 'https://example.com/arcgis/rest/services/MyService/FeatureServer/0'
322
- })
323
- .where("STATE_NAME = 'California'")
324
- .intersects({
325
- type: 'Point',
326
- coordinates: [-118, 34]
327
- })
328
- .run();
329
- ```
330
-
331
- ```typescript
332
- // Automatic pagination through all results
333
- import { query } from 'esri-gl';
334
-
335
- const allResults = await query({
336
- url: 'https://example.com/arcgis/rest/services/MyService/FeatureServer/0'
337
- })
338
- .where("STATE_NAME = 'California'")
339
- .runAll(); // Automatically paginates through all pages
130
+ // Query with pagination
131
+ const all = await query({ url: 'https://.../FeatureServer/0' })
132
+ .where("STATE_NAME = 'California'")
133
+ .runAll();
340
134
  ```
341
135
 
342
136
  ## React Integration
343
137
 
344
- esri-gl provides first-class React.js support with comprehensive hooks, components, and seamless integration with **react-map-gl**. Whether you're building with vanilla React + MapLibre/Mapbox or using the react-map-gl wrapper, esri-gl has you covered.
345
-
346
- ### Installation for React Projects
347
-
348
- ```bash
349
- # Core library
350
- npm install esri-gl
351
-
352
- # For React hooks and components
353
- npm install react react-dom @types/react @types/react-dom
354
-
355
- # For react-map-gl integration (recommended)
356
- npm install react-map-gl mapbox-gl
357
- # OR for MapLibre
358
- npm install react-map-gl maplibre-gl
359
- ```
360
-
361
- ### React Hooks Pattern
362
-
363
- Perfect for custom React components with full control over map lifecycle:
138
+ ### Hooks
364
139
 
365
140
  ```typescript
366
- import React, { useState, useRef, useEffect } from 'react';
367
- import { Map } from 'maplibre-gl';
368
- import { useDynamicMapService, useIdentifyFeatures, useFeatureService } from 'esri-gl/react';
369
-
370
- function CustomMapComponent() {
371
- const mapRef = useRef<HTMLDivElement>(null);
372
- const [map, setMap] = useState<Map | null>(null);
373
-
374
- // Initialize MapLibre map
375
- useEffect(() => {
376
- if (!mapRef.current) return;
377
- const mapInstance = new Map({
378
- container: mapRef.current,
379
- style: 'https://demotiles.maplibre.org/style.json',
380
- center: [-95, 37],
381
- zoom: 4
382
- });
383
- setMap(mapInstance);
384
- return () => mapInstance.remove();
385
- }, []);
386
-
387
- // Use esri-gl React hooks
388
- const { service: dynamicService, loading, error } = useDynamicMapService({
389
- sourceId: 'usa-service',
390
- map,
391
- options: {
392
- url: 'https://sampleserver6.arcgisonline.com/arcgis/rest/services/USA/MapServer',
393
- layers: [0, 1, 2],
394
- transparent: true
395
- }
396
- });
397
-
398
- const { service: featureService } = useFeatureService({
399
- sourceId: 'states-service',
400
- map,
401
- options: {
402
- url: 'https://services.arcgis.com/P3ePLMYs2RVChkJx/arcgis/rest/services/USA_Census_States/FeatureServer/0',
403
- useVectorTiles: true,
404
- useBoundingBox: true
405
- }
406
- });
141
+ import { useDynamicMapService, useIdentifyFeatures } from 'esri-gl/react';
407
142
 
408
- const { identify } = useIdentifyFeatures({
143
+ const { service, loading, error } = useDynamicMapService({
144
+ sourceId: 'usa-service',
145
+ map,
146
+ options: {
409
147
  url: 'https://sampleserver6.arcgisonline.com/arcgis/rest/services/USA/MapServer',
410
- tolerance: 3
411
- });
412
-
413
- // Handle map clicks for identify
414
- useEffect(() => {
415
- if (!map) return;
416
- const handleClick = async (e: any) => {
417
- const results = await identify({ lng: e.lngLat.lng, lat: e.lngLat.lat }, map);
418
- console.log('Identify results:', results);
419
- };
420
- map.on('click', handleClick);
421
- return () => map.off('click', handleClick);
422
- }, [map, identify]);
423
-
424
- return (
425
- <div>
426
- <div ref={mapRef} style={{ width: '100%', height: '500px' }} />
427
- {loading && <div>Loading Esri services...</div>}
428
- {error && <div>Error: {error.message}</div>}
429
- </div>
430
- );
431
- }
432
- ```
433
-
434
- ### React Map GL Components (Recommended)
435
-
436
- For the smoothest React experience with declarative layer management:
437
-
438
- ```typescript
439
- import React, { useState } from 'react';
440
- import { Map } from 'react-map-gl/mapbox';
441
- import {
442
- EsriDynamicLayer,
443
- EsriFeatureLayer,
444
- EsriVectorTileLayer,
445
- EsriImageLayer
446
- } from 'esri-gl/react-map-gl';
447
-
448
- function MapWithEsriLayers() {
449
- const [viewState, setViewState] = useState({
450
- longitude: -95,
451
- latitude: 37,
452
- zoom: 4
453
- });
454
-
455
- const [selectedStates, setSelectedStates] = useState<string[]>([]);
456
-
457
- return (
458
- <Map
459
- {...viewState}
460
- onMove={evt => setViewState(evt.viewState)}
461
- mapStyle="mapbox://styles/mapbox/streets-v11"
462
- style={{ width: '100%', height: '600px' }}
463
- >
464
- {/* Dynamic Map Service Layer */}
465
- <EsriDynamicLayer
466
- id="usa-demographics"
467
- url="https://sampleserver6.arcgisonline.com/arcgis/rest/services/Census/MapServer"
468
- layers={[0, 1, 2]}
469
- layerDefs={{
470
- 0: "POP2000 > 100000",
471
- 1: "STATE_NAME IN ('California', 'Texas', 'New York')"
472
- }}
473
- opacity={0.8}
474
- beforeId="waterway-label"
475
- />
476
-
477
- {/* Feature Service with Vector Tiles */}
478
- <EsriFeatureLayer
479
- id="us-states"
480
- url="https://services.arcgis.com/P3ePLMYs2RVChkJx/arcgis/rest/services/USA_Census_States/FeatureServer/0"
481
- useVectorTiles={true}
482
- useBoundingBox={true}
483
- where={selectedStates.length > 0 ? `STATE_NAME IN ('${selectedStates.join("','")}')` : undefined}
484
- paint={{
485
- 'fill-color': [
486
- 'case',
487
- ['in', ['get', 'STATE_NAME'], ['literal', selectedStates]],
488
- '#ff6b6b',
489
- '#627BC1'
490
- ],
491
- 'fill-opacity': 0.6,
492
- 'fill-outline-color': '#ffffff'
493
- }}
494
- onClick={(feature) => {
495
- const stateName = feature.properties?.STATE_NAME;
496
- if (stateName) {
497
- setSelectedStates(prev =>
498
- prev.includes(stateName)
499
- ? prev.filter(s => s !== stateName)
500
- : [...prev, stateName]
501
- );
502
- }
503
- }}
504
- />
505
-
506
- {/* Vector Tile Service */}
507
- <EsriVectorTileLayer
508
- id="world-imagery-labels"
509
- url="https://services.arcgisonline.com/arcgis/rest/services/World_Imagery/VectorTileServer"
510
- beforeId="usa-demographics"
511
- />
512
-
513
- {/* Image Service for Analytical Data */}
514
- <EsriImageLayer
515
- id="elevation-hillshade"
516
- url="https://sampleserver6.arcgisonline.com/arcgis/rest/services/Elevation/ImageServer"
517
- renderingRule={{
518
- rasterFunction: "Hillshade",
519
- rasterFunctionArguments: {
520
- Azimuth: 315,
521
- Altitude: 45
522
- }
523
- }}
524
- opacity={0.5}
525
- />
526
- </Map>
527
- );
528
- }
529
- ```
530
-
531
- ### Why Choose React Integration?
532
-
533
- #### React Hooks Benefits
534
- - **๐ŸŽ›๏ธ Full Control** - Direct access to map instance and service lifecycle
535
- - **๐Ÿ”„ State Management** - Seamless integration with React state and effects
536
- - **๐ŸŽฏ Custom Logic** - Perfect for complex interactions and custom components
537
- - **๐Ÿ“ฆ Lightweight** - Use only what you need
538
-
539
- #### React Map GL Benefits
540
- - **๐Ÿ“‹ Declarative** - Define layers as JSX components
541
- - **๐Ÿ”„ Automatic Updates** - Props changes automatically update layers
542
- - **๐ŸŽจ Built-in Styling** - Direct paint and layout prop support
543
- - **๐Ÿ‘† Event Handling** - onClick, onHover events built-in
544
- - **๐Ÿ—๏ธ Component Ecosystem** - Works with all react-map-gl features
545
-
546
- ### TypeScript Support
547
-
548
- Full TypeScript support with comprehensive type definitions:
549
-
550
- ```typescript
551
- import type {
552
- DynamicMapServiceOptions,
553
- FeatureServiceOptions,
554
- IdentifyResult,
555
- EsriLayerProps
556
- } from 'esri-gl';
557
- import type { MapRef } from 'react-map-gl/mapbox';
558
-
559
- // Fully typed component props
560
- interface MapComponentProps {
561
- serviceUrl: string;
562
- initialLayers?: number[];
563
- onFeatureClick?: (feature: IdentifyResult) => void;
564
- }
565
-
566
- const TypedMapComponent: React.FC<MapComponentProps> = ({
567
- serviceUrl,
568
- initialLayers = [0],
569
- onFeatureClick
570
- }) => {
571
- // Component implementation with full type safety
572
- };
573
- ```
574
-
575
- See [REACT.md](REACT.md) for complete React integration documentation and advanced patterns.
576
-
577
- ## Advanced Features
578
-
579
- ### Dynamic Layer Management
580
-
581
- ```typescript
582
- // Add layers dynamically
583
- const service = new DynamicMapService('census-source', map, {
584
- url: 'https://example.com/MapServer'
585
- });
586
-
587
- // Update visible layers
588
- service.setLayers([0, 2, 5]);
589
-
590
- // Update layer definitions
591
- service.setLayerDefs({
592
- 0: "POP2000 > 50000",
593
- 2: "STATE_NAME IN ('California', 'Nevada')"
594
- });
595
-
596
- // Clean up
597
- service.remove();
598
- ```
599
-
600
- ### Smart Vector Tile Detection
601
-
602
- The `FeatureService` automatically detects vector tile availability:
603
-
604
- ```typescript
605
- const service = new FeatureService('smart-source', map, {
606
- url: 'https://example.com/FeatureServer/0',
607
- useVectorTiles: true // Will automatically:
608
- // 1. Check for VectorTileServer endpoint
609
- // 2. Test various URL patterns
610
- // 3. Fall back to GeoJSON if vector tiles unavailable
611
- // 4. Log the decision process to console
612
- });
613
- ```
614
-
615
- ### Performance Optimization
616
-
617
- ```typescript
618
- const service = new FeatureService('optimized-source', map, {
619
- url: 'https://example.com/FeatureServer/0',
620
- useBoundingBox: true, // Only load features in current viewport
621
- maxRecordCount: 1000, // Limit records per request
622
- where: 'STATUS = "Active"', // Server-side filtering
623
- outFields: ['OBJECTID', 'NAME', 'STATUS'] // Limit fields
148
+ layers: [0, 1, 2]
149
+ }
624
150
  });
625
151
  ```
626
152
 
627
- ### Authentication
628
-
629
- ```typescript
630
- // Token-based authentication (URL parameter)
631
- const service = new DynamicMapService('secure-source', map, {
632
- url: 'https://example.com/arcgis/rest/services/SecureService/MapServer',
633
- token: 'your-auth-token'
634
- });
153
+ ### react-map-gl Components
635
154
 
636
- // Update token dynamically
637
- service.setToken('new-token');
155
+ ```tsx
156
+ import { EsriDynamicLayer, EsriFeatureLayer } from 'esri-gl/react-map-gl';
638
157
 
639
- // API Key authentication (X-Esri-Authorization header)
640
- const featureService = new FeatureService('api-key-source', map, {
641
- url: 'https://services.arcgis.com/.../FeatureServer/0',
642
- apiKey: 'your-api-key' // Sent as X-Esri-Authorization: Bearer header
643
- });
158
+ <Map mapStyle="mapbox://styles/mapbox/streets-v11">
159
+ <EsriDynamicLayer
160
+ id="census"
161
+ url="https://.../Census/MapServer"
162
+ layers={[0, 1]}
163
+ opacity={0.8}
164
+ />
165
+ <EsriFeatureLayer
166
+ id="states"
167
+ url="https://.../FeatureServer/0"
168
+ paint={{ 'fill-color': '#627BC1', 'fill-opacity': 0.6 }}
169
+ />
170
+ </Map>
644
171
  ```
645
172
 
646
173
  ## Examples
647
174
 
648
- Working examples are available in the [`examples/`](examples/) directory:
649
-
650
- | Example | Entry Point | Description |
651
- |---------|-------------|-------------|
652
- | [maplibre-esm](examples/maplibre-esm/) | `esri-gl` | All services and tasks with vanilla MapLibre GL JS |
653
- | [maplibre-react-hooks](examples/maplibre-react-hooks/) | `esri-gl/react` | All React hooks with MapLibre GL JS |
654
- | [maplibre-react-map-gl](examples/maplibre-react-map-gl/) | `esri-gl/react-map-gl` | All react-map-gl components and tasks |
175
+ Working examples in [`examples/`](examples/):
655
176
 
656
- To run an example:
177
+ | Example | Description |
178
+ |---------|-------------|
179
+ | [maplibre-esm](examples/maplibre-esm/) | All services and tasks with vanilla MapLibre |
180
+ | [maplibre-react-hooks](examples/maplibre-react-hooks/) | React hooks with MapLibre |
181
+ | [maplibre-react-map-gl](examples/maplibre-react-map-gl/) | react-map-gl components |
657
182
 
658
183
  ```bash
659
- cd examples/maplibre-esm # or maplibre-react-hooks, maplibre-react-map-gl
660
- npm install
661
- npm run dev
184
+ cd examples/maplibre-esm && npm install && npm run dev
662
185
  ```
663
186
 
664
187
  ## Development
665
188
 
666
- ### Build System
667
- - **Library Build**: Rollup with TypeScript, Babel, and Terser (UMD + ESM outputs)
668
- - **Type Declarations**: Generated with rollup-plugin-dts in `dist/` directory
669
- - **Demo Development**: Vite dev server with React and TypeScript
670
- - **Documentation**: Docusaurus build system
671
- - **Test Coverage**: 727 tests across 31 test suites
672
-
673
- ### Development Commands
674
-
675
189
  ```bash
676
- # Start demo development server
677
- npm run dev
678
-
679
- # Build library (both UMD and ESM)
680
- npm run build
681
-
682
- # Watch mode for library development
683
- npm run build:watch
684
-
685
- # Type checking
686
- npm run type-check
687
-
688
- # Linting and formatting
689
- npm run lint
690
- npm run format
691
-
692
- # Run tests
693
- npm run test
694
- npm run test:watch
695
- npm run test:coverage
696
-
697
- # Build documentation
698
- npm run build-docs
699
- npm run dev:docs
700
- ```
701
-
702
- ### Project Structure
703
-
704
- ```
705
- src/
706
- โ”œโ”€โ”€ Services/ # Core service classes
707
- โ”‚ โ”œโ”€โ”€ DynamicMapService.ts
708
- โ”‚ โ”œโ”€โ”€ FeatureService.ts
709
- โ”‚ โ”œโ”€โ”€ ImageService.ts
710
- โ”‚ โ”œโ”€โ”€ TiledMapService.ts
711
- โ”‚ โ”œโ”€โ”€ VectorTileService.ts
712
- โ”‚ โ””โ”€โ”€ VectorBasemapStyle.ts
713
- โ”œโ”€โ”€ Tasks/ # Task-based operations
714
- โ”‚ โ”œโ”€โ”€ Find.ts
715
- โ”‚ โ”œโ”€โ”€ IdentifyFeatures.ts
716
- โ”‚ โ”œโ”€โ”€ IdentifyImage.ts
717
- โ”‚ โ””โ”€โ”€ Query.ts
718
- โ”œโ”€โ”€ demo/ # Demo React components
719
- โ”œโ”€โ”€ tests/ # Comprehensive test suite (92.94% coverage)
720
- โ””โ”€โ”€ types.ts # TypeScript interfaces
721
-
722
- dist/
723
- โ”œโ”€โ”€ index.d.ts # Main TypeScript declarations
724
- โ”œโ”€โ”€ react.d.ts # React hooks declarations
725
- โ”œโ”€โ”€ react-map-gl.d.ts # React Map GL declarations
726
- โ”œโ”€โ”€ index.js # ESM build
727
- โ”œโ”€โ”€ index.umd.js # UMD build
728
- โ”œโ”€โ”€ react.js # React hooks ESM build
729
- โ”œโ”€โ”€ react-map-gl.js # React Map GL ESM build
730
- โ””โ”€โ”€ package.json # Package metadata with exports map
731
- ```
732
-
733
- ## Browser Support
734
-
735
- - **Modern Browsers**: Chrome, Firefox, Safari, Edge (ES2018+)
736
- - **MapLibre GL JS**: v2.0+ (recommended), v1.15+
737
- - **Mapbox GL JS**: v2.0+ (recommended), v1.13+
738
-
739
- ## TypeScript Support
740
-
741
- esri-gl is written in TypeScript and provides full type definitions consolidated in a single directory:
742
-
743
- ```typescript
744
- import type {
745
- DynamicMapServiceOptions,
746
- FeatureServiceOptions,
747
- IdentifyResult,
748
- EsriGeoJSONFeatureCollection
749
- } from 'esri-gl';
750
-
751
- const options: FeatureServiceOptions = {
752
- url: 'https://example.com/FeatureServer/0',
753
- useVectorTiles: true,
754
- where: '1=1'
755
- };
190
+ npm run dev # Start demo dev server
191
+ npm run build # Build library (ESM + UMD)
192
+ npm run test # Run tests (727 tests, 31 suites)
193
+ npm run type-check # TypeScript check
194
+ npm run lint # ESLint
195
+ npm run build:docs # Build documentation site
196
+ npm run build:demo # Build demo site
756
197
  ```
757
198
 
758
- All type declarations are available in the `dist/` directory after building.
759
-
760
199
  ## Contributing
761
200
 
762
- We welcome contributions! Please follow these steps:
201
+ 1. Fork and create a feature branch
202
+ 2. Make changes and add tests
203
+ 3. Run `npm run test && npm run type-check && npm run lint`
204
+ 4. Submit a pull request
763
205
 
764
- 1. Fork the repository
765
- 2. Create a feature branch: `git checkout -b feature/my-feature`
766
- 3. Make changes and add tests
767
- 4. Run validation: `npm run validate`
768
- 5. Commit changes: `git commit -m 'Add new feature'`
769
- 6. Push to branch: `git push origin feature/my-feature`
770
- 7. Submit a pull request
771
-
772
- ### Pre-commit Hooks
773
-
774
- This project uses [Husky](https://typicode.github.io/husky/) to automatically run quality checks before each commit:
775
-
776
- - **Formatting & Linting**: Automatically formats and lints staged files using Prettier and ESLint
777
- - **Testing**: Runs the full test suite to ensure no regressions
778
-
779
- The hooks are automatically installed when you run `npm install`. If you need to skip them (not recommended), you can use `git commit --no-verify`.
780
-
781
- For more details, see [.husky/README.md](.husky/README.md).
206
+ Pre-commit hooks automatically run formatting, linting, and tests via [Husky](https://typicode.github.io/husky/).
782
207
 
783
208
  ## License
784
209
 
785
- MIT License - see [LICENSE](LICENSE) file for details.
210
+ MIT โ€” see [LICENSE](LICENSE)
786
211
 
787
212
  ## Acknowledgements
788
213
 
789
- - **Esri Leaflet** - Inspiration for API design and architectural patterns
790
- - **MapLibre GL JS** & **Mapbox GL JS** - Incredible WebGL mapping libraries
791
- - **Esri ArcGIS Platform** - Comprehensive GIS services and APIs
792
- - **[mapbox-gl-esri-sources](https://github.com/frontiersi/mapbox-gl-esri-sources/)** - Reference implementation for Esri service integration patterns
793
- - **[mapbox-gl-arcgis-featureserver](https://github.com/rowanwins/mapbox-gl-arcgis-featureserver)** by **Rowan Winsemius** - The FeatureService implementation with PBF support is based on this excellent library. It provides efficient tile-based feature loading with Protocol Buffer Format (PBF) support for minimal payload size.
794
-
795
- ## Fork Notice
796
-
797
- This project originated as a fork of **[frontiersi/mapbox-gl-esri-sources](https://github.com/frontiersi/mapbox-gl-esri-sources/)**. It has been substantially refactored and expanded:
798
-
799
- - Migrated to a service + task architecture similar to Esri Leaflet
800
- - Added unified TypeScript typing and consolidated build outputs (ESM + UMD)
801
- - Introduced additional services (Dynamic, Image, Vector Basemap Styles, Identify / Query tasks, smart FeatureService vector tile detection, etc.)
802
- - Implemented a React/Vite demo suite and extended test coverage
803
-
804
- All original credit for the foundational concept and early integration patterns goes to the maintainers of the upstream repository. If you need the simpler original implementation, or want to compare behavior, please visit the upstream project.
805
-
214
+ - **[Esri Leaflet](https://esri.github.io/esri-leaflet/)** โ€” API design inspiration
215
+ - **[mapbox-gl-esri-sources](https://github.com/frontiersi/mapbox-gl-esri-sources/)** โ€” Foundational integration patterns (this project originated as a fork)
216
+ - **[mapbox-gl-arcgis-featureserver](https://github.com/rowanwins/mapbox-gl-arcgis-featureserver)** by Rowan Winsemius โ€” FeatureService PBF implementation
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "esri-gl",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "A module for making it easier to use Esri services in mapbox-gl or maplibre-gl.",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -33,7 +33,7 @@
33
33
  "bugs": {
34
34
  "url": "https://github.com/muimsd/esri-gl/issues"
35
35
  },
36
- "homepage": "https://esri-gl.netlify.app",
36
+ "homepage": "https://esri-gl.pages.dev",
37
37
  "keywords": [
38
38
  "mapbox",
39
39
  "maplibre",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "esri-gl",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "type": "module",
5
5
  "description": "A module for making it easier to use Esri services in mapbox-gl or maplibre-gl.",
6
6
  "main": "dist/index.js",
@@ -86,7 +86,7 @@
86
86
  "@rollup/plugin-babel": "^6.1.0",
87
87
  "@rollup/plugin-commonjs": "^29.0.2",
88
88
  "@rollup/plugin-node-resolve": "^16.0.3",
89
- "@rollup/plugin-terser": "^0.4.4",
89
+ "@rollup/plugin-terser": "^1.0.0",
90
90
  "@rollup/plugin-typescript": "^12.3.0",
91
91
  "@testing-library/dom": "^10.4.1",
92
92
  "@testing-library/jest-dom": "^6.9.1",
@@ -124,8 +124,8 @@
124
124
  "bugs": {
125
125
  "url": "https://github.com/muimsd/esri-gl/issues"
126
126
  },
127
- "homepage": "https://esri-gl.netlify.app",
128
- "demo": "https://esri-gl-demo.netlify.app",
127
+ "homepage": "https://esri-gl.pages.dev",
128
+ "demo": "https://esri-gl-demo.pages.dev",
129
129
  "author": "Muhammad Imran Siddique 2025, Rowan Winsemius 2020-2025",
130
130
  "license": "MIT",
131
131
  "keywords": [