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

keplergl / kepler.gl / 23881346444

02 Apr 2026 02:51AM UTC coverage: 60.559% (-1.1%) from 61.699%
23881346444

Pull #3271

github

web-flow
Merge cb3cf1185 into bc59e880b
Pull Request #3271: chore: deck.gl 9.2 upgrade & loaders.gl, luma.gl upgrades

6514 of 12797 branches covered (50.9%)

Branch coverage included in aggregate %.

284 of 765 new or added lines in 51 files covered. (37.12%)

122 existing lines in 17 files now uncovered.

13273 of 19877 relevant lines covered (66.78%)

79.35 hits per line

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

2.04
/src/deckgl-layers/src/raster/raster-layer/raster-layer.ts
1
// SPDX-License-Identifier: MIT
2
// Copyright contributors to the kepler.gl project
3

4
import {UpdateParameters} from '@deck.gl/core';
5
import {BitmapLayer} from '@deck.gl/layers';
6

7
import {
8
  buildRasterFragmentShader,
9
  buildRasterVertexShader,
10
  rasterUniforms,
11
  ensureRasterHooksRegistered,
12
  prepareLumaModules
13
} from './raster-layer-shaders';
14
import {loadImages} from '../images';
15
import type {RasterLayerAddedProps, ImageState} from '../types';
16
import {modulesEqual} from '../util';
17
import {patchPipelineValidation} from '../pipeline-validation-patch';
18

19
const defaultProps = {
13✔
20
  ...BitmapLayer.defaultProps,
21
  modules: {type: 'array', value: [], compare: true},
22
  images: {type: 'object', value: {}, compare: true},
23
  moduleProps: {type: 'object', value: {}, compare: true},
24
  onRedrawNeeded: {type: 'function', value: null, compare: false}
25
};
26

27
export default class RasterLayer extends BitmapLayer<RasterLayerAddedProps> {
28
  declare state: BitmapLayer<RasterLayerAddedProps>['state'] & {
29
    images: ImageState;
30
  };
31

NEW
32
  _redrawScheduled = false;
×
33

34
  initializeState(): void {
NEW
35
    patchPipelineValidation();
×
NEW
36
    ensureRasterHooksRegistered();
×
UNCOV
37
    this.setState({images: {}});
×
UNCOV
38
    super.initializeState();
×
39
  }
40

41
  draw(_opts: {shaderModuleProps: Record<string, unknown>}): void {
42
    const {model, images, coordinateConversion, bounds} = this.state;
×
43
    const {desaturate, transparentColor, tintColor, moduleProps} = this.props;
×
44

UNCOV
45
    if (
×
46
      !model ||
×
47
      !images ||
48
      Object.keys(images).length === 0 ||
49
      !Object.values(images).every(item => item)
×
50
    ) {
51
      return;
×
52
    }
53

54
    // Set UBO uniforms for the raster module
NEW
55
    model.shaderInputs.setProps({
×
56
      raster: {
57
        desaturate: desaturate || 0,
×
NEW
58
        transparentColor: (transparentColor || [0, 0, 0, 0]).map(x => (x ? x / 255 : 0)),
×
NEW
59
        tintColor: (tintColor || [255, 255, 255]).slice(0, 3).map(x => x / 255),
×
60
        coordinateConversion: coordinateConversion || 0,
×
61
        bounds: bounds || [0, 0, 0, 0],
×
62
        opacity: this.props.opacity ?? 1
×
63
      }
64
    });
65

66
    // Set props for each custom module through shaderInputs.
67
    // We call getUniforms ourselves to skip modules that return null (inactive).
68
    // Passing allModuleProps directly to setProps would cause the null-fallback
69
    // in ShaderInputs to treat the entire props bag as uniforms/bindings,
70
    // triggering expensive texture rebinding every frame.
NEW
71
    const allModuleProps = {...moduleProps, ...images};
×
NEW
72
    const modules = this.props.modules || [];
×
NEW
73
    for (const mod of modules) {
×
NEW
74
      if (mod.getUniforms) {
×
NEW
75
        const result = mod.getUniforms(allModuleProps);
×
NEW
76
        if (result) {
×
NEW
77
          model.shaderInputs.setProps({[mod.name]: result});
×
78
        }
79
      }
80
    }
81

NEW
82
    const drawSuccess = model.draw(this.context.renderPass);
×
NEW
83
    if (!drawSuccess) {
×
NEW
84
      this._scheduleRedraw();
×
85
    }
86
  }
87

88
  _scheduleRedraw(): void {
NEW
89
    if (this._redrawScheduled) return;
×
NEW
90
    this._redrawScheduled = true;
×
NEW
91
    requestAnimationFrame(() => {
×
NEW
92
      this._redrawScheduled = false;
×
NEW
93
      if (this.context.deck) {
×
94
        // @ts-expect-error accessing private deck.gl property
NEW
95
        this.context.deck._needsRedraw = 'RasterLayer pipeline pending';
×
96
      }
NEW
97
      this.context.layerManager?.setNeedsRedraw('RasterLayer pipeline pending');
×
NEW
98
      if (typeof this.props.onRedrawNeeded === 'function') {
×
NEW
99
        this.props.onRedrawNeeded();
×
100
      }
101
    });
102
  }
103

104
  getShaders(): any {
105
    const {modules = []} = this.props;
×
106

NEW
107
    const lumaModules = prepareLumaModules(modules);
×
NEW
108
    const parentShaders = super.getShaders();
×
109

110
    return {
×
111
      ...parentShaders,
112
      vs: buildRasterVertexShader(),
113
      fs: buildRasterFragmentShader(),
114
      modules: [...(parentShaders.modules || []), rasterUniforms, ...lumaModules]
×
115
    };
116
  }
117

118
  // eslint-disable-next-line complexity
119
  updateState(params: UpdateParameters<BitmapLayer<RasterLayerAddedProps>>): void {
120
    const {props, oldProps, changeFlags} = params;
×
121
    const modules = props && props.modules;
×
122
    const oldModules = oldProps && oldProps.modules;
×
123

UNCOV
124
    if (changeFlags.extensionsChanged || !modulesEqual(modules, oldModules)) {
×
NEW
125
      this.state.model?.destroy?.();
×
126
      // @ts-expect-error _getModel is internal to BitmapLayer
NEW
127
      this.state.model = this._getModel(this.context.device || this.context.gl);
×
UNCOV
128
      this.getAttributeManager()?.invalidateAll();
×
129
    }
130

131
    if (props && props.images) {
×
132
      this.updateImages({props, oldProps});
×
133
    }
134

135
    const attributeManager = this.getAttributeManager();
×
136

137
    if (props.bounds !== oldProps.bounds) {
×
138
      const oldMesh = this.state.mesh;
×
139
      const mesh = this._createMesh();
×
140
      this.state.model?.setVertexCount(mesh.vertexCount);
×
141
      for (const key in mesh) {
×
142
        if (oldMesh && oldMesh[key] !== mesh[key]) {
×
143
          attributeManager?.invalidate(key);
×
144
        }
145
      }
146
      this.setState({mesh, ...this._getCoordinateUniforms()});
×
147
    } else if (props._imageCoordinateSystem !== oldProps._imageCoordinateSystem) {
×
148
      this.setState(this._getCoordinateUniforms());
×
149
    }
150
  }
151

152
  updateImages({
153
    props,
154
    oldProps
155
  }: {
156
    props: RasterLayerAddedProps;
157
    oldProps: RasterLayerAddedProps;
158
  }): void {
159
    const {images} = this.state;
×
NEW
160
    const device = this.context.device;
×
NEW
161
    const gl = device?.gl || this.context.gl;
×
162

163
    const newImages = loadImages({
×
164
      gl,
165
      device,
166
      images,
167
      imagesData: props.images,
168
      oldImagesData: oldProps.images
169
    });
170
    if (newImages) {
×
171
      this.setState({images: newImages});
×
172
    }
173
  }
174

175
  finalizeState(): void {
176
    super.finalizeState(this.context);
×
177

178
    if (this.state.images) {
×
179
      for (const image of Object.values(this.state.images)) {
×
180
        if (Array.isArray(image)) {
×
NEW
181
          image.map(x => x && (x.destroy ? x.destroy() : x.delete?.()));
×
182
        } else if (image) {
×
NEW
183
          image.destroy ? image.destroy() : image.delete?.();
×
184
        }
185
      }
186
    }
187
  }
188
}
189

190
RasterLayer.defaultProps = defaultProps;
13✔
191
RasterLayer.layerName = 'RasterLayer';
13✔
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