esri-gl 0.9.0-alpha.11 → 0.9.0-alpha.7
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 +0 -9
- package/dist/esri-gl.esm.js +0 -522
- package/dist/esri-gl.esm.js.map +1 -1
- package/dist/esri-gl.js +1 -523
- package/dist/esri-gl.js.map +1 -1
- package/dist/esri-gl.min.js +1 -1
- package/dist/esri-gl.min.js.map +1 -1
- package/dist/types/Services/DynamicMapService.d.ts +3 -76
- package/dist/types/types.d.ts +0 -245
- package/package.json +19 -14
package/README.md
CHANGED
|
@@ -2,19 +2,10 @@
|
|
|
2
2
|
|
|
3
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.
|
|
4
4
|
|
|
5
|
-
> **🚧 Development Notice**
|
|
6
|
-
>
|
|
7
|
-
> This project is currently under active development. APIs may change between releases and some features may not be fully stable. Please use with caution in production environments and check the [changelog](CHANGES.md) for breaking changes between versions.
|
|
8
|
-
|
|
9
5
|
[](https://badge.fury.io/js/esri-gl)
|
|
10
6
|
[](http://www.typescriptlang.org/)
|
|
11
7
|
[](https://opensource.org/licenses/MIT)
|
|
12
8
|
|
|
13
|
-
## 🔗 Links
|
|
14
|
-
|
|
15
|
-
- **📚 [Documentation](https://esri-gl.netlify.app/)** - Complete API reference and guides
|
|
16
|
-
- **🎮 [Live Demos](https://esri-gl-demo.netlify.app/)** - Interactive examples and code samples
|
|
17
|
-
|
|
18
9
|
**Note**: This library is compatible with both **MapLibre GL JS** and **Mapbox GL JS**.
|
|
19
10
|
|
|
20
11
|
## Features
|
package/dist/esri-gl.esm.js
CHANGED
|
@@ -428,13 +428,6 @@ class DynamicMapService {
|
|
|
428
428
|
writable: true,
|
|
429
429
|
value: void 0
|
|
430
430
|
});
|
|
431
|
-
// Transaction-like updates
|
|
432
|
-
Object.defineProperty(this, "_pendingUpdates", {
|
|
433
|
-
enumerable: true,
|
|
434
|
-
configurable: true,
|
|
435
|
-
writable: true,
|
|
436
|
-
value: null
|
|
437
|
-
});
|
|
438
431
|
if (!esriServiceOptions.url) {
|
|
439
432
|
throw new Error('A url must be supplied as part of the esriServiceOptions object.');
|
|
440
433
|
}
|
|
@@ -444,7 +437,6 @@ class DynamicMapService {
|
|
|
444
437
|
this._defaultEsriOptions = {
|
|
445
438
|
layers: false,
|
|
446
439
|
layerDefs: false,
|
|
447
|
-
dynamicLayers: false,
|
|
448
440
|
format: 'png24',
|
|
449
441
|
dpi: 96,
|
|
450
442
|
transparent: true,
|
|
@@ -486,31 +478,6 @@ class DynamicMapService {
|
|
|
486
478
|
to = to.valueOf();
|
|
487
479
|
return `${from},${to}`;
|
|
488
480
|
}
|
|
489
|
-
// ArcGIS Dynamic Layer styling parameter (JSON string)
|
|
490
|
-
get _dynamicLayers() {
|
|
491
|
-
const dl = this.options.dynamicLayers;
|
|
492
|
-
if (!dl)
|
|
493
|
-
return false;
|
|
494
|
-
try {
|
|
495
|
-
const normalized = dl.map(l => {
|
|
496
|
-
const { visible, ...rest } = l;
|
|
497
|
-
const withSource = {
|
|
498
|
-
...rest,
|
|
499
|
-
// ensure required source exists
|
|
500
|
-
source: l.source ?? { type: 'mapLayer', mapLayerId: l.id },
|
|
501
|
-
};
|
|
502
|
-
// Convert client-friendly 'visible' to ArcGIS 'visibility'
|
|
503
|
-
if (typeof visible === 'boolean') {
|
|
504
|
-
withSource.visibility = visible;
|
|
505
|
-
}
|
|
506
|
-
return withSource;
|
|
507
|
-
});
|
|
508
|
-
return JSON.stringify(normalized);
|
|
509
|
-
}
|
|
510
|
-
catch {
|
|
511
|
-
return false;
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
481
|
get _source() {
|
|
515
482
|
const tileSize = this.rasterSrcOptions?.tileSize ?? 256;
|
|
516
483
|
// These are the bare minimum parameters
|
|
@@ -528,8 +495,6 @@ class DynamicMapService {
|
|
|
528
495
|
params.append('time', this._time);
|
|
529
496
|
if (this._layerDefs)
|
|
530
497
|
params.append('layerDefs', this._layerDefs);
|
|
531
|
-
if (this._dynamicLayers)
|
|
532
|
-
params.append('dynamicLayers', this._dynamicLayers);
|
|
533
498
|
return {
|
|
534
499
|
type: 'raster',
|
|
535
500
|
tiles: [`${this.options.url}/export?bbox={bbox-epsg-3857}&${params.toString()}`],
|
|
@@ -542,27 +507,20 @@ class DynamicMapService {
|
|
|
542
507
|
}
|
|
543
508
|
// This requires hooking into some undocumented methods
|
|
544
509
|
_updateSource() {
|
|
545
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
546
510
|
const src = this._map.getSource(this._sourceId);
|
|
547
511
|
src.tiles[0] = this._source.tiles[0];
|
|
548
512
|
src._options = this._source;
|
|
549
513
|
if (src.setTiles) {
|
|
550
514
|
// New MapboxGL >= 2.13.0
|
|
551
515
|
src.setTiles(this._source.tiles);
|
|
552
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
553
516
|
}
|
|
554
517
|
else if (this._map.style.sourceCaches) {
|
|
555
518
|
// Old MapboxGL and MaplibreGL
|
|
556
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
557
519
|
this._map.style.sourceCaches[this._sourceId].clearTiles();
|
|
558
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
559
520
|
this._map.style.sourceCaches[this._sourceId].update(this._map.transform);
|
|
560
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
561
521
|
}
|
|
562
522
|
else if (this._map.style._otherSourceCaches) {
|
|
563
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
564
523
|
this._map.style.sourceCaches[this._sourceId].clearTiles();
|
|
565
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
566
524
|
this._map.style.sourceCaches[this._sourceId].update(this._map.transform);
|
|
567
525
|
}
|
|
568
526
|
}
|
|
@@ -570,174 +528,6 @@ class DynamicMapService {
|
|
|
570
528
|
this.esriServiceOptions.layerDefs = obj;
|
|
571
529
|
this._updateSource();
|
|
572
530
|
}
|
|
573
|
-
/**
|
|
574
|
-
* Replace the entire dynamicLayers array. Server applies these drawing rules.
|
|
575
|
-
* Note: dynamicLayers overrides default drawing for listed sublayers.
|
|
576
|
-
*/
|
|
577
|
-
setDynamicLayers(layers) {
|
|
578
|
-
this.esriServiceOptions.dynamicLayers = layers || false;
|
|
579
|
-
this._updateSource();
|
|
580
|
-
}
|
|
581
|
-
/** Helper to ensure all visible layers are included when using dynamicLayers */
|
|
582
|
-
_ensureAllVisibleLayers(dynamicLayers) {
|
|
583
|
-
const visibleLayerIds = this._getVisibleLayerIds();
|
|
584
|
-
const existingIds = new Set(dynamicLayers.map(dl => dl.id));
|
|
585
|
-
// Add entries for visible layers that aren't already in dynamicLayers
|
|
586
|
-
const additional = visibleLayerIds
|
|
587
|
-
.filter(id => !existingIds.has(id))
|
|
588
|
-
.map(id => ({ id, visible: true }));
|
|
589
|
-
return [...dynamicLayers, ...additional];
|
|
590
|
-
}
|
|
591
|
-
/** Get the list of currently visible layer IDs */
|
|
592
|
-
_getVisibleLayerIds() {
|
|
593
|
-
const lyrs = this.options.layers;
|
|
594
|
-
if (!lyrs)
|
|
595
|
-
return [];
|
|
596
|
-
if (!Array.isArray(lyrs))
|
|
597
|
-
return [lyrs];
|
|
598
|
-
return lyrs;
|
|
599
|
-
}
|
|
600
|
-
/** Merge/update a sublayer's drawingInfo with provided fields. */
|
|
601
|
-
setLayerDrawingInfo(layerId, drawingInfo) {
|
|
602
|
-
const current = this.esriServiceOptions.dynamicLayers || [];
|
|
603
|
-
const next = Array.isArray(current) ? [...current] : [];
|
|
604
|
-
const idx = next.findIndex(l => l.id === layerId);
|
|
605
|
-
if (idx >= 0) {
|
|
606
|
-
next[idx] = { ...next[idx], drawingInfo: { ...next[idx].drawingInfo, ...drawingInfo } };
|
|
607
|
-
}
|
|
608
|
-
else {
|
|
609
|
-
next.push({ id: layerId, drawingInfo });
|
|
610
|
-
}
|
|
611
|
-
// Ensure all visible layers are included
|
|
612
|
-
this.esriServiceOptions.dynamicLayers = this._ensureAllVisibleLayers(next);
|
|
613
|
-
this._updateSource();
|
|
614
|
-
}
|
|
615
|
-
/** Convenience to set a renderer on a sublayer */
|
|
616
|
-
setLayerRenderer(layerId, renderer) {
|
|
617
|
-
this.setLayerDrawingInfo(layerId, { renderer });
|
|
618
|
-
}
|
|
619
|
-
/** Show/hide a sublayer via dynamicLayers */
|
|
620
|
-
setLayerVisibility(layerId, visible) {
|
|
621
|
-
const current = this.esriServiceOptions.dynamicLayers || [];
|
|
622
|
-
const next = Array.isArray(current) ? [...current] : [];
|
|
623
|
-
const idx = next.findIndex(l => l.id === layerId);
|
|
624
|
-
if (idx >= 0) {
|
|
625
|
-
next[idx] = { ...next[idx], visible };
|
|
626
|
-
}
|
|
627
|
-
else {
|
|
628
|
-
next.push({ id: layerId, visible });
|
|
629
|
-
}
|
|
630
|
-
// Ensure all visible layers are included
|
|
631
|
-
this.esriServiceOptions.dynamicLayers = this._ensureAllVisibleLayers(next);
|
|
632
|
-
this._updateSource();
|
|
633
|
-
}
|
|
634
|
-
/** Set a definitionExpression for a sublayer, applied server-side */
|
|
635
|
-
setLayerDefinition(layerId, definitionExpression) {
|
|
636
|
-
const current = this.esriServiceOptions.dynamicLayers || [];
|
|
637
|
-
const next = Array.isArray(current) ? [...current] : [];
|
|
638
|
-
const idx = next.findIndex(l => l.id === layerId);
|
|
639
|
-
if (idx >= 0) {
|
|
640
|
-
next[idx] = { ...next[idx], definitionExpression };
|
|
641
|
-
}
|
|
642
|
-
else {
|
|
643
|
-
next.push({ id: layerId, definitionExpression });
|
|
644
|
-
}
|
|
645
|
-
// Ensure all visible layers are included
|
|
646
|
-
this.esriServiceOptions.dynamicLayers = this._ensureAllVisibleLayers(next);
|
|
647
|
-
this._updateSource();
|
|
648
|
-
}
|
|
649
|
-
// Build a SQL where clause and apply as definitionExpression
|
|
650
|
-
setLayerFilter(layerId, filter) {
|
|
651
|
-
const where = this._buildWhere(filter);
|
|
652
|
-
if (where)
|
|
653
|
-
this.setLayerDefinition(layerId, where);
|
|
654
|
-
}
|
|
655
|
-
_escapeValue(val) {
|
|
656
|
-
if (val === null)
|
|
657
|
-
return 'NULL';
|
|
658
|
-
if (val instanceof Date)
|
|
659
|
-
return `${val.valueOf()}`; // epoch ms for time-enabled services
|
|
660
|
-
if (typeof val === 'number')
|
|
661
|
-
return `${val}`;
|
|
662
|
-
if (typeof val === 'boolean')
|
|
663
|
-
return val ? '1' : '0';
|
|
664
|
-
const s = String(val).replace(/'/g, "''");
|
|
665
|
-
return `'${s}'`;
|
|
666
|
-
}
|
|
667
|
-
// Very small, pragmatic builder that covers common cases in types.ts
|
|
668
|
-
_isGroupFilter(f) {
|
|
669
|
-
return (typeof f !== 'string' &&
|
|
670
|
-
'op' in f &&
|
|
671
|
-
(f.op === 'AND' || f.op === 'OR') &&
|
|
672
|
-
'filters' in f &&
|
|
673
|
-
Array.isArray(f.filters));
|
|
674
|
-
}
|
|
675
|
-
_isBetweenFilter(f) {
|
|
676
|
-
return (typeof f !== 'string' &&
|
|
677
|
-
'op' in f &&
|
|
678
|
-
f.op === 'BETWEEN' &&
|
|
679
|
-
'field' in f &&
|
|
680
|
-
typeof f.field === 'string' &&
|
|
681
|
-
'from' in f &&
|
|
682
|
-
f.from !== undefined &&
|
|
683
|
-
'to' in f &&
|
|
684
|
-
f.to !== undefined);
|
|
685
|
-
}
|
|
686
|
-
_isInFilter(f) {
|
|
687
|
-
return (typeof f !== 'string' &&
|
|
688
|
-
'op' in f &&
|
|
689
|
-
f.op === 'IN' &&
|
|
690
|
-
'field' in f &&
|
|
691
|
-
typeof f.field === 'string' &&
|
|
692
|
-
'values' in f &&
|
|
693
|
-
Array.isArray(f.values));
|
|
694
|
-
}
|
|
695
|
-
_isNullFilter(f) {
|
|
696
|
-
return (typeof f !== 'string' &&
|
|
697
|
-
'op' in f &&
|
|
698
|
-
(f.op === 'IS NULL' || f.op === 'IS NOT NULL') &&
|
|
699
|
-
'field' in f &&
|
|
700
|
-
typeof f.field === 'string');
|
|
701
|
-
}
|
|
702
|
-
_isComparisonFilter(f) {
|
|
703
|
-
return (typeof f !== 'string' &&
|
|
704
|
-
'field' in f &&
|
|
705
|
-
typeof f.field === 'string' &&
|
|
706
|
-
'op' in f &&
|
|
707
|
-
typeof f.op === 'string' &&
|
|
708
|
-
'value' in f &&
|
|
709
|
-
f.value !== undefined);
|
|
710
|
-
}
|
|
711
|
-
_buildWhere(filter) {
|
|
712
|
-
if (!filter)
|
|
713
|
-
return undefined;
|
|
714
|
-
if (typeof filter === 'string')
|
|
715
|
-
return filter.trim();
|
|
716
|
-
if (this._isBetweenFilter(filter)) {
|
|
717
|
-
return `${filter.field} BETWEEN ${this._escapeValue(filter.from)} AND ${this._escapeValue(filter.to)}`;
|
|
718
|
-
}
|
|
719
|
-
if (this._isInFilter(filter)) {
|
|
720
|
-
const vals = filter.values.map(v => this._escapeValue(v)).join(', ');
|
|
721
|
-
return `${filter.field} IN (${vals})`;
|
|
722
|
-
}
|
|
723
|
-
if (this._isNullFilter(filter)) {
|
|
724
|
-
return `${filter.field} ${filter.op}`;
|
|
725
|
-
}
|
|
726
|
-
if (this._isGroupFilter(filter)) {
|
|
727
|
-
const built = filter.filters
|
|
728
|
-
.map((f) => this._buildWhere(f))
|
|
729
|
-
.filter((s) => Boolean(s));
|
|
730
|
-
if (!built.length)
|
|
731
|
-
return undefined;
|
|
732
|
-
if (built.length === 1)
|
|
733
|
-
return built[0];
|
|
734
|
-
return `(${built.join(` ${filter.op} `)})`;
|
|
735
|
-
}
|
|
736
|
-
if (this._isComparisonFilter(filter)) {
|
|
737
|
-
return `${filter.field} ${filter.op} ${this._escapeValue(filter.value)}`;
|
|
738
|
-
}
|
|
739
|
-
return undefined;
|
|
740
|
-
}
|
|
741
531
|
setLayers(arr) {
|
|
742
532
|
this.esriServiceOptions.layers = arr;
|
|
743
533
|
this._updateSource();
|
|
@@ -775,9 +565,7 @@ class DynamicMapService {
|
|
|
775
565
|
return layersStr ? layersStr.replace('show', 'visible') : false;
|
|
776
566
|
}
|
|
777
567
|
identify(lnglat, returnGeometry = false) {
|
|
778
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
779
568
|
const canvas = this._map.getCanvas();
|
|
780
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
781
569
|
const bounds = this._map.getBounds().toArray();
|
|
782
570
|
const params = new URLSearchParams({
|
|
783
571
|
sr: '4326',
|
|
@@ -798,8 +586,6 @@ class DynamicMapService {
|
|
|
798
586
|
});
|
|
799
587
|
if (this._layerDefs)
|
|
800
588
|
params.append('layerDefs', this._layerDefs);
|
|
801
|
-
if (this._dynamicLayers)
|
|
802
|
-
params.append('dynamicLayers', this._dynamicLayers);
|
|
803
589
|
if (this._time)
|
|
804
590
|
params.append('time', this._time);
|
|
805
591
|
return new Promise((resolve, reject) => {
|
|
@@ -809,313 +595,6 @@ class DynamicMapService {
|
|
|
809
595
|
.catch(error => reject(error));
|
|
810
596
|
});
|
|
811
597
|
}
|
|
812
|
-
// ========================================
|
|
813
|
-
// Advanced Features
|
|
814
|
-
// ========================================
|
|
815
|
-
/** Set labeling configuration for a sublayer */
|
|
816
|
-
setLayerLabels(layerId, labelingInfo) {
|
|
817
|
-
const current = this.esriServiceOptions.dynamicLayers || [];
|
|
818
|
-
const next = Array.isArray(current) ? [...current] : [];
|
|
819
|
-
const idx = next.findIndex(l => l.id === layerId);
|
|
820
|
-
if (idx >= 0) {
|
|
821
|
-
next[idx] = {
|
|
822
|
-
...next[idx],
|
|
823
|
-
drawingInfo: {
|
|
824
|
-
...next[idx].drawingInfo,
|
|
825
|
-
labelingInfo: [labelingInfo],
|
|
826
|
-
},
|
|
827
|
-
};
|
|
828
|
-
}
|
|
829
|
-
else {
|
|
830
|
-
next.push({
|
|
831
|
-
id: layerId,
|
|
832
|
-
drawingInfo: { labelingInfo: [labelingInfo] },
|
|
833
|
-
});
|
|
834
|
-
}
|
|
835
|
-
// Ensure all visible layers are included
|
|
836
|
-
this.esriServiceOptions.dynamicLayers = this._ensureAllVisibleLayers(next);
|
|
837
|
-
this._updateSource();
|
|
838
|
-
}
|
|
839
|
-
/** Toggle label visibility for a sublayer */
|
|
840
|
-
setLayerLabelsVisible(layerId, visible) {
|
|
841
|
-
if (visible) {
|
|
842
|
-
// If enabling labels but no labeling info exists, set a default
|
|
843
|
-
const current = this.esriServiceOptions.dynamicLayers || [];
|
|
844
|
-
const layer = Array.isArray(current) ? current.find(l => l.id === layerId) : null;
|
|
845
|
-
if (!layer?.drawingInfo?.labelingInfo) {
|
|
846
|
-
// Set a basic label configuration
|
|
847
|
-
this.setLayerLabels(layerId, {
|
|
848
|
-
labelExpression: '[OBJECTID]', // Default to object ID
|
|
849
|
-
symbol: {
|
|
850
|
-
type: 'esriTS',
|
|
851
|
-
color: [0, 0, 0, 255],
|
|
852
|
-
font: { family: 'Arial', size: 8 },
|
|
853
|
-
},
|
|
854
|
-
});
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
else {
|
|
858
|
-
// Remove labeling info to disable labels
|
|
859
|
-
const current = this.esriServiceOptions.dynamicLayers || [];
|
|
860
|
-
const next = Array.isArray(current) ? [...current] : [];
|
|
861
|
-
const idx = next.findIndex(l => l.id === layerId);
|
|
862
|
-
if (idx >= 0) {
|
|
863
|
-
const drawingInfo = { ...next[idx].drawingInfo };
|
|
864
|
-
delete drawingInfo.labelingInfo;
|
|
865
|
-
next[idx] = { ...next[idx], drawingInfo };
|
|
866
|
-
// Ensure all visible layers are included
|
|
867
|
-
this.esriServiceOptions.dynamicLayers = this._ensureAllVisibleLayers(next);
|
|
868
|
-
this._updateSource();
|
|
869
|
-
}
|
|
870
|
-
}
|
|
871
|
-
}
|
|
872
|
-
/** Set time options for a time-enabled sublayer */
|
|
873
|
-
setLayerTimeOptions(layerId, timeOptions) {
|
|
874
|
-
const current = this.esriServiceOptions.dynamicLayers || [];
|
|
875
|
-
const next = Array.isArray(current) ? [...current] : [];
|
|
876
|
-
const idx = next.findIndex(l => l.id === layerId);
|
|
877
|
-
if (idx >= 0) {
|
|
878
|
-
next[idx] = { ...next[idx], layerTimeOptions: timeOptions };
|
|
879
|
-
}
|
|
880
|
-
else {
|
|
881
|
-
next.push({ id: layerId, layerTimeOptions: timeOptions });
|
|
882
|
-
}
|
|
883
|
-
// Ensure all visible layers are included
|
|
884
|
-
this.esriServiceOptions.dynamicLayers = this._ensureAllVisibleLayers(next);
|
|
885
|
-
this._updateSource();
|
|
886
|
-
}
|
|
887
|
-
/** Animate through time periods for time-enabled layers */
|
|
888
|
-
async animateTime(options) {
|
|
889
|
-
const { from, to, intervalMs, loop = false, onFrame, onComplete } = options;
|
|
890
|
-
const totalDuration = to.getTime() - from.getTime();
|
|
891
|
-
const steps = Math.ceil(totalDuration / intervalMs);
|
|
892
|
-
return new Promise(resolve => {
|
|
893
|
-
let currentStep = 0;
|
|
894
|
-
const animate = () => {
|
|
895
|
-
if (currentStep >= steps && !loop) {
|
|
896
|
-
onComplete?.();
|
|
897
|
-
resolve();
|
|
898
|
-
return;
|
|
899
|
-
}
|
|
900
|
-
const progress = (currentStep % steps) / steps;
|
|
901
|
-
const currentTime = new Date(from.getTime() + progress * totalDuration);
|
|
902
|
-
// Update service time extent
|
|
903
|
-
this.esriServiceOptions.from = currentTime;
|
|
904
|
-
this.esriServiceOptions.to = currentTime;
|
|
905
|
-
this._updateSource();
|
|
906
|
-
onFrame?.(currentTime, progress);
|
|
907
|
-
currentStep++;
|
|
908
|
-
setTimeout(animate, intervalMs);
|
|
909
|
-
};
|
|
910
|
-
animate();
|
|
911
|
-
});
|
|
912
|
-
}
|
|
913
|
-
/** Get statistics for a sublayer */
|
|
914
|
-
async getLayerStatistics(layerId, statisticFields, options = {}) {
|
|
915
|
-
const queryUrl = `${this.esriServiceOptions.url}/${layerId}/query`;
|
|
916
|
-
const params = new URLSearchParams({
|
|
917
|
-
f: 'json',
|
|
918
|
-
where: options.where || '1=1',
|
|
919
|
-
outStatistics: JSON.stringify(statisticFields),
|
|
920
|
-
returnGeometry: 'false',
|
|
921
|
-
});
|
|
922
|
-
if (options.groupByFieldsForStatistics) {
|
|
923
|
-
params.append('groupByFieldsForStatistics', options.groupByFieldsForStatistics);
|
|
924
|
-
}
|
|
925
|
-
const response = await fetch(`${queryUrl}?${params.toString()}`);
|
|
926
|
-
const data = await response.json();
|
|
927
|
-
if (data.error) {
|
|
928
|
-
throw new Error(`Statistics query failed: ${data.error.message}`);
|
|
929
|
-
}
|
|
930
|
-
return data.features || [];
|
|
931
|
-
}
|
|
932
|
-
/** Query features from a specific sublayer */
|
|
933
|
-
async queryLayerFeatures(layerId, options = {}) {
|
|
934
|
-
const queryUrl = `${this.esriServiceOptions.url}/${layerId}/query`;
|
|
935
|
-
const params = new URLSearchParams({
|
|
936
|
-
f: 'json',
|
|
937
|
-
where: options.where || '1=1',
|
|
938
|
-
returnGeometry: options.returnGeometry !== false ? 'true' : 'false',
|
|
939
|
-
outFields: Array.isArray(options.outFields)
|
|
940
|
-
? options.outFields.join(',')
|
|
941
|
-
: options.outFields || '*',
|
|
942
|
-
});
|
|
943
|
-
if (options.geometry) {
|
|
944
|
-
params.append('geometry', JSON.stringify(options.geometry));
|
|
945
|
-
params.append('geometryType', options.geometryType || 'esriGeometryEnvelope');
|
|
946
|
-
params.append('spatialRel', options.spatialRel || 'esriSpatialRelIntersects');
|
|
947
|
-
}
|
|
948
|
-
if (options.orderByFields) {
|
|
949
|
-
params.append('orderByFields', options.orderByFields);
|
|
950
|
-
}
|
|
951
|
-
if (options.resultOffset) {
|
|
952
|
-
params.append('resultOffset', options.resultOffset.toString());
|
|
953
|
-
}
|
|
954
|
-
if (options.resultRecordCount) {
|
|
955
|
-
params.append('resultRecordCount', options.resultRecordCount.toString());
|
|
956
|
-
}
|
|
957
|
-
if (options.returnCountOnly) {
|
|
958
|
-
params.append('returnCountOnly', 'true');
|
|
959
|
-
}
|
|
960
|
-
if (options.returnIdsOnly) {
|
|
961
|
-
params.append('returnIdsOnly', 'true');
|
|
962
|
-
}
|
|
963
|
-
const response = await fetch(`${queryUrl}?${params.toString()}`);
|
|
964
|
-
const data = await response.json();
|
|
965
|
-
if (data.error) {
|
|
966
|
-
throw new Error(`Layer query failed: ${data.error.message}`);
|
|
967
|
-
}
|
|
968
|
-
return data;
|
|
969
|
-
}
|
|
970
|
-
/** Export high-resolution map image */
|
|
971
|
-
async exportMapImage(options) {
|
|
972
|
-
const exportUrl = `${this.esriServiceOptions.url}/export`;
|
|
973
|
-
const params = new URLSearchParams({
|
|
974
|
-
f: 'image',
|
|
975
|
-
bbox: options.bbox.join(','),
|
|
976
|
-
size: options.size.join(','),
|
|
977
|
-
format: options.format || 'png24',
|
|
978
|
-
transparent: options.transparent !== false ? 'true' : 'false',
|
|
979
|
-
dpi: (options.dpi || 96).toString(),
|
|
980
|
-
bboxSR: (options.bboxSR || 3857).toString(),
|
|
981
|
-
imageSR: (options.imageSR || 3857).toString(),
|
|
982
|
-
});
|
|
983
|
-
if (options.layerDefs) {
|
|
984
|
-
params.append('layerDefs', JSON.stringify(options.layerDefs));
|
|
985
|
-
}
|
|
986
|
-
if (options.dynamicLayers) {
|
|
987
|
-
const normalized = this._ensureAllVisibleLayers(options.dynamicLayers);
|
|
988
|
-
params.append('dynamicLayers', JSON.stringify(normalized));
|
|
989
|
-
}
|
|
990
|
-
if (options.gdbVersion) {
|
|
991
|
-
params.append('gdbVersion', options.gdbVersion);
|
|
992
|
-
}
|
|
993
|
-
if (options.historicMoment) {
|
|
994
|
-
params.append('historicMoment', options.historicMoment.toString());
|
|
995
|
-
}
|
|
996
|
-
const response = await fetch(`${exportUrl}?${params.toString()}`);
|
|
997
|
-
if (!response.ok) {
|
|
998
|
-
throw new Error(`Export failed: ${response.statusText}`);
|
|
999
|
-
}
|
|
1000
|
-
return response.blob();
|
|
1001
|
-
}
|
|
1002
|
-
/** Generate legend information for layers */
|
|
1003
|
-
async generateLegend(layerIds) {
|
|
1004
|
-
const legendUrl = `${this.esriServiceOptions.url}/legend`;
|
|
1005
|
-
const params = new URLSearchParams({
|
|
1006
|
-
f: 'json',
|
|
1007
|
-
});
|
|
1008
|
-
if (layerIds?.length) {
|
|
1009
|
-
params.append('layers', layerIds.join(','));
|
|
1010
|
-
}
|
|
1011
|
-
const response = await fetch(`${legendUrl}?${params.toString()}`);
|
|
1012
|
-
const data = await response.json();
|
|
1013
|
-
if (data.error) {
|
|
1014
|
-
throw new Error(`Legend generation failed: ${data.error.message}`);
|
|
1015
|
-
}
|
|
1016
|
-
return data.layers || [];
|
|
1017
|
-
}
|
|
1018
|
-
/** Get detailed information about a specific layer */
|
|
1019
|
-
async getLayerInfo(layerId) {
|
|
1020
|
-
const layerUrl = `${this.esriServiceOptions.url}/${layerId}`;
|
|
1021
|
-
const params = new URLSearchParams({ f: 'json' });
|
|
1022
|
-
const response = await fetch(`${layerUrl}?${params.toString()}`);
|
|
1023
|
-
const data = await response.json();
|
|
1024
|
-
if (data.error) {
|
|
1025
|
-
throw new Error(`Layer info request failed: ${data.error.message}`);
|
|
1026
|
-
}
|
|
1027
|
-
return data;
|
|
1028
|
-
}
|
|
1029
|
-
/** Get field information for a layer */
|
|
1030
|
-
async getLayerFields(layerId) {
|
|
1031
|
-
const layerInfo = await this.getLayerInfo(layerId);
|
|
1032
|
-
return layerInfo.fields || [];
|
|
1033
|
-
}
|
|
1034
|
-
/** Get spatial extent of a layer */
|
|
1035
|
-
async getLayerExtent(layerId) {
|
|
1036
|
-
const layerInfo = await this.getLayerInfo(layerId);
|
|
1037
|
-
if (!layerInfo.extent) {
|
|
1038
|
-
throw new Error(`No extent available for layer ${layerId}`);
|
|
1039
|
-
}
|
|
1040
|
-
return layerInfo.extent;
|
|
1041
|
-
}
|
|
1042
|
-
/** Discover all layers in the service */
|
|
1043
|
-
async discoverLayers() {
|
|
1044
|
-
const serviceUrl = this.esriServiceOptions.url;
|
|
1045
|
-
const params = new URLSearchParams({ f: 'json' });
|
|
1046
|
-
const response = await fetch(`${serviceUrl}?${params.toString()}`);
|
|
1047
|
-
const data = await response.json();
|
|
1048
|
-
if (data.error) {
|
|
1049
|
-
throw new Error(`Service discovery failed: ${data.error.message}`);
|
|
1050
|
-
}
|
|
1051
|
-
return data.layers || [];
|
|
1052
|
-
}
|
|
1053
|
-
/** Apply multiple layer operations in a single update */
|
|
1054
|
-
setBulkLayerProperties(operations) {
|
|
1055
|
-
const current = this.esriServiceOptions.dynamicLayers || [];
|
|
1056
|
-
const next = Array.isArray(current) ? [...current] : [];
|
|
1057
|
-
// Process all operations
|
|
1058
|
-
for (const op of operations) {
|
|
1059
|
-
const idx = next.findIndex(l => l.id === op.layerId);
|
|
1060
|
-
const layer = idx >= 0 ? { ...next[idx] } : { id: op.layerId };
|
|
1061
|
-
switch (op.operation) {
|
|
1062
|
-
case 'visibility':
|
|
1063
|
-
layer.visible = op.value;
|
|
1064
|
-
break;
|
|
1065
|
-
case 'renderer':
|
|
1066
|
-
layer.drawingInfo = { ...layer.drawingInfo, renderer: op.value };
|
|
1067
|
-
break;
|
|
1068
|
-
case 'definition':
|
|
1069
|
-
layer.definitionExpression = op.value;
|
|
1070
|
-
break;
|
|
1071
|
-
case 'filter': {
|
|
1072
|
-
const where = this._buildWhere(op.value);
|
|
1073
|
-
if (where)
|
|
1074
|
-
layer.definitionExpression = where;
|
|
1075
|
-
break;
|
|
1076
|
-
}
|
|
1077
|
-
case 'labels':
|
|
1078
|
-
layer.drawingInfo = {
|
|
1079
|
-
...layer.drawingInfo,
|
|
1080
|
-
labelingInfo: op.value,
|
|
1081
|
-
};
|
|
1082
|
-
break;
|
|
1083
|
-
case 'time':
|
|
1084
|
-
layer.layerTimeOptions = op.value;
|
|
1085
|
-
break;
|
|
1086
|
-
}
|
|
1087
|
-
if (idx >= 0) {
|
|
1088
|
-
next[idx] = layer;
|
|
1089
|
-
}
|
|
1090
|
-
else {
|
|
1091
|
-
next.push(layer);
|
|
1092
|
-
}
|
|
1093
|
-
}
|
|
1094
|
-
// Ensure all visible layers are included
|
|
1095
|
-
this.esriServiceOptions.dynamicLayers = this._ensureAllVisibleLayers(next);
|
|
1096
|
-
this._updateSource();
|
|
1097
|
-
}
|
|
1098
|
-
/** Begin a batch update transaction */
|
|
1099
|
-
beginUpdate() {
|
|
1100
|
-
const current = this.esriServiceOptions.dynamicLayers || [];
|
|
1101
|
-
this._pendingUpdates = Array.isArray(current) ? [...current] : [];
|
|
1102
|
-
}
|
|
1103
|
-
/** Commit all pending updates */
|
|
1104
|
-
commitUpdate() {
|
|
1105
|
-
if (this._pendingUpdates) {
|
|
1106
|
-
this.esriServiceOptions.dynamicLayers = this._ensureAllVisibleLayers(this._pendingUpdates);
|
|
1107
|
-
this._pendingUpdates = null;
|
|
1108
|
-
this._updateSource();
|
|
1109
|
-
}
|
|
1110
|
-
}
|
|
1111
|
-
/** Rollback pending updates */
|
|
1112
|
-
rollbackUpdate() {
|
|
1113
|
-
this._pendingUpdates = null;
|
|
1114
|
-
}
|
|
1115
|
-
/** Check if currently in a transaction */
|
|
1116
|
-
get isInTransaction() {
|
|
1117
|
-
return this._pendingUpdates !== null;
|
|
1118
|
-
}
|
|
1119
598
|
update() {
|
|
1120
599
|
this._updateSource();
|
|
1121
600
|
}
|
|
@@ -1262,7 +741,6 @@ class ImageService {
|
|
|
1262
741
|
this._defaultEsriOptions = {
|
|
1263
742
|
layers: false,
|
|
1264
743
|
layerDefs: false,
|
|
1265
|
-
dynamicLayers: false,
|
|
1266
744
|
format: 'jpgpng',
|
|
1267
745
|
dpi: 96,
|
|
1268
746
|
transparent: true,
|