coconut_tools.play_with_the_frame

Synoptic magnetogram utilities (header/filename parsing + aligned plotting).

This module provides small, focused helpers to:

  • Read longitude/latitude world coordinates directly from a FITS header (no third‑party WCS objects required).

  • Detect unit quirks in synoptic products (e.g., micro‑degrees in some GONG files) and convert to degrees when appropriate.

  • Infer the starting Carrington longitude from either header keywords or from GONG‑style filenames, then align a synoptic map so the left edge lands at 0° Carrington.

  • Produce a diagnostic plot that clearly reports which longitude was used and why (header vs filename).

The public entry point is plot_synoptic_aligned(), which returns the (fig, ax, info) triple for further customization.

Notes

  • The FITS header is accessed directly; the formulas follow the simple linear world‑coordinate relation:

    world = CRVALn + (i - CRPIXn) * CDELTn,  with i starting at 1
    
  • Longitude wrapping uses the [0, 360) convention.

  • Latitude is returned in degrees. If the header encodes sine‑latitude (CSLT), it is converted to degrees.

Examples

Basic usage:

>>> from coconut_tools.solarmach_plot import plot_synoptic_aligned
>>> fig, ax, info = plot_synoptic_aligned("/path/to/file.fits", vmin=-100, vmax=100)
>>> info["lon0_used"]
123.0

Functions

detect_instrument(h[, fname])

Detect the likely instrument/vendor from header or filename.

fits_latitude_axis(header[, data, output, ...])

Build a latitude axis from FITS WCS keywords.

fits_longitude_deg(header, *[, one_based_fits])

Compute the x-axis longitude vector in degrees from a FITS header.

is_carrington_longitude(header[, fname])

Heuristically decide whether the x axis is Carrington longitude.

order_carrington_0_360(lon_native, data, *)

Reorder a Carrington map so longitude runs from 0 to 360 degrees.

plot_synoptic_aligned(fits_path[, ...])

Plot a synoptic map aligned so the left edge is 0° Carrington.

plot_synoptic_imshow(data, lon, lat, header, *)

Plot a synoptic magnetogram with imshow using lon/lat vectors.

read_synoptic_fits(fits_path)

Read a synoptic FITS file and return the first valid 2D image HDU.

start_lon_from_filename(fname)

Extract starting Carrington longitude from a GONG-style filename.

coconut_tools.play_with_the_frame.detect_instrument(h, fname='')[source]

Detect the likely instrument/vendor from header or filename.

Parameters:
  • h (collections.abc.Mapping) – FITS header.

  • fname (str, optional) – Filename used as a fallback hint.

Returns:

"HMI", "GONG", or "UNKNOWN".

Return type:

str

coconut_tools.play_with_the_frame.fits_latitude_axis(header, data=None, *, output='deg', one_based_fits=True, force_degrees=False, force_sine=False)[source]

Build a latitude axis from FITS WCS keywords.

Supports degrees-latitude and sine-latitude conventions. If the output latitude is descending, the axis is flipped to be increasing and the data are flipped consistently.

Parameters:
  • header (collections.abc.Mapping) – FITS header.

  • data (np.ndarray | np.ma.MaskedArray | None, optional) – 2D data array with shape (..., ny, nx) or (ny, nx). If provided, it will be flipped when the latitude axis is reversed.

  • output (str, optional) – "deg" or "sin" output axis.

  • one_based_fits (bool, optional) – FITS CRPIX convention is 1-based.

  • force_degrees (bool, optional) – Force interpretation as degrees.

  • force_sine (bool, optional) – Force interpretation as sine-latitude.

Returns:

(lat_out, data_out, meta) where lat_out has shape (ny,), data_out is flipped if needed, and meta contains diagnostics such as detected_mode, flipped, and the WCS keywords used.

Return type:

tuple[np.ndarray, np.ndarray | np.ma.MaskedArray | None, dict]

Raises:
  • KeyError – If required WCS keywords are missing from the header.

  • ValueError – If output is not "deg" or "sin" or if both

  • force_degrees` and force_sine are True

coconut_tools.play_with_the_frame.fits_longitude_deg(header, *, one_based_fits=True)[source]

Compute the x-axis longitude vector in degrees from a FITS header.

Parameters:
  • header (collections.abc.Mapping) – FITS header (hdul[0].header).

  • one_based_fits (bool, optional) – FITS convention uses 1-based pixel coordinates for CRPIX.

Returns:

(x_deg, meta) where x_deg has shape (NAXIS1,) and meta contains diagnostics on units and scaling.

Return type:

tuple[np.ndarray, dict]

Raises:

KeyError – If required WCS keywords are missing from the header.

coconut_tools.play_with_the_frame.is_carrington_longitude(header, fname='')[source]

Heuristically decide whether the x axis is Carrington longitude.

Parameters:
  • header (collections.abc.Mapping) – FITS header.

  • fname (str, optional) – Filename used as a weak hint.

Returns:

(is_carrington, info) where info contains a reason string plus the relevant header fields.

Return type:

tuple[bool, dict]

coconut_tools.play_with_the_frame.order_carrington_0_360(lon_native, data, *, tol=1e-06)[source]

Reorder a Carrington map so longitude runs from 0 to 360 degrees.

If the native longitude axis is decreasing (common for HMI synoptic maps), apply the physical transform phi = (360 - lon) mod 360.

Parameters:
  • lon_native (np.ndarray) – Native Carrington longitudes, shape (nx,).

  • data (np.ndarray | np.ma.MaskedArray) – Magnetogram data, shape (ny, nx).

  • tol (float, optional) – Tolerance used to stabilize roll detection.

Returns:

(lon_out, data_out) with lon_out in [0, 360).

Return type:

tuple[np.ndarray, np.ndarray | np.ma.MaskedArray]

coconut_tools.play_with_the_frame.plot_synoptic_aligned(fits_path, prefer_filename_for_gong=True, vmin=None, vmax=None, cmap='RdBu_r', force_degrees=False, force_sine=False)[source]

Plot a synoptic map aligned so the left edge is 0° Carrington.

Workflow:
  1. Read data and header from fits_path.

  2. Build the native longitude axis and infer a start longitude from the header and, for GONG files, the filename.

  3. Choose which start longitude to use (header by default; filename for GONG if prefer_filename_for_gong=True).

  4. Shift/wrap columns so the left edge is at 0°, and build a latitude axis in degrees.

  5. Render a diagnostic plot and return (fig, ax, info).

Parameters:
  • fits_path (str | pathlib.Path) – Path to the synoptic FITS file.

  • prefer_filename_for_gong (bool, optional) – If True, prefer the filename-encoded longitude for GONG products while still reporting both header and filename values.

  • vmin (float | None, optional) – Color scaling lower bound.

  • vmax (float | None, optional) – Color scaling upper bound.

  • cmap (str, optional) – Matplotlib colormap name.

  • force_degrees (bool, optional) – Force latitude axis to be degrees.

  • force_sine (bool, optional) – Force latitude axis to be sine-latitude.

Returns:

(fig, ax, info) where info includes diagnostic keys such as instrument, cdelt1_deg, lon0_header, lon0_filename, and lon0_used.

Return type:

tuple[matplotlib.figure.Figure, matplotlib.axes.Axes, dict]

Raises:

RuntimeError – If the map is not in Carrington longitude.

Notes

  • The x-extent is set to [0, 360] after reordering by longitude.

  • Missing/invalid data are masked with np.ma.masked_invalid.

coconut_tools.play_with_the_frame.plot_synoptic_imshow(data, lon, lat, header, *, inst='UNKNOWN', lon_meta=None, lat_mode='unknown', lon0_file=None, lon0_used=None, vmin=None, vmax=None, cmap='RdBu_r')[source]

Plot a synoptic magnetogram with imshow using lon/lat vectors.

Assumes lon and lat are 1D vectors matching data columns/rows, the data are ordered so that lon is increasing in [0, 360), and lat is increasing (south to north).

Parameters:
  • data (np.ndarray | np.ma.MaskedArray) – Magnetogram data, shape (ny, nx).

  • lon (np.ndarray) – Longitudes in degrees, shape (nx,).

  • lat (np.ndarray) – Latitudes in degrees, shape (ny,).

  • header (collections.abc.Mapping) – FITS header.

  • inst (str, optional) – Instrument label.

  • lon_meta (dict | None, optional) – Metadata from fits_longitude_deg.

  • lat_mode (str, optional) – Latitude decoding mode string.

  • lon0_file (float | None, optional) – Filename-encoded starting longitude.

  • lon0_used (float | None, optional) – Longitude used for alignment.

  • vmin (float | None, optional) – imshow vmin.

  • vmax (float | None, optional) – imshow vmax.

  • cmap (str, optional) – Matplotlib colormap name.

Returns:

(fig, ax, info) with diagnostic information.

Return type:

tuple[matplotlib.figure.Figure, matplotlib.axes.Axes, dict]

coconut_tools.play_with_the_frame.read_synoptic_fits(fits_path)[source]

Read a synoptic FITS file and return the first valid 2D image HDU.

Parameters:

fits_path (str | pathlib.Path) – Path to the FITS file.

Returns:

(header, data, hdu_index) where data is a 2D float array.

Return type:

tuple[collections.abc.Mapping, np.ndarray, int]

Raises:

RuntimeError – If no valid 2D image HDU is found.

coconut_tools.play_with_the_frame.start_lon_from_filename(fname)[source]

Extract starting Carrington longitude from a GONG-style filename.

The function parses filenames like mrzqs170404t1814c2189_268.fits.gz and returns the trailing 268 value.

Parameters:

fname (str) – Input filename.

Returns:

Parsed degree value if found, otherwise None.

Return type:

int | None