Merge pull request #13020 from tschaub/set-style
Allow WebGL tile layer style to be updated
This commit is contained in:
20
examples/cog-style.html
Normal file
20
examples/cog-style.html
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
layout: example.html
|
||||
title: Change Tile Layer Style
|
||||
shortdesc: Updating the style of a WebGL tile layer
|
||||
docs: >
|
||||
When you want to change the style of a WebGL tile layer based on some change in your
|
||||
application state, you should use the `layer.updateStyleVariables()` method. A layer can
|
||||
be efficiently rendered even if style variables are changed on every render frame.
|
||||
In cases where you need to completely replace the style of a layer, you can call the
|
||||
`layer.setStyle()` method. This method should not be called in response to frequent
|
||||
user events (e.g. mouse movement, dragging a slider, etc.).
|
||||
tags: "cog, webgl, style"
|
||||
---
|
||||
<div id="map" class="map"></div>
|
||||
Set the layer style
|
||||
<select id="style">
|
||||
<option value="trueColor">True Color</option>
|
||||
<option value="falseColor">False Color</option>
|
||||
<option value="ndvi">NDVI</option>
|
||||
</select>
|
||||
105
examples/cog-style.js
Normal file
105
examples/cog-style.js
Normal file
@@ -0,0 +1,105 @@
|
||||
import GeoTIFF from '../src/ol/source/GeoTIFF.js';
|
||||
import Map from '../src/ol/Map.js';
|
||||
import TileLayer from '../src/ol/layer/WebGLTile.js';
|
||||
import View from '../src/ol/View.js';
|
||||
|
||||
const max = 3000;
|
||||
function normalize(value) {
|
||||
return ['/', value, max];
|
||||
}
|
||||
|
||||
const red = normalize(['band', 1]);
|
||||
const green = normalize(['band', 2]);
|
||||
const blue = normalize(['band', 3]);
|
||||
const nir = normalize(['band', 4]);
|
||||
|
||||
const trueColor = {
|
||||
color: ['array', red, green, blue, 1],
|
||||
gamma: 1.1,
|
||||
};
|
||||
|
||||
const falseColor = {
|
||||
color: ['array', nir, red, green, 1],
|
||||
gamma: 1.1,
|
||||
};
|
||||
|
||||
const ndvi = {
|
||||
color: [
|
||||
'interpolate',
|
||||
['linear'],
|
||||
['/', ['-', nir, red], ['+', nir, red]],
|
||||
// color ramp for NDVI values, ranging from -1 to 1
|
||||
-0.2,
|
||||
[191, 191, 191],
|
||||
-0.1,
|
||||
[219, 219, 219],
|
||||
0,
|
||||
[255, 255, 224],
|
||||
0.025,
|
||||
[255, 250, 204],
|
||||
0.05,
|
||||
[237, 232, 181],
|
||||
0.075,
|
||||
[222, 217, 156],
|
||||
0.1,
|
||||
[204, 199, 130],
|
||||
0.125,
|
||||
[189, 184, 107],
|
||||
0.15,
|
||||
[176, 194, 97],
|
||||
0.175,
|
||||
[163, 204, 89],
|
||||
0.2,
|
||||
[145, 191, 82],
|
||||
0.25,
|
||||
[128, 179, 71],
|
||||
0.3,
|
||||
[112, 163, 64],
|
||||
0.35,
|
||||
[97, 150, 54],
|
||||
0.4,
|
||||
[79, 138, 46],
|
||||
0.45,
|
||||
[64, 125, 36],
|
||||
0.5,
|
||||
[48, 110, 28],
|
||||
0.55,
|
||||
[33, 97, 18],
|
||||
0.6,
|
||||
[15, 84, 10],
|
||||
0.65,
|
||||
[0, 69, 0],
|
||||
],
|
||||
};
|
||||
|
||||
const layer = new TileLayer({
|
||||
style: trueColor,
|
||||
source: new GeoTIFF({
|
||||
normalize: false,
|
||||
sources: [
|
||||
{
|
||||
url: 'https://s2downloads.eox.at/demo/EOxCloudless/2020/rgbnir/s2cloudless2020-16bits_sinlge-file_z0-4.tif',
|
||||
},
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
const map = new Map({
|
||||
target: 'map',
|
||||
layers: [layer],
|
||||
view: new View({
|
||||
projection: 'EPSG:4326',
|
||||
center: [0, 0],
|
||||
zoom: 2,
|
||||
maxZoom: 6,
|
||||
}),
|
||||
});
|
||||
|
||||
const styles = {trueColor, falseColor, ndvi};
|
||||
const styleSelector = document.getElementById('style');
|
||||
|
||||
function update() {
|
||||
const style = styles[styleSelector.value];
|
||||
layer.setStyle(style);
|
||||
}
|
||||
styleSelector.addEventListener('change', update);
|
||||
@@ -311,6 +311,28 @@ class WebGLTileLayer extends BaseTileLayer {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the layer style. The `updateStyleVariables` function is a more efficient
|
||||
* way to update layer rendering. In cases where the whole style needs to be updated,
|
||||
* this method may be called instead.
|
||||
* @param {Style} style The new style.
|
||||
*/
|
||||
setStyle(style) {
|
||||
this.style_ = style;
|
||||
const source = this.getSource();
|
||||
const parsedStyle = parseStyle(
|
||||
this.style_,
|
||||
'bandCount' in source ? source.bandCount : 4
|
||||
);
|
||||
const renderer = this.getRenderer();
|
||||
renderer.reset({
|
||||
vertexShader: parsedStyle.vertexShader,
|
||||
fragmentShader: parsedStyle.fragmentShader,
|
||||
uniforms: parsedStyle.uniforms,
|
||||
});
|
||||
this.changed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update any variables used by the layer style and trigger a re-render.
|
||||
* @param {Object<string, number>} variables Variables to update.
|
||||
|
||||
@@ -90,6 +90,17 @@ class WebGLLayerRenderer extends LayerRenderer {
|
||||
layer.addChangeListener(LayerProperty.MAP, this.removeHelper_.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset options (only handles uniforms).
|
||||
* @param {Options} options Options.
|
||||
*/
|
||||
reset(options) {
|
||||
this.uniforms_ = options.uniforms;
|
||||
if (this.helper) {
|
||||
this.helper.setUniforms(this.uniforms_);
|
||||
}
|
||||
}
|
||||
|
||||
removeHelper_() {
|
||||
if (this.helper) {
|
||||
this.helper.dispose();
|
||||
|
||||
@@ -213,6 +213,24 @@ class WebGLTileLayerRenderer extends WebGLLayerRenderer {
|
||||
this.tileTextureCache_ = new LRUCache(cacheSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Options} options Options.
|
||||
*/
|
||||
reset(options) {
|
||||
super.reset({
|
||||
uniforms: options.uniforms,
|
||||
});
|
||||
this.vertexShader_ = options.vertexShader;
|
||||
this.fragmentShader_ = options.fragmentShader;
|
||||
|
||||
if (this.helper) {
|
||||
this.program_ = this.helper.getProgram(
|
||||
this.fragmentShader_,
|
||||
this.vertexShader_
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
afterHelperCreated() {
|
||||
this.program_ = this.helper.getProgram(
|
||||
this.fragmentShader_,
|
||||
|
||||
@@ -340,7 +340,6 @@ class WebGLHelper extends Disposable {
|
||||
* @type {WebGLRenderingContext}
|
||||
*/
|
||||
this.gl_ = getContext(this.canvas_);
|
||||
const gl = this.getGL();
|
||||
|
||||
/**
|
||||
* @private
|
||||
@@ -407,14 +406,11 @@ class WebGLHelper extends Disposable {
|
||||
*/
|
||||
this.uniforms_ = [];
|
||||
if (options.uniforms) {
|
||||
for (const name in options.uniforms) {
|
||||
this.uniforms_.push({
|
||||
name: name,
|
||||
value: options.uniforms[name],
|
||||
});
|
||||
}
|
||||
this.setUniforms(options.uniforms);
|
||||
}
|
||||
|
||||
const gl = this.getGL();
|
||||
|
||||
/**
|
||||
* An array of PostProcessingPass objects is kept in this variable, built from the steps provided in the
|
||||
* options. If no post process was given, a default one is used (so as not to have to make an exception to
|
||||
@@ -447,6 +443,20 @@ class WebGLHelper extends Disposable {
|
||||
this.startTime_ = Date.now();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object<string, UniformValue>} uniforms Uniform definitions.
|
||||
*/
|
||||
setUniforms(uniforms) {
|
||||
this.uniforms_ = [];
|
||||
for (const name in uniforms) {
|
||||
this.uniforms_.push({
|
||||
name: name,
|
||||
value: uniforms[name],
|
||||
});
|
||||
}
|
||||
this.uniformLocations_ = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} canvasCacheKey The canvas cache key.
|
||||
* @return {boolean} The provided key matches the one this helper was constructed with.
|
||||
|
||||
BIN
test/rendering/cases/cog-style/expected.png
Normal file
BIN
test/rendering/cases/cog-style/expected.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 57 KiB |
45
test/rendering/cases/cog-style/main.js
Normal file
45
test/rendering/cases/cog-style/main.js
Normal file
@@ -0,0 +1,45 @@
|
||||
import GeoTIFF from '../../../../src/ol/source/GeoTIFF.js';
|
||||
import Map from '../../../../src/ol/Map.js';
|
||||
import TileLayer from '../../../../src/ol/layer/WebGLTile.js';
|
||||
|
||||
const source = new GeoTIFF({
|
||||
sources: [
|
||||
{
|
||||
url: '/data/raster/sentinel-b04.tif',
|
||||
min: 0,
|
||||
max: 10000,
|
||||
},
|
||||
{
|
||||
url: '/data/raster/sentinel-b08.tif',
|
||||
min: 0,
|
||||
max: 10000,
|
||||
},
|
||||
],
|
||||
transition: 0,
|
||||
});
|
||||
|
||||
const layer = new TileLayer({
|
||||
source: source,
|
||||
});
|
||||
|
||||
new Map({
|
||||
layers: [layer],
|
||||
target: 'map',
|
||||
view: source.getView(),
|
||||
});
|
||||
|
||||
layer.setStyle({
|
||||
color: [
|
||||
'interpolate',
|
||||
['linear'],
|
||||
['/', ['-', ['band', 2], ['band', 1]], ['+', ['band', 2], ['band', 1]]],
|
||||
-0.2,
|
||||
[200, 0, 0],
|
||||
1,
|
||||
[0, 255, 0],
|
||||
],
|
||||
});
|
||||
|
||||
render({
|
||||
message: 'update the layer style',
|
||||
});
|
||||
Reference in New Issue
Block a user