Skip to content

218 image analysis and image plotting helper refactor#219

Merged
Jan-Willem merged 81 commits intomainfrom
218-image-analysis-and-image-plotting-helper-refactor
Apr 2, 2026
Merged

218 image analysis and image plotting helper refactor#219
Jan-Willem merged 81 commits intomainfrom
218-image-analysis-and-image-plotting-helper-refactor

Conversation

@dmehring
Copy link
Copy Markdown
Contributor

@dmehring dmehring commented Apr 2, 2026

This PR builds on PR 217. PR 217 should be merged before this one is.

dmehring and others added 3 commits April 2, 2026 14:20
Normalise DataArrays with named x/y dims to (x, y) axis order before
resolving coordinates and labels, so callers using the common astronomy
(y, x) storage layout get correct plot axes and labels.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
WCSAxes derives correct RA/Dec labels from the WCS object itself.
Only call set_axislabel() in the WCS branch when the caller explicitly
passed a string coordinate selector as a custom label.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Non-monotonic world-coordinate values in coord are intentionally
allowed; only the synthetic pixel-index axis must be strictly
increasing. Update the Returns and Notes sections to reflect the
actual preconditions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 11 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

import dask.array as da

from ...utils._gaussian_math import (
FWHM2SIG as _FWHM2SIG,
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_FWHM2SIG is imported from astroviper.utils._gaussian_math but never used in this module. Please drop the unused import (or use it) to avoid lint failures / dead imports.

Suggested change
FWHM2SIG as _FWHM2SIG,

Copilot uses AI. Check for mistakes.
Comment on lines +64 to +75
def _coord_from_spec(spec, dim_name: str, axis_size: int) -> np.ndarray:
if spec is None:
if dim_name in data.coords:
coord_values = np.asarray(data.coords[dim_name].values, dtype=float)
else:
coord_values = np.arange(axis_size, dtype=float)
elif isinstance(spec, str):
if spec not in data.coords:
raise ValueError(f"Coordinate {spec!r} not found in DataArray.")
coord_values = np.asarray(data.coords[spec].values, dtype=float)
else:
coord_values = np.asarray(spec, dtype=float)
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_resolve_plot_coords() coerces DataArray coordinate values to dtype=float even when show_world_axes=False (pixel/imshow mode), which can raise for legitimate non-numeric coords (e.g., datetime64 or string labels) even though those coords are not needed for pixel plotting. Consider only resolving/casting x/y coordinate vectors when show_world_axes=True and wcs is None, or avoid forcing dtype=float unless the coordinates are actually used for pcolormesh/world-axis rendering.

Copilot uses AI. Check for mistakes.
Comment on lines +44 to +56
def make_scene_via_component_models(
nx: int,
ny: int,
components: list[dict],
*,
offset: float = 0.1,
noise_std: float = 0.02,
seed: int | None = None,
coords: bool = True,
angle: str = "math", # "math" | "pa" | "auto" — same semantics as astroviper's model
x_world: tuple[float, float] = (0.0, 1.0),
y_world: tuple[float, float] = (0.0, 1.0),
) -> xr.DataArray:
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make_scene_via_component_models() annotates components as list[dict], but the implementation treats it as either a list-of-dicts (single-channel) or a list-of-lists-of-dicts (multi-channel) via isinstance(components[0], dict). Tightening the type annotation (or simplifying the logic to match the annotation) would make the intended input contract clearer and avoid confusion for benchmark users.

Copilot uses AI. Check for mistakes.
dmehring and others added 6 commits April 2, 2026 15:18
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add need_coords flag to _resolve_plot_coords and set it only for the
pcolormesh (show_world_axes=True, no WCS) branch where coordinate
vectors are actually consumed. Other rendering modes no longer attempt
a float cast on DataArray coordinates, so datetime64 or string labels
no longer raise needlessly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ssian2d_fit.py


Only set envs if run directly

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…ssian2d_fit.py


remove dead code

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…dels

The function accepts both list[dict] (single-channel) and
list[list[dict]] (multi-channel); update the annotation and docstring
to reflect both forms.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 11 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +105 to +116
values = np.asarray(data)
if values.ndim != 2:
raise ValueError(
"data must be a 2D array-like object with shape (nx, ny) for plotting."
)

if not need_coords:
return (
values,
np.arange(values.shape[0], dtype=float),
np.arange(values.shape[1], dtype=float),
)
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_resolve_plot_coords skips validation of x_coords/y_coords when need_coords=False, which means passing a string selector with a NumPy input (or in the WCS branch) is silently ignored instead of raising as documented. Consider validating x_coords/y_coords types up front (or at least rejecting str selectors when data is not an xarray.DataArray) so mistakes don’t slip through depending on show_world_axes/wcs.

Copilot uses AI. Check for mistakes.
dmehring and others added 4 commits April 2, 2026 15:45
String selectors were silently ignored when need_coords=False (imshow
and WCS branches). Now validate them upfront in both the DataArray and
plain-array paths so bad inputs always raise regardless of rendering
mode.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ssian2d_fit.py


remove dead code

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…ssian2d_fit.py


remove dead code

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@dmehring dmehring requested a review from Copilot April 2, 2026 19:51
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 11 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +72 to +81
# Validate string selectors upfront so bad inputs always raise,
# regardless of whether coordinate vectors will be consumed.
for spec, label in ((x_coords, "x"), (y_coords, "y")):
if isinstance(spec, str) and spec not in data.coords:
raise ValueError(f"Coordinate {spec!r} not found in DataArray.")

if not need_coords:
# Coordinate vectors will not be used by the caller; skip the
# DataArray coord lookup and float cast to avoid raising on
# non-numeric coordinates (e.g. datetime64, string labels).
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the wcs plotting branch, need_coords is false and x_coords/y_coords are used only as optional axis-label overrides, but this code still requires the strings to exist in data.coords and raises otherwise. That makes it impossible to use x_coords='ra'/y_coords='dec' as pure labels unless the DataArray happens to expose coords with those exact names. Consider skipping this validation when need_coords=False (or splitting the API into separate x_label/y_label parameters) so WCS label overrides don’t depend on DataArray coordinate presence.

Suggested change
# Validate string selectors upfront so bad inputs always raise,
# regardless of whether coordinate vectors will be consumed.
for spec, label in ((x_coords, "x"), (y_coords, "y")):
if isinstance(spec, str) and spec not in data.coords:
raise ValueError(f"Coordinate {spec!r} not found in DataArray.")
if not need_coords:
# Coordinate vectors will not be used by the caller; skip the
# DataArray coord lookup and float cast to avoid raising on
# non-numeric coordinates (e.g. datetime64, string labels).
if not need_coords:
# Coordinate vectors will not be used by the caller; skip the
# DataArray coord lookup and float cast to avoid raising on
# non-numeric coordinates (e.g. datetime64, string labels) and
# to allow string x_coords / y_coords to be used as label-only
# overrides by callers such as the WCS plotting path.

Copilot uses AI. Check for mistakes.
Comment on lines +78 to +86
if not need_coords:
# Coordinate vectors will not be used by the caller; skip the
# DataArray coord lookup and float cast to avoid raising on
# non-numeric coordinates (e.g. datetime64, string labels).
return (
np.asarray(data.values),
np.arange(nx, dtype=float),
np.arange(ny, dtype=float),
)
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

np.asarray(data.values) forces immediate materialization of the full DataArray (and will compute Dask-backed arrays) even in modes where callers might reasonably expect plotting to accept already-realized NumPy inputs only. Since other parts of this PR explicitly avoid touching .values to preserve laziness, it would be more consistent to use np.asarray(data.data) plus an explicit np.asarray(... ) conversion at the plotting call sites (where materialization is truly required), or document clearly that generate_plot() always realizes the full array for plotting.

Copilot uses AI. Check for mistakes.
dmehring and others added 2 commits April 2, 2026 16:04
…=False

In the WCS and imshow branches, x_coords/y_coords strings are label
overrides and do not require a matching DataArray coordinate. Move the
existence check inside the need_coords=True path where coords are
actually looked up. The pcolormesh path (world axes, no WCS) still
raises on missing coord names.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…boundary

_resolve_plot_coords now returns data.data (raw underlying array) instead
of calling np.asarray(data.values), keeping lazy Dask arrays un-computed
until generate_plot explicitly materializes with np.asarray() at the
single rendering boundary. Document the behaviour in both docstrings.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 11 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +11 to +18
def _resolve_plot_coords(
data,
x_coords: Optional[Union[str, np.ndarray]] = None,
y_coords: Optional[Union[str, np.ndarray]] = None,
*,
need_coords: bool = True,
) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
"""
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_resolve_plot_coords is annotated to return tuple[np.ndarray, np.ndarray, np.ndarray], but in the xarray.DataArray path it returns data.data, which can be a Dask array (or other array-like) rather than a NumPy ndarray. This makes the type hint inaccurate and can trip static type checking; consider widening the first element’s type (e.g., np.ndarray | dask.array.Array or a more general ArrayLike) to match the docstring’s “NumPy or Dask” behavior.

Copilot uses AI. Check for mistakes.
The first element may be a NumPy ndarray or a Dask array depending on
the DataArray's backing storage; Any accurately reflects this while the
docstring explains the contract.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Jan-Willem Jan-Willem merged commit c5b0948 into main Apr 2, 2026
3 of 19 checks passed
e-koch pushed a commit that referenced this pull request Apr 6, 2026
…tting-helper-refactor

218 image analysis and image plotting helper refactor
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Image analysis and image plotting helper refactor

4 participants