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

keplergl / kepler.gl / 14430112347

13 Apr 2025 01:53PM UTC coverage: 61.717% (-4.4%) from 66.129%
14430112347

Pull #3048

github

web-flow
Merge 4d33fb563 into 9de30e2ba
Pull Request #3048: [feat] Raster Tile Layer

6066 of 11656 branches covered (52.04%)

Branch coverage included in aggregate %.

136 of 1263 new or added lines in 45 files covered. (10.77%)

5 existing lines in 3 files now uncovered.

12551 of 18509 relevant lines covered (67.81%)

82.41 hits per line

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

2.52
/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 {project32, UpdateParameters} from '@deck.gl/core/typed';
5
import {BitmapLayer} from '@deck.gl/layers/typed';
6
import {isWebGL2} from '@luma.gl/core';
7
import {ProgramManager} from '@luma.gl/engine';
8

9
import fsWebGL1 from './raster-layer-webgl1.fs';
10
import vsWebGL1 from './raster-layer-webgl1.vs';
11
import fsWebGL2 from './raster-layer-webgl2.fs';
12
import vsWebGL2 from './raster-layer-webgl2.vs';
13
import {loadImages} from '../images';
14
import type {RasterLayerAddedProps, ImageState} from '../types';
15
import {modulesEqual} from '../util';
16

17
const defaultProps = {
13✔
18
  ...BitmapLayer.defaultProps,
19
  modules: {type: 'array', value: [], compare: true},
20
  images: {type: 'object', value: {}, compare: true},
21
  moduleProps: {type: 'object', value: {}, compare: true}
22
};
23

24
export default class RasterLayer extends BitmapLayer<RasterLayerAddedProps> {
25
  declare state: BitmapLayer<RasterLayerAddedProps>['state'] & {
26
    images: ImageState;
27
  };
28

29
  initializeState(): void {
NEW
30
    const {gl} = this.context;
×
NEW
31
    const programManager = ProgramManager.getDefaultProgramManager(gl);
×
32

NEW
33
    const fsStr1 = 'fs:DECKGL_MUTATE_COLOR(inout vec4 image, in vec2 coord)';
×
NEW
34
    const fsStr2 = 'fs:DECKGL_CREATE_COLOR(inout vec4 image, in vec2 coord)';
×
35

36
    // Only initialize shader hook functions _once globally_
37
    // Since the program manager is shared across all layers, but many layers
38
    // might be created, this solves the performance issue of always adding new
39
    // hook functions.
NEW
40
    if (!programManager._hookFunctions.includes(fsStr1)) {
×
NEW
41
      programManager.addShaderHook(fsStr1);
×
42
    }
NEW
43
    if (!programManager._hookFunctions.includes(fsStr2)) {
×
NEW
44
      programManager.addShaderHook(fsStr2);
×
45
    }
46

47
    // images is a mapping from keys to Texture2D objects. The keys should match
48
    // names of uniforms in shader modules
NEW
49
    this.setState({images: {}});
×
50

NEW
51
    super.initializeState();
×
52
  }
53

54
  draw({uniforms}: {uniforms: {[key: string]: any}}): void {
NEW
55
    const {model, images, coordinateConversion, bounds} = this.state;
×
NEW
56
    const {desaturate, transparentColor, tintColor, moduleProps} = this.props;
×
57

58
    // Render the image
NEW
59
    if (
×
60
      !model ||
×
61
      !images ||
62
      Object.keys(images).length === 0 ||
NEW
63
      !Object.values(images).every(item => item)
×
64
    ) {
NEW
65
      return;
×
66
    }
67

NEW
68
    model
×
69
      .setUniforms({
70
        ...uniforms,
71
        desaturate,
NEW
72
        transparentColor: transparentColor?.map(x => (x ? x / 255 : 0)),
×
NEW
73
        tintColor: tintColor?.slice(0, 3).map(x => x / 255),
×
74
        coordinateConversion,
75
        bounds
76
      })
77
      .updateModuleSettings({
78
        ...moduleProps,
79
        ...images
80
      })
81
      .draw();
82
  }
83

84
  getShaders(): any {
NEW
85
    const {gl} = this.context;
×
NEW
86
    const {modules = []} = this.props;
×
NEW
87
    const webgl2 = isWebGL2(gl);
×
88

89
    // Choose webgl version for module
90
    // If fs2 or fs1 keys exist, prefer them, but fall back to fs, so that
91
    // version-independent modules don't need to care
NEW
92
    for (const module of modules) {
×
NEW
93
      module.fs = webgl2 ? module.fs2 || module.fs : module.fs1 || module.fs;
×
94

95
      // Sampler type is always float for WebGL1
NEW
96
      if (!webgl2 && module.defines) {
×
NEW
97
        module.defines.SAMPLER_TYPE = 'sampler2D';
×
98
      }
99
    }
100

NEW
101
    return {
×
102
      ...super.getShaders(),
103
      vs: webgl2 ? vsWebGL2 : vsWebGL1,
×
104
      fs: webgl2 ? fsWebGL2 : fsWebGL1,
×
105
      modules: [project32, ...modules]
106
    };
107
  }
108

109
  // eslint-disable-next-line complexity
110
  updateState(params: UpdateParameters<BitmapLayer<RasterLayerAddedProps>>): void {
NEW
111
    const {props, oldProps, changeFlags} = params;
×
NEW
112
    const modules = props && props.modules;
×
NEW
113
    const oldModules = oldProps && oldProps.modules;
×
114

115
    // setup model first
116
    // If the list of modules changed, need to recompile the shaders
NEW
117
    if (changeFlags.extensionsChanged || !modulesEqual(modules, oldModules)) {
×
NEW
118
      const {gl} = this.context;
×
NEW
119
      this.state.model?.delete();
×
NEW
120
      this.state.model = this._getModel(gl);
×
NEW
121
      this.getAttributeManager()?.invalidateAll();
×
122
    }
123

NEW
124
    if (props && props.images) {
×
NEW
125
      this.updateImages({props, oldProps});
×
126
    }
127

NEW
128
    const attributeManager = this.getAttributeManager();
×
129

NEW
130
    if (props.bounds !== oldProps.bounds) {
×
NEW
131
      const oldMesh = this.state.mesh;
×
NEW
132
      const mesh = this._createMesh();
×
NEW
133
      this.state.model?.setVertexCount(mesh.vertexCount);
×
NEW
134
      for (const key in mesh) {
×
NEW
135
        if (oldMesh && oldMesh[key] !== mesh[key]) {
×
NEW
136
          attributeManager?.invalidate(key);
×
137
        }
138
      }
NEW
139
      this.setState({mesh, ...this._getCoordinateUniforms()});
×
NEW
140
    } else if (props._imageCoordinateSystem !== oldProps._imageCoordinateSystem) {
×
NEW
141
      this.setState(this._getCoordinateUniforms());
×
142
    }
143
  }
144

145
  updateImages({
146
    props,
147
    oldProps
148
  }: {
149
    props: RasterLayerAddedProps;
150
    oldProps: RasterLayerAddedProps;
151
  }): void {
NEW
152
    const {images} = this.state;
×
NEW
153
    const {gl} = this.context;
×
154

NEW
155
    const newImages = loadImages({
×
156
      gl,
157
      images,
158
      imagesData: props.images,
159
      oldImagesData: oldProps.images
160
    });
NEW
161
    if (newImages) {
×
NEW
162
      this.setState({images: newImages});
×
163
    }
164
  }
165

166
  finalizeState(): void {
NEW
167
    super.finalizeState(this.context);
×
168

NEW
169
    if (this.state.images) {
×
NEW
170
      for (const image of Object.values(this.state.images)) {
×
NEW
171
        if (Array.isArray(image)) {
×
NEW
172
          image.map(x => x && x.delete());
×
NEW
173
        } else if (image) {
×
NEW
174
          image.delete();
×
175
        }
176
      }
177
    }
178
  }
179
}
180

181
RasterLayer.defaultProps = defaultProps;
13✔
182
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