Skip to main content

Web Viewer Raster Symbology

We decided to simplify the work necessary to bring our QML files from QRave into the web so we can have symbolized rasters in WebRAVE.

The raster .txt format we are using is based on the r.colors spec and we use it when we run gdalDem to "bake-in" the symbology. The spec for this file is compatible with both r.colors and ESRI HDR color table files (.clr).

The right approach depends on which QGIS renderer your layer uses — read on to find yours.


File placement

The filename must follow the pattern somename.txt, where somename matches the symbology attribute in your business logic XML — the same naming convention used for .qml files.

For example, given this business logic entry:

<Node label="Detrended DEM (HAND - Height Above Nearest Drainage)" xpath="Raster[@id='HAND']" type="raster" symbology="hand" transparency="40" />

the file should be named hand.txt.

Case sensitivity

The .txt extension must always be lowercase, and the filename must match the business logic XML exactly — including capitalization.

These files should live alongside the .json files for the WebRAVE vectors:

# Shared across all projects:
RiverscapesXML/Symbology/web/Shared/whatever.txt

# Project-specific:
RiverscapesXML/Symbology/web/ProjectTypeName/whatever.txt

The .txt file format

Every .txt color ramp file has the same basic structure: a header line declaring the interpolation mode, followed by one comma-separated row per color stop.

# QGIS Generated Color Map Export File
INTERPOLATION:INTERPOLATED
1259.34,255,235,176,255,My Label

The columns are:

ValueRedGreenBlueAlphaLabel
FLOATInt (0–255)Int (0–255)Int (0–255)Int (0–255)TEXT

The label column is always treated as text, so you can use descriptive names like Open Water or numeric stand-ins like 1259 — your choice.


Understanding the interpolation modes

The INTERPOLATION: header controls how QGIS (and subsequently gdaldem) fills in colors between your defined stops. There are three modes:

QGIS Renderer (UI name).txt headerBehaviorBest for
Singleband pseudocolorINTERPOLATION:INTERPOLATEDSmooth linear gradient between stopsContinuous data (DEMs, temperature, precipitation)
Singleband pseudocolor (clamped)INTERPOLATION:DISCRETESmooth gradient between stops; values below the lowest or above the highest stop are clamped to the edge colorContinuous data where the color ramp range should stay fixed (slope, aspect, bathymetry)
Paletted / Unique values (exact)INTERPOLATION:EXACTStrict per-value matching; everything else is NoDataCategorical data (land-cover codes, classified integers)

1. INTERPOLATION:INTERPOLATED (Linear / Smooth)

QGIS creates a smooth gradient between your color stops, linearly blending colors for any value that falls between them.

  • How it works: If you set 0 to Blue and 100 to Red, a pixel with value 50 will render as Purple — a perfect 50/50 blend.
  • Best used for: Continuous data where smooth transitions represent reality, such as Digital Elevation Models (DEMs), temperature maps, or precipitation data.

2. INTERPOLATION:DISCRETE (Clamped / Fixed-Range)

The tiler builds a smooth gradient between your color stops (same as INTERPOLATED), but it does not stretch the color ramp to fit the raster's full data range. Instead, the color ramp stays fixed at its own elevation range, and any pixel value falling outside that range receives the nearest edge color.

  • How it works: If your color ramp defines stops at 10 (Blue) and 90 (Red), a pixel with value 50 will render as a smooth blend (Purple). But a pixel with value 5 (below the lowest stop) gets solid Blue (clamped), and a pixel with value 120 (above the highest stop) gets solid Red (clamped).
  • Key difference from INTERPOLATED: With INTERPOLATED, the entire ramp is stretched so the raster's min and max align with the first and last color stops. With DISCRETE, the ramp stays in its original range and values outside that range are clamped to the edge colors.
  • Best used for: Continuous data where the color ramp's value range is meaningful and shouldn't be stretched — for example, slope maps where 0–30° has a fixed color scheme and steeper slopes should all show the same "max" color, or bathymetry where depth bands should stay put.

3. INTERPOLATION:EXACT (Strict Matching)

There is no blending and no grouping. A color is applied only if a pixel's value matches a defined stop exactly; everything else renders as transparent (NoData).

  • How it works: If you assign 1 to Green and 2 to Yellow, only pixels that are exactly 1.000 or 2.000 get those colors. A pixel with value 1.5 will be transparent.
  • Best used for: Categorical or classified data where in-between values are meaningless — for example, a land-cover map where 1 = Forest, 2 = Water, 3 = Urban.

Exporting from QGIS

Singleband pseudocolor → INTERPOLATION:INTERPOLATED

Use this for continuous, smoothly varying data like DEMs or temperature surfaces.

  1. Load your raster layer in QGIS using QViewer (assuming you already have a .qml for this layer).
  2. Open the Layer Styling panel, confirm the renderer is set to Singleband pseudocolor.
  3. Click the small save icon to export the color map.

Name the exported file according to the file placement rules above and commit it to git.

Example: dem.txt

# QGIS Generated Color Map Export File
INTERPOLATION:INTERPOLATED
1129.3089599609375,255,235,176,255,1129
1482.00189819335946595,38,115,0,255,1482
1834.6948364257814319,115,77,0,255,1835
2198.075439453125,255,255,255,255,2198

The label column can hold descriptive text instead of numbers — Small, Medium, Large works just as well as the raw float values.

Note that the exact float values exported by QGIS are specific to the raster you were symbolizing. When applied to other rasters, these values are converted to percentages at tile-time so the color ramp stretches correctly across the new data range.

Details
Under the hood: tile-time conversion

When the tiling pipeline processes an INTERPOLATED file, it converts the absolute values to percentages and produces an internal representation like this:

{
"gdalDem": "0%,255,235,176,255\n33%,38,115,0,255\n66%,115,77,0,255\n100%,255,255,255,255\nnv,0,0,0,0",
"legend": [
["rgba(255,235,176,255)", "0%"],
["rgba(38,115,0,255)", "33%"],
["rgba(115,77,0,255)", "66%"],
["rgba(255,255,255,255)", "100%"]
],
"rampType": "INTERPOLATED"
}

The legend array drives the legend displayed in the web viewer. The gdalDem string is written to a temp file and passed directly to the gdaldem command:

0%,255,235,176,255
33%,38,115,0,255
66%,115,77,0,255
100%,255,255,255,255
nv,0,0,0,0

nv is the NoData value — it is set automatically to ensure transparent rendering of NoData pixels.


Paletted / Unique values → INTERPOLATION:EXACT only

For paletted (categorical) rasters, use INTERPOLATION:EXACT — this gives strict per-value matching.

Exporting from QGIS:

  1. Load your raster layer in QGIS using QViewer (assuming you already have a .qml for this layer).
  2. Next to the Delete All button, click the small menu button and choose Export Color Map to File.

This produces a space-separated .clr file:

1 31 213 223 255 Small
3 98 218 162 255 Medium
4 62 212 79 255 Large
5 217 210 69 255 Huge
6 230 87 51 255 Scientifically Ginormous

You will need to manually convert this to the comma-separated .txt format with the INTERPOLATION:EXACT header.

INTERPOLATION:DISCRETE is not for categorical data

Despite its name, INTERPOLATION:DISCRETE performs smooth linear interpolation between color stops with edge clamping — it does not produce stepped blocks. It is designed for continuous data (like slope surfaces) where you want a fixed-range gradient, not for categorical/paletted rasters.

For truly stepped or blocky rendering, use INTERPOLATION:EXACT with every integer value in your range listed explicitly.

Using INTERPOLATION:EXACT

Use this when your raster uses strict integer codes where in-between values are meaningless (e.g. a land-cover classification). Only pixels whose values match a table entry exactly will be colored; everything else renders as transparent (NoData).

# QGIS Generated Color Map Export File
INTERPOLATION:EXACT
-9999,255,255,255,255,(-9999) No Data
0,0,0,0,255,None
11,0,92,230,255,Open Water
12,255,255,255,255,Perennial Ice/Snow
31,0,0,0,255,Barren-Rock/Sand/Clay
381,255,115,223,255,Inter-Mountain Basins Big Sagebrush Steppe
383,255,255,190,255,Inter-Mountain Basins Sparsely Vegetated Systems
384,255,255,190,255,Rocky Mountain Alpine/Montane Sparsely Vegetated Systems
385,255,255,190,255,North American Warm Desert Sparsely Vegetated Systems
386,205,205,102,255,Western Great Plains Shortgrass Prairie
387,137,205,102,255,Western Great Plains Foothill and Piedmont Grassland
388,132,140,200,255,Wyoming Basins Dwarf Sagebrush Shrubland and Steppe

In this example, a value of 382 has no entry in the table — it won't render at all. This is a key difference from INTERPOLATED, where 382 would be smoothly blended between the nearest stops.