@@ -146,6 +146,97 @@ def subplot_imaging_dataset(
146146
147147
148148
149+ def _intro_panel (array , radius = None , excluded_pixels = None ):
150+ """Prepare a display-only crop with excluded values missing, not zeroed.
151+
152+ Preserve the native pixel/coordinate registration; fitting arrays are never
153+ mutated. Coordinates, ticks and colour mapping remain the renderer's job.
154+ """
155+ import numpy as np
156+ from autoarray .plot .utils import zoom_array
157+
158+ if radius is not None and (not np .isfinite (radius ) or radius <= 0 ):
159+ raise ValueError ("radius must be a finite positive number" )
160+ if radius is None and excluded_pixels is None :
161+ array = zoom_array (array )
162+ values = np .array (array .native .array , dtype = float , copy = True )
163+ values [np .asarray (array .mask .array , dtype = bool )] = np .nan
164+ if excluded_pixels is not None :
165+ excluded = np .asarray (excluded_pixels , dtype = bool )
166+ if excluded .shape != values .shape :
167+ raise ValueError ("excluded_pixels must have the dataset image shape" )
168+ values [excluded ] = np .nan
169+ xmin , xmax , ymin , ymax = array .geometry .extent
170+ if radius is not None :
171+ ny , nx = values .shape
172+ dx , dy = (xmax - xmin ) / nx , (ymax - ymin ) / ny
173+ x = xmin + (np .arange (nx ) + 0.5 ) * dx
174+ y = ymax - (np .arange (ny ) + 0.5 ) * dy
175+ rows , cols = np .flatnonzero (abs (y ) <= radius ), np .flatnonzero (abs (x ) <= radius )
176+ if not len (rows ) or not len (cols ):
177+ raise ValueError ("radius does not include any pixel centres" )
178+ r0 , r1 , c0 , c1 = rows [0 ], rows [- 1 ] + 1 , cols [0 ], cols [- 1 ] + 1
179+ values = values [r0 :r1 , c0 :c1 ]
180+ xmin , xmax , ymin , ymax = xmin + c0 * dx , xmin + c1 * dx , ymax - r1 * dy , ymax - r0 * dy
181+ return values , (xmin , xmax , ymin , ymax )
182+
183+
184+ def subplot_imaging_dataset_intro (
185+ dataset ,
186+ output_path : Optional [str ] = None ,
187+ output_filename : str = "dataset_intro" ,
188+ output_format : str = None ,
189+ colormap = None ,
190+ stretch : Optional [float ] = 0.01 ,
191+ radius : Optional [float ] = None ,
192+ excluded_pixels = None ,
193+ ):
194+ """Four-panel introduction: Image, Noise map, Signal-to-noise map and PSF.
195+
196+ Uses the ordinary plot renderer and configured colormap, fonts, titles,
197+ arcsecond ticks, colourbars and saving. Unlike the diagnostic 3x3 subplot,
198+ it omits duplicate log panels and over-sampling maps. Numerical sampling
199+ and the dataset itself are unchanged.
200+
201+ ``stretch`` is the image's asinh linear-width fraction: smaller values
202+ reveal faint structure next to bright light; None selects linear display.
203+ The S/N and PSF panels use asinh fractions 0.1 and 0.003, respectively;
204+ noise remains linear. All colourbars retain original measurement values.
205+ ``colormap=None`` inherits configuration on every panel.
206+
207+ ``radius`` optionally crops the first three panels about (0, 0), in
208+ arcseconds, for display only. None follows the usual mask-zoom convention.
209+ ``excluded_pixels`` is a native-shaped boolean array (True = exclude) for
210+ deliberately down-weighted neighbours. These pixels, dataset-masked values
211+ and non-finite measurements use the colormap's standard bad-value colour;
212+ they do not determine the display scale. No clipping or data mutation.
213+ """
214+ from autoarray .plot .array import plot_array
215+ from autoarray .plot .utils import apply_labels
216+
217+ panels = [
218+ (dataset .data , "Image" , stretch , None ),
219+ (dataset .noise_map , "Noise map" , None , None ),
220+ (dataset .signal_to_noise_map , "Signal-to-noise map" , 0.1 , "" ),
221+ ]
222+ prepared = [(_intro_panel (array , radius , excluded_pixels ), title , scale , unit )
223+ for array , title , scale , unit in panels ]
224+ fig , axes = subplots (2 , 2 , figsize = conf_subplot_figsize (2 , 2 ))
225+ for ax , ((values , extent ), title , scale , unit ) in zip (axes .flat , prepared ):
226+ plot_array (values , ax = ax , extent = extent , title = title ,
227+ colormap = colormap , stretch = scale , cb_unit = unit )
228+ if dataset .psf is not None :
229+ plot_array (dataset .psf .kernel , ax = axes [1 , 1 ], title = "PSF" ,
230+ colormap = colormap , stretch = 0.003 , cb_unit = "" )
231+ else :
232+ apply_labels (axes [1 , 1 ], title = "PSF" , is_subplot = True )
233+ axes [1 , 1 ].text (0.5 , 0.5 , "No PSF supplied" , ha = "center" , va = "center" ,
234+ transform = axes [1 , 1 ].transAxes )
235+ tight_layout ()
236+ subplot_save (fig , output_path , output_filename , output_format )
237+ return fig
238+
239+
149240def subplot_imaging_dataset_list (
150241 dataset_list ,
151242 output_path = None ,
0 commit comments