Temperature
This section demonstrates how we use shaders to visualize temperature data. The data is fetched from a tile server and rendered using WebGL shaders.
Temperature data
The temperature data is fetched from a tile server that provides XYZ tiles with temperature data. To simplify the example, we use a static image of the temperature data and a static image of the map background. The image below consists of multiple tiles stitched together to create a larger image. In the next example, we will use WebGL to render this data together with our background map.

Render raw temperature data with WebGL
Setting up WebGL requires some boilerplate code, which we will not provide in these simplified examples. If you are unfamiliar with WebGL, we recommend visiting WebGL Fundamentals to get a grasp of the basics. After creating a canvas element and setting up the WebGL context, we can start creating our shaders.
Vertex shader
attribute vec2 aPosition;
attribute vec2 aTexCoords;
uniform vec2 uResolution;
varying vec2 vTexCoord;
void main() {
// convert the rectangle from pixels to 0.0 to 1.0
vec2 zeroToOne = aPosition / uResolution;
// convert from 0->1 to 0->2
vec2 zeroToTwo = zeroToOne * 2.0;
// convert from 0->2 to -1->+1 (clipspace)
vec2 clipSpace = zeroToTwo - 1.0;
vTexCoord = aTexCoords;
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
// pass the texCoord to the fragment shader
// The GPU will interpolate this value between points.
}
A simple vertex shader whose sole purpose is to provide our fragment shader with the texture coordinates.
Fragment shader
precision mediump float;
// our texture
uniform sampler2D uDataTexture; // Texture containing the temperature data
uniform sampler2D uMapTexture; // Texture containing the background map
varying vec2 vTexCoord;
void main() {
// Calling texture2D to get the color of the temperature data and the background map
vec4 temperatureDataColor = texture2D(uDataTexture, vTexCoord);
vec4 mapTextureColor = texture2D(uMapTexture, vTexCoord);
// Blend the colors by multiplying them
gl_FragColor = temperatureDataColor * mapTextureColor;
}
The fragment shader gets the texture coordinates (vTexCoord) to look up which color this coordinate represents in both the temperature data and our background map. As a simple way of blending these colors, we multiply them by each other.
Output
This is the result that is drawn to our canvas element.
Applying our own color gradient
The raw data is not easily readable for humans, as it only contains temperature values stored in the R channel of the image. To make it more readable, we need to make some changes in the fragment shader. The vertex shader will remain the same.
Apply a color from our own gradient.
Fragment shader changes
To read the color from the gradient instead of just using the raw data, we have added uniform sampler2D uColorGradientTexture
, and the function vec2 getDirectionFromWindData(vec4 windData)
to help us achieve this.
precision mediump float;
// our texture
uniform sampler2D uDataTexture;
uniform sampler2D uMapTexture;
uniform sampler2D uColorGradientTexture;
varying vec2 vTexCoord;
void main() {
// Getting temperature data from the temperature data texture,
// and getting the color from the backgroundmap texture
vec4 temperatureData = texture2D(uDataTexture, vTexCoord);
vec4 mapTextureColor = texture2D(uMapTexture, vTexCoord);
// In this example the color gradient is moving from left to right, where the color on the
// far left represents the coldest value and far right the warmest, so we can use the
// r-value directly since it represents the temperature
vec4 temperatureColor = texture2D(uColorGradientTexture, vec2(temperatureData.r, 0.0));
gl_FragColor = temperatureColor * mapTextureColor;
}
Output
After making these changes to the fragment shader, we can see the color gradient has been rendered in the end result.