---
title: Legend
source: https://infrared.city/docs/sdk/1.0/python/legend/
---

# Legend

Legend (colour-scale) ranges for grid results.

An `AreaResult` / `TiledResult` already carries the default range on `min_legend` / `max_legend`: the EXACT min/max measured over the finished merged and clipped grid. This module is for the other display modes an app may want, computed on demand: your display picks the mode, the SDK computes the range.

Modes

`"exact"` The true min/max. What `min_legend` / `max_legend` already hold; pass it here only to recompute against a different grid (e.g. one array out of a `shared_legend_range` comparison). `"trimmed"` The 2nd/98th percentile (exact, numpy's linear interpolation): an outlier-robust range for display when a few extreme cells would otherwise wash out the colour scale. `"fixed"` The metric's full physical scale, ignoring the data; pass `fixed=`. `registry_fixed_range` The metric's registry-defined scale (`visualConfigurations`), when the catalogue defines one.

`shared_legend_range` pools several results onto ONE scale, for comparing scenarios side by side rather than each on its own auto-range.

Notes

Categorical results (e.g. wind-comfort classes) have no numeric range: the grid holds class codes, not measurements. A result that carries a class list (`legend`, as the merge builds it) gets `None` from the measured modes here, and `shared_legend_range` leaves it out of the pool, just as its own `min_legend` / `max_legend` are `None`. A raw numpy array carries no such marker, so ranging over a class-code array is your choice, and so is a result saved with `to_dict()` before `legend` existed.

## LegendRange  `module-attribute`

```python
LegendRange = Tuple[float, float]
```

A legend range as `(minimum, maximum)`.

## legend_range

```python
legend_range(
result: Any, mode: str = "exact", *, fixed: Optional[LegendRange] = None
) -> Optional[LegendRange]
```

Return the legend range for one result, in the given display `mode`.

Parameters:

| Name | Type | Description | Default |
| --- | --- | --- | --- |
| `result` | `AreaResult, TiledResult, or numpy.ndarray` | The grid to range over. `AreaResult`/`TiledResult` contribute their `merged_grid`. | *required* |
| `mode` | `('exact', 'trimmed', 'fixed')` | See the module docstring. Default `"exact"`. | `"exact"` |
| `fixed` | `(float, float)` | Required when `mode="fixed"`, and refused with `ValueError` for any other mode. | `None` |

Returns:

| Type | Description |
| --- | --- |
| `(float, float) or None` | `None` when the grid is empty, no finite value is found, or (measured modes) the result is categorical. |

Raises:

| Type | Description |
| --- | --- |
| `ValueError` | If `mode` is not one of the three modes, or `fixed` is missing for `mode="fixed"` or given for any other mode. |
| `TypeError` | If `result` is not an `AreaResult`, a `TiledResult` or a numpy array. |

## shared_legend_range

```python
shared_legend_range(
results: Sequence[Any],
mode: str = "exact",
*,
fixed: Optional[LegendRange] = None,
) -> Optional[LegendRange]
```

One pooled legend range over several results — a shared scale for comparing scenarios, instead of each result auto-ranging on its own.

Parameters:

| Name | Type | Description | Default |
| --- | --- | --- | --- |
| `results` | `sequence of AreaResult, TiledResult, or numpy.ndarray` | Mixed types are fine; each contributes its grid. Mixed dtypes are pooled as float64 (a lone float32 array is not narrowed to save a copy across a mixed set the way `legend_range` does for one). | *required* |
| `mode` | `str` | See `legend_range`. | `'exact'` |
| `fixed` | `str` | See `legend_range`. | `'exact'` |

Returns:

| Type | Description |
| --- | --- |
| `(float, float) or None` | `None` when every grid is empty or no finite value is found. In the measured modes a categorical result is left out of the pool. |

Raises:

| Type | Description |
| --- | --- |
| `ValueError` | If `mode` is not one of the three modes, or `fixed` is missing for `mode="fixed"` or given for any other mode. |
| `TypeError` | If an item of `results` is not an `AreaResult`, a `TiledResult` or a numpy array. |

## registry_fixed_range

```python
registry_fixed_range(
analysis_type: str, variant: Optional[str] = None
) -> Optional[LegendRange]
```

Return the metric's full scale from the public colour registry, if any.

Reads the same TTL-cached `visualConfigurations` document `infrared_sdk.layers._registry` already fetches for local grid rendering — no second fetch, no second cache.

Parameters:

| Name | Type | Description | Default |
| --- | --- | --- | --- |
| `analysis_type` | `str` | The registry's `process_id` (e.g. `"wind-speed"`, `"thermal-comfort-index"`). | *required* |
| `variant` | `str` | A criteria/subtype key for a multi-variant analysis type. | `None` |

Returns:

| Type | Description |
| --- | --- |
| `(float, float) or None` | `None` when the registry has no entry for `analysis_type`, or the entry's steps are categorical (string) or absent, which is the case for many analyses today. |
