> 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 by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

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

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

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.
