gelsa.esa.line_map_studio#

Stand-alone GUI for publication-quality Euclid emission line map figures.

Everything the figure needs is driven from the widgets: the archive login, the MER stamps, the SIR frames, the stacked line map, the colour transform and the export. A notebook only has to open it:

from gelsa.esa import line_map_studio
studio = line_map_studio.make_line_map_studio()
studio

A target is either typed in as RA, Dec and redshift, or picked from a targets.txt file with columns name,ra_deg,dec_deg,redshift. Where the line map is centred can be given either as a redshift plus a rest-frame line, or directly as any observed wavelength, which is what the stacker actually uses. Only the SIR frames whose grism sensitivity range covers that wavelength are stacked, so the blue or red grism is picked to suit it.

Wavelengths are in nanometres throughout the interface, the labels and the FITS header, and converted to the Angstrom that gelsa works in at the call.

The line map is coloured with gelsa.colorize: colorize.colorize traces each bright continuum pixel along its dispersion direction and paints the trace with a wavelength colour map, so the hue at a point says which wavelength the light landing there came from, and colorize.spectro_composite uses the stacked line flux as the brightness under that hue. This is the recipe of colorize/dinosaur_zoom.ipynb.

Functions

compute_continuum_rgb(stamps, wcs, transform)

Run the azulero IYJH -> RGB transform and return a float RGB image.

make_line_map_studio([targets_file, ...])

Build the studio GUI.

number(description, value, vmin, vmax, step)

A bounded number box for a figure parameter.

read_targets(path)

Read a name,ra_deg,dec_deg,redshift target list.

trace_sweep(frame, wcs, fixed_angstrom[, ...])

How far a frame's dispersion sweeps across the field.

Classes

LineMapStudio([targets_file, ...])

Widget front end for the whole line map figure pipeline.

OutputHandler(output[, level])

Write log records into an ipywidgets.Output box.

Quiet([name, level])

Hold a noisy logger down while a routine that spams it runs.

class gelsa.esa.line_map_studio.OutputHandler(output, level=20)[source]#

Bases: Handler

Write log records into an ipywidgets.Output box.

Appending to the widget directly, rather than printing inside its context manager, shows the messages whichever callback logged them.

emit(record)[source]#

Do whatever it takes to actually log the specified logging record.

This version is intended to be implemented by subclasses and so raises a NotImplementedError.

class gelsa.esa.line_map_studio.Quiet(name='azulero', level=40)[source]#

Bases: object

Hold a noisy logger down while a routine that spams it runs.

gelsa.esa.line_map_studio.number(description, value, vmin, vmax, step, tooltip='', width='215px', label_width='118px')[source]#

A bounded number box for a figure parameter.

Typed in directly, and stepped by step with the arrow keys or the spinner, which is how a nearly-right figure gets nudged the last bit.

gelsa.esa.line_map_studio.trace_sweep(frame, wcs, fixed_angstrom, nsamples=200)[source]#

How far a frame’s dispersion sweeps across the field.

colorize paints a pixel by walking its trace across the frame’s whole sensitivity range and mapping each wavelength back to the sky at the fixed wavelength. What makes a frame useful is how far that walk travels: a trace crossing the cut-out paints a rainbow over it, while one that stays put paints a single spot and leaves the line map grey.

Counting how many samples land inside the field does not measure that, and in fact prefers the wrong frames – some map every wavelength back to the same pixel, so all their samples are “inside” while the trace goes nowhere. The score here is instead the extent the in-field part of the trace covers as a fraction of the field, which is 0 both for a frame that misses the target and for one that collapses to a point.

This is the walk colorize does, run once for the field centre, so a usable frame can be picked before spending a minute on the full trace.

gelsa.esa.line_map_studio.read_targets(path)[source]#

Read a name,ra_deg,dec_deg,redshift target list.

Blank lines and lines starting with # are skipped, as is a header row. Whitespace-separated files are accepted as well as comma-separated ones, and the redshift column may be missing.

Returns:

One {'name', 'ra', 'dec', 'redshift'} per target.

Return type:

list of dict

gelsa.esa.line_map_studio.compute_continuum_rgb(stamps, wcs, transform)[source]#

Run the azulero IYJH -> RGB transform and return a float RGB image.

process_iyjh works in place and returns BGR, so the input is copied and the channels flipped here.

class gelsa.esa.line_map_studio.LineMapStudio(targets_file='targets.txt', credentials_file='/media/home/my_workspace/password', config_file=None, calibdir=None, outdir='.', Euclid=None, G=None)[source]#

Bases: object

Widget front end for the whole line map figure pipeline.

Parameters:
  • targets_file (str) – Target list read by the Targets dropdown.

  • credentials_file (str) – Default for the archive login, editable in the GUI.

  • config_file (str, optional) – Defaults for the gelsa calibration, editable in the GUI. Left empty, gelsa uses its built-in configuration and finds the calibration directory in datalabs, as gelsa.Gelsa does.

  • calibdir (str, optional) – Defaults for the gelsa calibration, editable in the GUI. Left empty, gelsa uses its built-in configuration and finds the calibration directory in datalabs, as gelsa.Gelsa does.

  • outdir (str) – Where Save figure and Save settings write.

  • Euclid (astroquery.esa.euclid.core.EuclidClass, optional) – An already logged-in archive connection, if the notebook made one.

  • G (gelsa.Gelsa, optional) – An already configured Gelsa instance.

continuum_subtraction()[source]#

The continuum subtraction to hand to Analysis.crop.

MedianFilterCutout filters each extracted cutout, which is what the demo notebook uses; MedianFilterFrame filters the whole frame instead, which is slower but sees more of the trace. None leaves the continuum in, so the map is of the total flux at that wavelength.

observed_wavelength()[source]#

The wavelength the line map is centred on, in nm.

hue_range_angstrom()[source]#

The wavelengths the colour map spans, in Angstrom, or None.

None means the trace frame’s own sensitivity range is used, which is what colorize does by default. Pinning it lets targets traced from different frames – blue grism at one redshift, red grism at another – be read against one scale. It changes only how the colour map is normalized: the trace still walks the wavelengths the frame really disperses.

observed_angstrom()[source]#

The same wavelength in Angstrom, which is what gelsa takes.

connect()[source]#

Log in to the archive and configure gelsa. Raises on failure.

An Euclid or G handed to the constructor is reused, so a batch run pays for the login and the calibration load only once.

load_target()[source]#

Retrieve stamps, SIR frames and crops, then stack. Raises on failure.

seed(stamps, wcs, analysis, ra, dec, redshift=None, rest_line='Ha 656.5', observed_nm=None, line_map=None, build_hue=True)[source]#

Take over data a notebook has already built, instead of loading it.

This leaves the studio where load_target would: the stamps, the crops and a trace frame are in place, so the line map can be restacked and the hue map built from the GUI straight away. Load target still works for a new target once the archive is connected.

Parameters:
  • stamps (numpy.ndarray) – (4, ny, nx) stack of VIS, NIR_Y, NIR_J, NIR_H stamps.

  • wcs (astropy.wcs.WCS) – WCS of the stamps; the line map is stacked on it.

  • analysis (gelsa.analysis.Analysis) – With its crops already made by Analysis.crop. Its Gelsa instance is reused, and its SIR frames are the trace candidates.

  • ra (float) – Target position in degrees.

  • dec (float) – Target position in degrees.

  • redshift (float, optional) – Centres the line map on rest_line at this redshift.

  • rest_line (str) – Key of LINE_LIST.

  • observed_nm (float, optional) – Centre on this observed wavelength instead of a redshifted line.

  • line_map (tuple, optional) – (elmap, var, norm, count) from build_stacked_line_map at that wavelength. Stacked here if not given.

  • build_hue (bool) – Trace the hue map straight away, as Load target does, so the rainbow line map shows without pressing Build hue map.

choose_trace_frame(max_tries=12, good=0.25)[source]#

Pick a frame whose dispersion sweeps the field, and open it.

Frames whose sensitivity range covers the observed wavelength are tried first, those of the preferred grism ahead of the rest, and the first one that sweeps at least good of the field is kept. The grism matters beyond geometry: colorize spreads the colour map over the trace frame’s whole sensitivity range, so a red-grism frame would give this target a 1198-1891 nm hue scale while its neighbours run 924-1368 nm, and the two could not be compared. Only if nothing sweeps the field does the best-scoring candidate win, and the caller is told the score is poor.

Returns the sweep of the frame it settled on, as a fraction of the field.

set_trace_frame(index)[source]#

Open a particular frame from the list as the trace frame.

An index only means anything against the frame list it was read from, so pinning one is for re-tracing an existing target, not for a fresh retrieval where the query may come back in a different order.

Returns the sweep of that frame, for reporting.

stack_line_map()[source]#

Stack the line map at the current wavelength. Raises on failure.

Reuses the crops, so this is the cheap way to move the centre wavelength once a target is loaded.

step_wavelength(direction)[source]#

Move the observed wavelength by one step, then restack and redraw.

direction is +1 or -1. The centre switches to the observed wavelength mode, starting from wherever the redshift put it, so the steps do not depend on the rest line.

build_hue_map()[source]#

Trace the dispersion of every bright continuum pixel into a hue map.

This is colorize.colorize: hue encodes the wavelength whose light would land at that point, which is what turns a flat line map into the rainbow image. Raises on failure.

transform()[source]#

Return the azulero.image.color.Transform the sliders describe.

continuum_rgb()[source]#

The continuum RGB image for the current transform, cached.

line_rgb()[source]#

The coloured line map: rainbow hue from colorize, or a single tint.

available_panels()[source]#

The ticked panels that actually have their data, in figure order.

figure(panels=None)[source]#

Build the matplotlib figure for the current settings.

Parameters:

panels (list of str, optional) – Which panels to draw. Defaults to the ones ticked in the Layout tab; passing a single name renders that panel on its own, which is how the per-panel export works.

refresh(_=None)[source]#

Re-render the figure into the image widget.

apply_transform(transform)[source]#

Load a azulero.image.color.Transform into the colour boxes.

The boxes are filled with the redraw suspended, so the figure is rebuilt once at the end rather than on every box.

params()[source]#

Every GUI setting as a plain dict, for saving or reporting.

write_figure(path, panels=None)[source]#

Render panels and write them to path at the export settings.

save_figures()[source]#

Write the figure, combined or one file per panel, and return the paths.

Raises on failure, so a script can tell that nothing was written.

apply_params(saved)[source]#

Load a settings dict as written by params().

Unknown keys are ignored, so a file from an older version still loads, and a hand-edited file missing whole blocks leaves those settings at whatever they are now.

gelsa.esa.line_map_studio.make_line_map_studio(targets_file='targets.txt', credentials_file='/media/home/my_workspace/password', config_file=None, calibdir=None, outdir='.', Euclid=None, G=None)[source]#

Build the studio GUI. See LineMapStudio for the arguments.