> For the complete documentation index, see [llms.txt](https://developers.felt.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developers.felt.com/felt-style-language/types-of-visualizations/raster.md).

# Raster visualizations

Raster layers are styled with one of five `type` values. They share a common set of `config` options and **never support popups or labels**.

| Mode                                                                      | `type`        | Use it for                                                     |
| ------------------------------------------------------------------------- | ------------- | -------------------------------------------------------------- |
| [Image](#image-simple)                                                    | `simple`      | Display the raster as-is (satellite, aerial, base tiles)       |
| [Numeric — single band](#numeric-single-band)                             | `numeric`     | Classify one band (elevation, temperature, a hazard index)     |
| [Numeric — multiband / raster algebra](#numeric-multiband-raster-algebra) | `numeric`     | Compute a spectral index (NDVI, NDMI, NDWI) from several bands |
| [Categorical](#categorical)                                               | `categorical` | Discrete pixel classes (land cover, soil types)                |
| [Hillshade](#hillshade)                                                   | `hillshade`   | Relief shading from elevation, optionally tinted               |

## Shared `config` options

| Option             | Notes                                                                                                                                                                                                   |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `band`             | Which band to read (1-indexed). Numeric/hillshade.                                                                                                                                                      |
| `steps`            | Classification — see [Classification methods](/felt-style-language/classification-methods.md). `{"type": "continuous"}` for smooth, or breaks like `[0, 500, 1000]`.                                    |
| `method`           | Spectral-index formula and band mapping (multiband only).                                                                                                                                               |
| `categories`       | Categorical only — `{"type": "all"}` for every pixel value, or an explicit array.                                                                                                                       |
| `noData`           | Value(s) to exclude, e.g. `[-9999]` for voids or `[0]` for no-data.                                                                                                                                     |
| `rasterResampling` | Numeric and tinted hillshade only — categorical rasters reject it (they always resample nearest). `"nearest"` keeps exact pixel values; `"linear"` interpolates (use for continuous imagery/elevation). |
| `outOfRangeValues` | `"color"` clamps to the nearest class color; `"hide"` makes them transparent.                                                                                                                           |

In `paint`, `color` takes a [raster palette](/felt-style-language/colors-and-palettes.md#raster-palettes) or an explicit color array, plus `opacity` and `isSandwiched` (render below basemap water/roads).

## Image (simple)

Renders the image with no classification — just `opacity` and resampling.

```json
{
  "version": "2.3.1",
  "type": "simple",
  "config": {},
  "paint": {"opacity": 0.93, "isSandwiched": false}
}
```

## Numeric — single band

Classify one band. Prefer `continuous` for smooth data (elevation, temperature); use breaks for discrete groups (hazard levels).

```json
{
  "version": "2.3.1",
  "type": "numeric",
  "config": {
    "band": 1,
    "steps": {"type": "continuous"},
    "noData": [-9999],
    "rasterResampling": "linear"
  },
  "paint": {"color": "@mplVirdis", "opacity": 1, "isSandwiched": false},
  "legend": {"displayName": "auto"}
}
```

A style is **classed** when `steps` has more than two break points — the color array then needs one entry per class (`N` break points produce `N − 1` classes; extra colors are ignored). With exactly two steps (`[min, max]`, as below), the style is **continuous**: the whole color array is interpolated smoothly across the range:

```json
{
  "version": "2.3.1",
  "type": "numeric",
  "config": {"band": 1, "steps": [-154.46, 7987.46]},
  "legend": {"displayName": {"0": "-154.46", "1": "7.99K"}},
  "paint": {
    "opacity": 1,
    "isSandwiched": false,
    "color": ["#454b9f", "#2d79a4", "#18a2a9", "#8cc187", "#e5d96c", "#eab459", "#ef8b45", "#e66250", "#db2d5e"]
  }
}
```

## Numeric — multiband (raster algebra)

Compute a derived index from multiple bands before classifying. The `config.method` names the index and maps its inputs to band numbers, and `band` lists every band used. Supported indices:

* **NDVI** — `(NIR − R) / (NIR + R)`, vegetation health. Requires `NIR`, `R`.
* **NDMI** — `(NIR − SWIR) / (NIR + SWIR)`, moisture. Requires `NIR`, `SWIR`.
* **NDWI** — `(G − NIR) / (G + NIR)`, water. Requires `G`, `NIR`.

Band numbers are 1-indexed and sensor-specific (Landsat 8/9: R=4, G=3, NIR=5, SWIR=6 · Sentinel-2: R=4, G=3, NIR=8, SWIR=11).

```json
{
  "version": "2.3.1",
  "type": "numeric",
  "config": {
    "method": {"NDVI": {"NIR": 5, "R": 4}},
    "band": [5, 4],
    "steps": {"type": "continuous"},
    "noData": [0],
    "rasterResampling": "nearest",
    "outOfRangeValues": "hide"
  },
  "paint": {"color": "@cbRedYlGrn", "opacity": 1, "isSandwiched": false},
  "legend": {"displayName": "auto"}
}
```

NDVI output ranges from −1 (water/bare) to 1 (dense vegetation); classify it with explicit breaks (e.g. `[-1, -0.2, 0, 0.2, 0.4, 0.6, 1]`) for labelled classes. Good starting points: NDVI → `[-0.5, 0.5]` with `@cbRedYlGrn`; NDWI → `[-0.5, 0.25]` and NDMI → `[-0.5, 0.5]` with `@feltVibrant`.

## Categorical

Raster categories are **raw pixel values** (integers), so there is no `categoricalAttribute`. Use `{"type": "all"}` when you don't know the values, or an explicit array when you do, with one color per value. Don't set `rasterResampling` here — categorical rasters always use nearest resampling automatically (the property is rejected on this type), since linear interpolation would blend category codes into invalid values.

```json
{
  "version": "2.3.1",
  "type": "categorical",
  "config": {"categories": [1, 2, 3, 4, 5]},
  "paint": {
    "color": ["#228B22", "#4682B4", "#DAA520", "#8B4513", "#808080"],
    "opacity": 0.85,
    "isSandwiched": true
  },
  "legend": {
    "displayName": {"1": "Forest", "2": "Water", "3": "Cropland", "4": "Bare Soil", "5": "Urban"}
  }
}
```

For raster categorical, use the [vector categorical palettes](/felt-style-language/colors-and-palettes.md#categorical-distinct-colors) (`@catPalette1`…`@catPalette7`). A fully-transparent class can be expressed as `"rgba(0, 0, 0, 0)"`.

## Hillshade

Simulates light and shadow on terrain. **Plain** hillshade is grayscale relief (`config` carries just `band`; `paint` carries `source` and `intensity`). **Tinted** hillshade overlays a hypsometric color ramp — add `steps` to `config` and `color` to `paint`; only do this when elevation coloring is wanted.

| Paint property | Default | Notes                                                                         |
| -------------- | ------- | ----------------------------------------------------------------------------- |
| `source`       | 315     | Light azimuth in degrees (0 = North, 90 = East). 315 (northwest) is standard. |
| `intensity`    | 0.5     | Shadow intensity 0–1; higher = more dramatic relief.                          |
| `color`        | —       | Raster palette or color array (tinted only), low → high elevation.            |

```json
{
  "version": "2.3.1",
  "type": "hillshade",
  "config": {"band": 1},
  "paint": {"source": 315, "intensity": 0.5, "isSandwiched": true},
  "legend": {}
}
```

Tinted, classifying elevation like any numeric raster (use `rasterResampling: "linear"` and `noData` for voids):

```json
{
  "version": "2.3.1",
  "type": "hillshade",
  "config": {
    "band": 1,
    "steps": {"type": "quantiles", "count": 6},
    "noData": [-9999],
    "rasterResampling": "linear"
  },
  "paint": {"color": "@terrain", "source": 315, "intensity": 0.5, "isSandwiched": true},
  "legend": {"displayName": "auto"}
}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://developers.felt.com/felt-style-language/types-of-visualizations/raster.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
