• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

visgl / loaders.gl / 24310065355

12 Apr 2026 03:30PM UTC coverage: 55.777% (+0.3%) from 55.485%
24310065355

push

github

web-flow
feat: new VectorSource (#3373)

9401 of 18236 branches covered (51.55%)

Branch coverage included in aggregate %.

226 of 276 new or added lines in 9 files covered. (81.88%)

4 existing lines in 1 file now uncovered.

19600 of 33759 relevant lines covered (58.06%)

5031.76 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

68.75
/modules/deck-layers/src/vector-source-layer.ts
1
// loaders.gl
2
// SPDX-License-Identifier: MIT
3
// Copyright (c) vis.gl contributors
4

5
import {
6
  CompositeLayer,
7
  type CompositeLayerProps,
8
  type DefaultProps,
9
  type Layer,
10
  type UpdateParameters,
11
  _deepEqual as deepEqual
12
} from '@deck.gl/core';
13
import type {GeoJsonLayerProps} from '@deck.gl/layers';
14
import {GeoJsonLayer} from '@deck.gl/layers';
15
import type {BinaryFeatureCollection, GeoJSONTable} from '@loaders.gl/schema';
16
import type {VectorSource} from '@loaders.gl/loader-utils';
17
import {VectorSet} from './vector-source-layer/vector-set';
18

19
/** Props for {@link VectorSourceLayer}. */
20
export type VectorSourceLayerProps = CompositeLayerProps & {
21
  /** Fully constructed loaders.gl vector source. */
22
  data: VectorSource;
23
  /** Named source layers forwarded to `VectorSource#getFeatures`. */
24
  layers: string | string[];
25
  /** Output CRS forwarded to `VectorSource#getFeatures`. */
26
  crs?: string;
27
  /** Debounce interval applied before viewport requests are issued. */
28
  debounceTime?: number;
29
  /** Called when the current viewport request resolves successfully. */
30
  onDataLoad?: (table: GeoJSONTable | BinaryFeatureCollection) => void;
31
  /** Called when metadata or viewport requests fail. */
32
  onError?: (error: Error) => void;
33
  /** Called when metadata/viewport loading starts or stops. */
34
  onLoadingStateChange?: (isLoading: boolean) => void;
35
  /** Optional props forwarded into the default `GeoJsonLayer`. */
36
  geoJsonLayerProps?: Partial<GeoJsonLayerProps>;
37
};
38

39
type VectorSourceLayerState = {
40
  vectorSet: VectorSet | null;
41
  unsubscribeVectorSetEvents: (() => void) | null;
42
};
43

44
const defaultProps: DefaultProps<VectorSourceLayerProps> = {
3✔
45
  id: 'vector-source-layer',
46
  crs: 'EPSG:4326',
47
  debounceTime: 200,
48
  geoJsonLayerProps: {type: 'object', compare: false, value: {}},
49
  onDataLoad: {type: 'function', value: () => {}},
50
  onError: {
51
    type: 'function',
52
    compare: false,
53
    value: (error: Error) => {
54
      // eslint-disable-next-line no-console
NEW
55
      console.error(error);
×
56
    }
57
  },
58
  onLoadingStateChange: {type: 'function', value: () => {}}
59
};
60

61
/**
62
 * Internal deck.gl layer that renders a source-backed vector table for the active viewport.
63
 *
64
 * This class is exported for internal repository use and examples, and is not documented
65
 * beyond these TSDoc comments.
66
 */
67
export class VectorSourceLayer extends CompositeLayer<VectorSourceLayerProps> {
68
  /** deck.gl layer name used in debugging output. */
69
  static layerName = 'VectorSourceLayer';
3✔
70

71
  /** Default props shared across source-backed vector layers. */
72
  static defaultProps: DefaultProps = defaultProps;
3✔
73

74
  /** Typed deck.gl state for the owned vector runtime. */
75
  state = null as unknown as VectorSourceLayerState;
2✔
76

77
  /** Returns true when the current vector runtime has accepted data. */
78
  get isLoaded(): boolean {
NEW
79
    return Boolean(this.state?.vectorSet?.isLoaded) && super.isLoaded;
×
80
  }
81

82
  /** Lets deck.gl know that viewport changes should trigger updates. */
83
  shouldUpdateState(): boolean {
NEW
84
    return true;
×
85
  }
86

87
  /** Initializes state on first render. */
88
  initializeState(): void {
89
    this.state = {
2✔
90
      vectorSet: null,
91
      unsubscribeVectorSetEvents: null
92
    };
93
  }
94

95
  /** Finalizes subscriptions and owned vector state. */
96
  finalizeState(): void {
NEW
97
    this._releaseVectorSet();
×
98
  }
99

100
  /** Keeps the owned vector runtime in sync with the current source props and viewport. */
101
  updateState({changeFlags, props, oldProps}: UpdateParameters<this>): void {
102
    const dataChanged = changeFlags.dataChanged;
3✔
103

104
    if (dataChanged) {
3✔
105
      const vectorSet = this._getOrCreateVectorSet(props.data, true);
2✔
106
      vectorSet.setOptions(this._getVectorSetOptions(props));
2✔
107
      void vectorSet.loadMetadata().catch(() => {});
2✔
108
      void this._updateViewport();
2✔
109
      return;
2✔
110
    }
111

112
    if (!this.state.vectorSet) {
1!
NEW
113
      return;
×
114
    }
115

116
    if (!deepEqual(props.layers, oldProps.layers, 1) || props.crs !== oldProps.crs) {
1!
NEW
117
      this.state.vectorSet.setOptions(this._getVectorSetOptions(props));
×
NEW
118
      void this._updateViewport();
×
NEW
119
      return;
×
120
    }
121

122
    if (props.debounceTime !== oldProps.debounceTime) {
1!
NEW
123
      this.state.vectorSet.setOptions(this._getVectorSetOptions(props));
×
NEW
124
      void this._updateViewport();
×
NEW
125
      return;
×
126
    }
127

128
    if (changeFlags.viewportChanged) {
1!
129
      void this._updateViewport();
1✔
130
    }
131
  }
132

133
  /** Renders the current accepted vector table through `GeoJsonLayer`. */
134
  renderLayers(): Layer | null {
135
    const table = this.state.vectorSet?.data;
1✔
136
    if (!table) {
1!
NEW
137
      return null;
×
138
    }
139

140
    const geoJsonData = isGeoJSONTable(table)
1!
141
      ? {
142
          type: table.type,
143
          features: table.features
144
        }
145
      : table;
146

147
    return new GeoJsonLayer({
1✔
148
      ...this.getSubLayerProps({id: 'geojson'}),
149
      ...this.props.geoJsonLayerProps,
150
      data: geoJsonData
151
    }) as unknown as Layer;
152
  }
153

154
  /** Creates or reuses the shared vector runtime for the current source. */
155
  private _getOrCreateVectorSet(vectorSource: VectorSource, sourceChanged: boolean): VectorSet {
156
    if (!this.state.vectorSet || sourceChanged) {
2!
157
      this._releaseVectorSet();
2✔
158

159
      const vectorSet = VectorSet.fromVectorSource(vectorSource, {
2✔
160
        layers: this.props.layers,
161
        crs: this.props.crs,
162
        debounceTime: this.props.debounceTime
163
      });
164
      const unsubscribeVectorSetEvents = vectorSet.subscribe({
2✔
165
        onLoadingStateChange: isLoading => this.props.onLoadingStateChange?.(isLoading),
6✔
166
        onUpdate: () => this.setNeedsUpdate(),
14✔
167
        onDataLoad: table => this.props.onDataLoad?.(table),
2✔
168
        onError: error => this.props.onError?.(error)
1✔
169
      });
170

171
      this.setState({vectorSet, unsubscribeVectorSetEvents});
2✔
172
      return vectorSet;
2✔
173
    }
174

NEW
175
    return this.state.vectorSet;
×
176
  }
177

178
  /** Tears down subscriptions and owned vector runtime state. */
179
  private _releaseVectorSet(): void {
180
    this.state?.unsubscribeVectorSetEvents?.();
2✔
181
    this.state?.vectorSet?.finalize();
2✔
182
    this.setState?.({
2✔
183
      vectorSet: null,
184
      unsubscribeVectorSetEvents: null
185
    });
186
  }
187

188
  /** Builds runtime options from the current layer props. */
189
  private _getVectorSetOptions(props: VectorSourceLayerProps) {
190
    return {
2✔
191
      vectorSource: props.data,
192
      layers: props.layers,
193
      crs: props.crs,
194
      debounceTime: props.debounceTime
195
    };
196
  }
197

198
  /** Requests the current viewport table when a viewport is available. */
199
  private async _updateViewport(): Promise<void> {
200
    const viewport = this.context.viewport;
3✔
201
    const vectorSet = this.state.vectorSet;
3✔
202
    if (!viewport || !vectorSet) {
3!
NEW
203
      return;
×
204
    }
205

206
    await vectorSet.updateViewport(viewport);
3✔
207
  }
208
}
209

210
function isGeoJSONTable(
211
  data: GeoJSONTable | BinaryFeatureCollection
212
): data is GeoJSONTable {
213
  return (data as GeoJSONTable).shape === 'geojson-table';
1✔
214
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc