Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions autoarray/config/visualize/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,52 @@ The `config` folder contains configuration files which customize default **PyAut
- `mat_wrap.yaml`: Specify the default matplotlib settings when figures and subplots are plotted.
- `mat_wrap_1d.yaml`: Specify the default matplotlib settings when 1D figures and subplots are plotted.
- `mat_wrap_2d.yaml`: Specify the default matplotlib settings when 2D figures and subplots are plotted.

# Changing the colormap

Every 2D figure — imaging data, fits, residual maps, inversion reconstructions —
draws with the colormap named by the `colormap` key of `general.yaml`:

```yaml
colormap: autoarray # any matplotlib colormap name, e.g. magma, viridis, inferno
```

`autoarray` is the colormap bundled with **PyAutoArray**; any other value is
looked up in matplotlib, so `magma`, `viridis`, `inferno`, `plasma`, `jet` and
the rest of `list(matplotlib.colormaps)` all work. Editing this one key is
enough — no plotting code needs changing.

A name matplotlib does not recognise (a typo, say) raises a `ValueError` naming
the key and the offending value. It is **not** silently swapped back for the
default, so a colormap setting never goes quietly ignored.

## One figure at a time

To override the colormap for a single figure without touching config, pass
`colormap=` to any plot function:

```python
import autoarray.plot as aplt

aplt.plot_array(array=image, colormap="magma")
aplt.plot_inversion_reconstruction(pixel_values=values, mapper=mapper, colormap="viridis")
```

The same argument exists on the **PyAutoGalaxy** and **PyAutoLens** plot
functions (`subplot_fit`, `plot_tracer`, `subplot_sensitivity`, …), which pass
it straight through to **PyAutoArray**. Its "use the config value" default is
spelled `None` in **PyAutoArray** and **PyAutoLens**, and `"default"` in
**PyAutoGalaxy**; both mean the same thing.

## Figures that deliberately ignore the setting

A few figures fix their colormap because the colormap carries meaning that a
user preference should not override:

- The `array_overlay` of `plot_array` uses `Greys`, so the overlaid array stays
legible on top of whatever colormap the main array is drawn in.
- The weak-lensing figures in **PyAutoLens** use `twilight` for position angles
(cyclic data needs a cyclic colormap) and `RdBu_r` for residuals (diverging
data needs a diverging colormap centred on zero).
- The cluster figures in **PyAutoLens** use `gnuplot2`, and the interactive GUI
tools use `jet`, to keep faint features visible while masks are drawn by hand.
115 changes: 107 additions & 8 deletions autoarray/plot/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -901,34 +901,133 @@ def hide_unused_axes(axes) -> None:
ax.axis("off")


#: Config key holding the default colormap, quoted in error messages.
_COLORMAP_CONF_KEY = "visualize/general.yaml -> colormap"


def _default_colormap() -> str:
"""Return the colormap name from config, registering the custom one if needed."""
"""Return the default colormap name for 2D figures.

The name is read from the ``colormap`` key of ``visualize/general.yaml``.
The two failure modes are deliberately kept apart:

- **No config at all** (``autonerves`` not installed, or no ``colormap``
key on the config path — e.g. a bare install with no workspace) falls
back quietly to the bundled ``"autoarray"`` colormap. Nothing is
misconfigured, so nothing is said.
- **A config value that matplotlib does not recognise** raises
``ValueError``. A typo'd colormap name used to revert silently to
``"autoarray"``, so the user never learned their setting was ignored.

Returns
-------
str
A colormap name matplotlib can resolve — either ``"autoarray"`` (the
bundled colormap, registered here on first use) or a matplotlib name.

Raises
------
ValueError
If the ``colormap`` config key is set to something that is not a
registered matplotlib colormap.
"""
try:
from autonerves import conf
name = conf.instance["visualize"]["general"]["colormap"]
except Exception:
from autonerves.exc import ConfigException
except ImportError:
name = "autoarray"
else:
try:
name = conf.instance["visualize"]["general"]["colormap"]
except (KeyError, ConfigException):
name = "autoarray"

if name == "autoarray":
from autoarray.plot.segmentdata import register

register()
return name

_validate_colormap(name)

return name


def _validate_colormap(name) -> None:
"""Raise ``ValueError`` unless *name* is a colormap matplotlib knows about.

Parameters
----------
name
The colormap name read from config (or passed by the user).

Raises
------
ValueError
If *name* is not a string, or is not a registered matplotlib colormap.
"""
import matplotlib

if isinstance(name, str) and name in matplotlib.colormaps:
return

raise ValueError(
f"Unknown colormap {name!r}.\n\n"
f"The config key `{_COLORMAP_CONF_KEY}` is set to {name!r}, which is "
f"not a colormap matplotlib recognises, so no figure can be drawn "
f"with it.\n\n"
f"Use either `autoarray` (the colormap bundled with PyAutoArray) or "
f"any matplotlib colormap name, for example `magma`, `viridis`, "
f"`inferno`, `plasma` or `jet`.\n"
f"The full list is `list(matplotlib.colormaps)`."
)


def _conf_imshow_origin() -> str:
"""Return the imshow origin from config (``"upper"`` or ``"lower"``)."""
"""Return the imshow origin from config (``"upper"`` or ``"lower"``).

An absent config falls back quietly to ``"upper"``; a value matplotlib's
``imshow`` would reject raises ``ValueError`` rather than being silently
swapped for the default (same contract as :func:`_default_colormap`).
"""
try:
from autonerves import conf
return conf.instance["visualize"]["general"]["general"]["imshow_origin"]
except Exception:
from autonerves.exc import ConfigException
except ImportError:
return "upper"

try:
origin = conf.instance["visualize"]["general"]["general"]["imshow_origin"]
except (KeyError, ConfigException):
return "upper"

if origin not in ("upper", "lower"):
raise ValueError(
f"Invalid imshow origin {origin!r}.\n\n"
f"The config key `visualize/general.yaml -> general -> "
f"imshow_origin` must be either `upper` or `lower`."
)

return origin


def _conf_output_format() -> str:
"""Return the default output_format from config (``"show"``, ``"png"``, etc.)."""
"""Return the default output_format from config (``"show"``, ``"png"``, etc.).

An absent config falls back quietly to ``"show"``. The value itself is not
validated here — an unsupported format surfaces as matplotlib's own
``savefig`` error, which already names the offending format and lists the
supported ones.
"""
try:
from autonerves import conf
from autonerves.exc import ConfigException
except ImportError:
return "show"

try:
return conf.instance["visualize"]["general"]["general"]["output_format"]
except Exception:
except (KeyError, ConfigException):
return "show"


Expand Down
Loading
Loading