Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
a5d1c01
refactor(glyphs): move group-object render logic onto the objects
MAfarrag Aug 11, 2026
72f2e70
refactor(glyphs): move colorbar dispatch and facet-label logic onto t…
MAfarrag Aug 11, 2026
d7ecefd
refactor(glyphs)!: group RGB band-prep params into RgbBands
MAfarrag Aug 11, 2026
0905a1c
refactor(glyphs): move remaining group-driven logic onto the objects
MAfarrag Aug 11, 2026
d47b54b
fix(array_glyph): apply RGB cutoff to each band's data, not the band …
MAfarrag Aug 11, 2026
6421412
test(glyphs): unit tests and doctest examples for the grouped-object …
MAfarrag Aug 11, 2026
88aeb05
docs(colorbar): note the intentional ticks_spacing exclusion in reset…
MAfarrag Aug 11, 2026
b22acfe
docs(array_glyph): correct prepare_array rgb docstring (no [3,2,1] de…
MAfarrag Aug 11, 2026
d9612c6
docs(array_glyph): use a fractional cutoff in the prepare_array example
MAfarrag Aug 11, 2026
310c707
docs(test): update stale _plot_point_values/_prepare_sentinel_rgb ref…
MAfarrag Aug 11, 2026
24671e4
refactor(glyphs): drop the now-dead array_glyph _Unset sentinel
MAfarrag Aug 11, 2026
7d0614e
test(array_glyph): make the prepare_array cutoff test assert real str…
MAfarrag Aug 11, 2026
2ab31de
docs(array_glyph): drop no-op cutoff (no surface_reflectance) from ex…
MAfarrag Aug 11, 2026
c48bf80
fix(array_glyph): raise on RgbBands.prepare with indices=None instead…
MAfarrag Aug 11, 2026
92db3a1
docs(array_glyph): reword RgbBands.validate error to reflect 3+ band …
MAfarrag Aug 11, 2026
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
23 changes: 23 additions & 0 deletions docs/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,29 @@ Only the `DeprecationWarning` that steered you toward `ColorBar` is gone. The ty
`colorbar=ColorBar(...)` form (`cleopatra.styling.colorbar.ColorBar`) is still preferred and wins when both are
given.

### RGB band preparation → `RgbBands`

`ArrayGlyph`'s **constructor** no longer takes the four loose RGB keywords. Bundle them into an `RgbBands`
(importable from `cleopatra.glyphs.gridded.array_glyph`) passed as `rgb_bands=`:

| Old | New |
| --- | --- |
| `ArrayGlyph(arr, rgb=[r, g, b])` | `ArrayGlyph(arr, rgb_bands=RgbBands([r, g, b]))` |
| `ArrayGlyph(arr, rgb=..., surface_reflectance=..., cutoff=..., percentile=...)` | `ArrayGlyph(arr, rgb_bands=RgbBands([...], surface_reflectance=..., cutoff=..., percentile=...))` |

```python
# before
ArrayGlyph(sentinel_2, rgb=[3, 2, 1], surface_reflectance=10000, cutoff=[0.3, 0.3, 0.3])

# after
from cleopatra.glyphs.gridded.array_glyph import RgbBands
ArrayGlyph(sentinel_2, rgb_bands=RgbBands([3, 2, 1], surface_reflectance=10000, cutoff=[0.3, 0.3, 0.3]))
```

The lower-level `ArrayGlyph.prepare_array(...)` and `ArrayGlyph.scale_percentile(...)` utilities are unchanged —
they still accept the loose `rgb` / `surface_reflectance` / `cutoff` / `percentile` keywords (they now build an
`RgbBands` internally).

---

## Subpackage restructure
Expand Down
4 changes: 2 additions & 2 deletions examples/array_plot_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
Config.set_matplotlib_backend()
import matplotlib.pyplot as plt

from cleopatra.glyphs.gridded.array_glyph import ArrayGlyph
from cleopatra.glyphs.gridded.array_glyph import ArrayGlyph, RgbBands

# from matplotlib.transforms import blended_transform_factory
# %% create the glyph from a masked array
Expand All @@ -24,7 +24,7 @@
plt.show()
# %%
# plt.ioff()
array = ArrayGlyph(arr, rgb=[3, 2, 1], cutoff=[0.3, 0.3, 0.3])
array = ArrayGlyph(arr, rgb_bands=RgbBands([3, 2, 1]))
# %%
arr = np.load("tests/data/arr.npy")
exclude_value = arr[0, 0]
Expand Down
11 changes: 6 additions & 5 deletions examples/plot_rgb.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,24 @@

import numpy as np

from cleopatra.glyphs.gridded.array_glyph import ArrayGlyph
from cleopatra.glyphs.gridded.array_glyph import ArrayGlyph, RgbBands

sentinel_2 = np.load("tests/data/s2a.npy")
extent = [34.626902783650785, 34.654007151597256, 31.82337186561403, 31.8504762335605]
# %% process the channels by excluding the top and lowest 1% of the values.
array = ArrayGlyph(sentinel_2, rgb=[3, 2, 1], percentile=1, extent=extent)
array = ArrayGlyph(sentinel_2, rgb_bands=RgbBands([3, 2, 1], percentile=1), extent=extent)
fig, ax = array.plot()
# %% process the channels by the surface reflectance of the sentinel data,
array = ArrayGlyph(sentinel_2, rgb=[3, 2, 1], surface_reflectance=10000)
array = ArrayGlyph(sentinel_2, rgb_bands=RgbBands([3, 2, 1], surface_reflectance=10000))
array.plot()
# %%
array = ArrayGlyph(
sentinel_2, rgb=[3, 2, 1], surface_reflectance=10000, cutoff=[0.3, 0.3, 0.3]
sentinel_2,
rgb_bands=RgbBands([3, 2, 1], surface_reflectance=10000, cutoff=[0.3, 0.3, 0.3]),
)
array.plot()
# %%
sentinel_2 = np.load("tests/data/gaza-20231002.npy")

array = ArrayGlyph(sentinel_2, rgb=[0, 1, 2])
array = ArrayGlyph(sentinel_2, rgb_bands=RgbBands([0, 1, 2]))
array.plot()
55 changes: 35 additions & 20 deletions src/cleopatra/glyphs/base/glyph.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
from __future__ import annotations

import inspect
import math
import os
import warnings
from collections.abc import Iterator
Expand All @@ -26,11 +25,16 @@
from matplotlib.legend import Legend
from matplotlib.patches import Rectangle

from cleopatra.glyphs.base.animation import SUPPORTED_VIDEO_FORMAT # noqa: F401 (re-export)
from cleopatra.glyphs.base.animation import (
SUPPORTED_VIDEO_FORMAT, # noqa: F401 (re-export)
)
from cleopatra.glyphs.base.animation import save_animation as _save_animation
from cleopatra.styling.colors import resolve_colormap
from cleopatra.styling.scaling import MAX_DISCRETE_LEVELS # noqa: F401 (re-export)
from cleopatra.styling.scaling import ColorScaling, levels_to_bounds
from cleopatra.styling.scaling import (
MAX_DISCRETE_LEVELS, # noqa: F401 (re-export)
ColorScaling,
levels_to_bounds,
)
from cleopatra.styling.styles import DEFAULT_OPTIONS as STYLE_DEFAULTS
from cleopatra.styling.styles import (
categorize,
Expand Down Expand Up @@ -594,6 +598,33 @@ def _merge_group_params(self, *groups: Any) -> None:
if key in self.default_options:
self.default_options[key] = val

def _snapshot_group_options(self, *groups: Any) -> dict:
"""Snapshot the current value of every option key `groups` will touch.

Records the pre-merge value of each `default_options` key any of the
given group objects will write (via `to_options()`), so a failed merge
(e.g. an invalid `style` validated afterwards) can restore the WHOLE
set -- not just the key that failed -- keeping a co-passed
`color=`/`contour=`/`cells=` from leaking into a later plain `plot()`
on a sticky-options glyph.

Args:
*groups: The grouped parameter objects about to be merged (each a
`to_options()`-bearing object, or `None` to skip).

Returns:
dict: `{key: current value}` for every key the groups will touch
that exists in `default_options`.
"""
snapshot: dict = {}
for group in groups:
if group is None:
continue
for key in group.to_options():
if key in self.default_options and key not in snapshot:
snapshot[key] = self.default_options[key]
return snapshot

@contextmanager
def _rollback_options_on_error(self) -> Iterator[None]:
"""Restore `default_options` if the wrapped render body raises.
Expand Down Expand Up @@ -1700,22 +1731,6 @@ def adjust_ticks(
else:
self.ax.get_yaxis().set_visible(visible)

@staticmethod
def _plot_point_values(
ax, point_table: np.ndarray, point_label_color, point_label_size
):
"""Plot point value labels on the axes."""
write_points = lambda x: ax.text(
x[2],
x[1],
x[0],
ha="center",
va="center",
color=point_label_color,
fontsize=point_label_size,
)
return list(map(write_points, point_table))

def save_animation(self, path: str | os.PathLike, fps: int = 2, **kwargs) -> None:
"""Save this glyph's animation (`self.anim`) to a file.

Expand Down
Loading