File: plotresults.py

package info (click to toggle)
pybdsf 1.13.0-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 22,188 kB
  • sloc: fortran: 40,850; python: 14,895; ansic: 4,347; cpp: 1,586; makefile: 131; sh: 46
file content (752 lines) | stat: -rw-r--r-- 29,725 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
"""Plotting module

This module is used to display fits results.
"""
from __future__ import print_function
from __future__ import absolute_import
from . import has_pl
if has_pl:
    import matplotlib.pyplot as pl
    import matplotlib.cm as cm
    from matplotlib.patches import Ellipse
    from matplotlib import collections
from . import functions as func
from .const import fwsig
import warnings
import numpy as N
warnings.simplefilter(action='ignore', category=FutureWarning)


def plotresults(img, ch0_image=True, rms_image=True, mean_image=True,
                ch0_islands=True, gresid_image=True, sresid_image=False,
                gmodel_image=True, smodel_image=False, pyramid_srcs=False,
                source_seds=False, ch0_flagged=False, pi_image=False,
                psf_major=False, psf_minor=False, psf_pa=False, broadcast=False):
    """Show the results of a fit."""
    global img_ch0, img_rms, img_mean, img_gaus_mod, img_shap_mod
    global img_gaus_resid, img_shap_resid, pixels_per_beam, pix2sky
    global vmin, vmax, vmin_cur, vmax_cur, ch0min, ch0max, img_pi
    global low, fig, images, src_list, srcid_cur, sky2pix, markers
    global img_psf_maj, img_psf_min, img_psf_pa, do_broadcast, samp_client
    global samp_key, samp_gaul_table_url, samp_srl_table_url

    if not has_pl:
        print("\033[31;1mWARNING\033[0m: Matplotlib not found. Plotting is disabled.")
        return
    if hasattr(img, 'samp_client'):
        samp_client = img.samp_client
        samp_key = img.samp_key
        if hasattr(img, 'samp_srl_table_url'):
            samp_srl_table_url = img.samp_srl_table_url
        else:
            samp_srl_table_url = None
        if hasattr(img, 'samp_gaul_table_url'):
            samp_gaul_table_url = img.samp_gaul_table_url
        else:
            samp_gaul_table_url = None
    else:
        samp_client = None
        samp_key = None
        samp_srl_table_url = None
        samp_gaul_table_url = None
    do_broadcast = broadcast

    # Define the images. The images are used both by imshow and by the
    # on_press() and coord_format event handlers
    pix2sky = img.pix2sky
    sky2pix = img.sky2pix
    gfactor = 2.0 * N.sqrt(2.0 * N.log(2.0))
    pixels_per_beam = 2.0 * N.pi * (img.beam2pix(img.beam)[0]
                                    * img.beam2pix(img.beam)[1]) / gfactor**2

    # Construct lists of images, titles, etc.
    images = []
    titles = []
    names = []
    markers = []
    img_gaus_mod = None  # default needed for key press event
    img_shap_mod = None  # default needed for key press event
    if ch0_image:
        img_ch0 = img.ch0_arr
        images.append(img_ch0)
        titles.append('Original (ch0) Image\n(arbitrary logarithmic scale)')
        names.append('ch0')
    if ch0_islands:
        img_ch0 = img.ch0_arr
        images.append(img_ch0)
        if hasattr(img, 'ngaus'):
            if hasattr(img, 'ch0_pi_arr'):
                ch0_str = 'Islands (hatched boundaries; red = PI only) and\nGaussians'
            else:
                ch0_str = 'Islands (hatched boundaries) and\nGaussians'
            if hasattr(img, 'atrous_gaussians'):
                ch0_str += ' (red = wavelet)'
            titles.append(ch0_str)
        else:
            titles.append('Islands (hatched boundaries)')
        names.append('ch0')
    if ch0_flagged:
        if not hasattr(img, 'ngaus'):
            print('Image was not fit with Gaussians. Skipping display of flagged Gaussians.')
        else:
            img_ch0 = img.ch0_arr
            images.append(img_ch0)
            titles.append('Flagged Gaussians')
        names.append('ch0')
    if pi_image:
        if not hasattr(img, 'ch0_pi_arr'):
            print('Polarization module not run. Skipping PI image.')
        else:
            img_pi = img.ch0_pi_arr
            images.append(img_pi)
            titles.append('Polarized Intensity Image')
            names.append('ch0_pi')
    if rms_image:
        img_rms = img.rms_arr
        images.append(img_rms)
        titles.append('Background rms Image')
        names.append('rms')
    if gresid_image:
        if not hasattr(img, 'ngaus'):
            print('Image was not fit with Gaussians. Skipping residual Gaussian image.')
        else:
            img_gaus_resid = img.resid_gaus_arr
            images.append(img_gaus_resid)
            titles.append('Gaussian Residual Image')
            names.append('gaus_resid')
    if gmodel_image:
        if not hasattr(img, 'ngaus'):
            print('Image was not fit with Gaussians. Skipping model Gaussian image.')
        else:
            img_gaus_mod = img.model_gaus_arr
            images.append(img_gaus_mod)
            titles.append('Gaussian Model Image')
            names.append('gaus_mod')
    if mean_image:
        img_mean = img.mean_arr
        images.append(img_mean)
        titles.append('Background mean Image')
        names.append('mean')
    if sresid_image:
        if not img.opts.shapelet_do:
            print('Image was not decomposed into shapelets. Skipping residual shapelet image.')
        else:
            img_shap_resid = img.resid_shap_arr
            images.append(img_shap_resid)
            titles.append('Shapelet Residual Image')
            names.append('shap_resid')
    if smodel_image:
        if not img.opts.shapelet_do:
            print('Image was not decomposed into shapelets. Skipping model shapelet image.')
        else:
            img_shap_mod = img.model_shap_arr
            images.append(img_shap_mod)
            titles.append('Shapelet Model Image')
            names.append('shap_mod')
    if source_seds:
        if not img.opts.spectralindex_do:
            print('Source SEDs were not fit. Skipping source SED plots.')
        else:
            src_list = img.sources
            sed_src = get_src(src_list, 0)
            if sed_src is None:
                print('No sources found. Skipping source SED plots.')
            else:
                images.append('seds')
                titles.append('')
                names.append('seds')
                srcid_cur = 0
    if pyramid_srcs:
        if not img.opts.atrous_do:
            print('Image was not decomposed into wavelets. Skipping wavelet images.')
        else:
            # Get the unique j levels and store them. Only make subplots for
            # occupied j levels
            print('Pyramidal source plots not yet supported.')
#             j_list = []
#             for p in img.pyrsrcs:
#                 for l in p.jlevels:
#                     j_list.append(l)
#             j_set = set(j_list)
#             j_with_gaus = list(j_set)
#             index_first_waveplot = len(images)
#             for i in range(len(j_with_gaus)):
#                 images.append('wavelets')
#                 names.append('pyrsrc'+str(i))
    if psf_major or psf_minor or psf_pa:
        if not img.opts.psf_vary_do:
            print('PSF variation not calculated. Skipping PSF variation images.')
        else:
            if psf_major:
                img_psf_maj = img.psf_vary_maj_arr*fwsig
                images.append(img_psf_maj)
                titles.append('PSF Major Axis FWHM (pixels)')
                names.append('psf_maj')
            if psf_minor:
                img_psf_min = img.psf_vary_min_arr*fwsig
                images.append(img_psf_min)
                titles.append('PSF Minor Axis FWHM (pixels)')
                names.append('psf_min')
            if psf_pa:
                img_psf_pa = img.psf_vary_pa_arr
                images.append(img_psf_pa)
                titles.append('PSF Pos. Angle FWhM (degrees)')
                names.append('psf_pa')

    if images == []:
        print('No images to display.')
        return

    im_mean = img.clipped_mean
    im_rms = img.clipped_rms
    if img.resid_gaus_arr is None:
        low = 1.1*abs(img.min_value)
    else:
        low = N.max([1.1*abs(img.min_value), 1.1*abs(N.nanmin(img.resid_gaus_arr))])
    if low <= 0.0:
        low = 1E-6
    vmin_est = im_mean - im_rms*5.0 + low
    if vmin_est <= 0.0:
        vmin = N.log10(low)
    else:
        vmin = N.log10(vmin_est)
    vmax = N.log10(im_mean + im_rms*30.0 + low)
    ch0min = vmin
    ch0max = N.log10(img.max_value + low)
    vmin_cur = vmin
    vmax_cur = vmax
    origin = 'lower'  # note: origin is used in cmd string below
    colours = ['m', 'b', 'c', 'g', 'y', 'k']  # reserve red ('r') for wavelets
    styles = ['-', '-.', '--']
    print('=' * 72)
    print('NOTE -- With the mouse pointer in plot window:')
    print('  Press "i" ........ : Get integrated flux densities and mean rms')
    print('                       values for the visible portion of the image')
    print('  Press "m" ........ : Change min and max scaling values')
    print('  Press "n" ........ : Show / hide island IDs')
    print('  Press "0" ........ : Reset scaling to default')
    if 'seds' in names:
        print('  Press "c" ........ : Change source for SED plot')
    if ch0_islands and hasattr(img, 'ngaus'):
        print('  Click Gaussian ... : Print Gaussian and source IDs (zoom_rect mode, ')
        print('                       toggled with the "zoom" button and indicated in ')
        print('                       the lower right corner, must be off)')
        if 'seds' in names:
            print('                       The SED plot will also show the chosen source.')
    print('_' * 72)

    if len(images) > 1:
        numx = 2
    else:
        numx = 1
    numy = int(N.ceil(float(len(images))/float(numx)))
    fig = pl.figure(figsize=(max(15, 10.0*float(numy)/float(numx)), 10.0))
    fig.canvas.manager.set_window_title('PyBDSF Fit Results for '+ img.filename)
    gray_palette = cm.gray
    gray_palette.set_bad('k')

    for i, image in enumerate(images):
        if isinstance(image, N.ndarray):
            if i == 0:
                cmd = 'ax' + str(i+1) + ' = pl.subplot(' + str(numx) + \
                    ', ' + str(numy) + ', ' + str(i+1) + ')'
            else:
                cmd = 'ax' + str(i+1) + ' = pl.subplot(' + str(numx) + \
                    ', ' + str(numy) + ', ' + str(i+1) + ', sharex=ax1' + \
                    ', sharey=ax1)'
            exec(cmd, globals())
            if 'PSF' in titles[i]:
                im = image  # note: im is used in cmd string below
            else:
                im = N.log10(image + low)  # note: im is used in cmd string below
            if 'Islands' in titles[i]:
                island_offsets_x = []
                island_offsets_y = []
                border_color = []
                ax = pl.gca()
                for iisl, isl in enumerate(img.islands):
                    xb, yb = isl.border
                    if hasattr(isl, '_pi'):
                        for c in range(len(xb)):
                            border_color.append('r')
                    else:
                        for c in range(len(xb)):
                            border_color.append('#afeeee')
                    island_offsets_x += xb.tolist()
                    island_offsets_y += yb.tolist()
                    marker = ax.text(N.max(xb)+2, N.max(yb), str(isl.island_id),
                                     color='#afeeee', clip_on=True)
                    marker.set_visible(not marker.get_visible())
                    markers.append(marker)
                    # draw the gaussians with one colour per source or island
                    # (if gaul2srl was not run)
                    if hasattr(img, 'nsrc'):
                        nsrc = len(isl.sources)
                        for isrc in range(nsrc):
                            col = colours[int(isrc % 6)]
                            style = styles[int(isrc/6 % 3)]
                            src = isl.sources[isrc]
                            for g in src.gaussians:
                                if hasattr(g, 'valid'):
                                    valid = g.valid
                                else:
                                    valid = True
                                if g.jlevel == 0 and valid and g.gaus_num >= 0:
                                    gidx = g.gaus_num
                                    e = Ellipse(xy=g.centre_pix, width=g.size_pix[0],
                                                height=g.size_pix[1], angle=g.size_pix[2]+90.0)
                                    ax.add_artist(e)
                                    e.set_picker(3)
                                    e.set_clip_box(ax.bbox)
                                    e.set_facecolor(col)
                                    e.set_alpha(0.5)
                                    e.gaus_id = gidx
                                    e.src_id = src.source_id
                                    e.jlevel = g.jlevel
                                    e.isl_id = g.island_id
                                    e.tflux = g.total_flux
                                    e.pflux = g.peak_flux
                                    e.centre_sky = g.centre_sky
                if len(img.islands) > 0:
                    island_offsets = list(zip(N.array(island_offsets_x), N.array(island_offsets_y)))
                    isl_borders = collections.AsteriskPolygonCollection(4, offsets=island_offsets, color=border_color,
                                                                        transOffset=ax.transData, sizes=(10.0,))
                    ax.add_collection(isl_borders)

                if hasattr(img, 'gaussians'):
                    for atrg in img.gaussians:
                        if atrg.jlevel > 0 and atrg.gaus_num >= 0:
                            col = 'r'
                            style = '-'
                            gidx = atrg.gaus_num
                            e = Ellipse(xy=atrg.centre_pix, width=atrg.size_pix[0], height=atrg.size_pix[1], angle=atrg.size_pix[2]+90.0)
                            ax.add_artist(e)
                            e.set_picker(3)
                            e.set_clip_box(ax.bbox)
                            e.set_edgecolor(col)
                            e.set_facecolor('none')
                            e.set_alpha(0.8)
                            e.gaus_id = gidx
                            e.src_id = atrg.source_id
                            e.jlevel = atrg.jlevel
                            e.isl_id = atrg.island_id
                            e.tflux = atrg.total_flux
                            e.pflux = atrg.peak_flux
                            e.centre_sky = atrg.centre_sky

            if 'Flagged' in titles[i]:
                for iisl, isl in enumerate(img.islands):
                    ax = pl.gca()
                    style = '-'
                    for ig, g in enumerate(isl.fgaul):
                        col = colours[ig % 6]
                        ellx, elly = func.drawellipse(g)
                        gline, = ax.plot(ellx, elly, color=col,
                                         linestyle=style, picker=3)
                        gline.flag = g.flag

            if 'PSF' in titles[i]:
                cmd = 'ax' + str(i+1) + ".imshow(N.transpose(im), origin=origin, "\
                      "interpolation='nearest', cmap=gray_palette)"
            else:
                cmd = 'ax' + str(i+1) + ".imshow(N.transpose(im), origin=origin, "\
                      "interpolation='nearest', vmin=vmin, vmax=vmax, cmap=gray_palette)"
            exec(cmd)
            cmd = 'ax' + str(i+1) + '.format_coord = format_coord_'+names[i]
            exec(cmd)
            pl.title(titles[i])
        elif image == 'seds':
            cmd = 'ax' + str(i+1) + ' = pl.subplot(' + str(numx) + \
                ', ' + str(numy) + ', ' + str(i+1) + ')'
            exec(cmd, globals())
            ax = pl.gca()
            plot_sed(sed_src, ax)

        elif image == 'wavelets':
            print('Pyramidal source plots not yet supported.')
            # if i == index_first_waveplot:
            #     for j in range(len(j_with_gaus)):
            #         cmd = 'ax' + str(j+i+1) + ' = pl.subplot(' + str(numx) + \
            #             ', ' + str(numy) + ', ' + str(j+i+1) + ', sharex=ax1, '+\
            #             'sharey=ax1)'
            #         exec(cmd)
            #         pl.title('Pyramidal Sources for\nWavelet Scale J = ' +
            #                  str(j_with_gaus[j]))
            # for pyr in img.pyrsrcs:
            #     for iisl, isl in enumerate(pyr.islands):
            #         jj = pyr.jlevels[iisl]
            #         jindx = j_with_gaus.index(jj)
            #         col = colours[pyr.pyr_id % 6]
            #         ind = N.where(~isl.mask_active)
            #         cmd = "ax" + str(jindx + index_first_waveplot + 1) + \
            #             ".plot(ind[0]+isl.origin[0], "\
            #             "ind[1]+isl.origin[1], '.', color=col)"
            #         exec(cmd)

    fig.canvas.mpl_connect('key_press_event', on_press)
    fig.canvas.mpl_connect('pick_event', on_pick)
    pl.show()
    pl.close('all')


def on_pick(event):
    global images, srcid_cur, samp_client, samp_key, do_broadcast, samp_gaul_table_url, samp_srl_table_url
    g = event.artist
    if hasattr(g, 'gaus_id'):
        gaus_id = g.gaus_id
        src_id = g.src_id
        isl_id = g.isl_id
        tflux = g.tflux
        pflux = g.pflux
        wav_j = g.jlevel
        if wav_j == 0:
            print('Gaussian #' + str(gaus_id) + ' (in src #' + str(src_id) +
                  ', isl #' + str(isl_id) + '): F_tot = ' + str(round(tflux, 4)) +
                  ' Jy, F_peak = ' + str(round(pflux, 4)) + ' Jy/beam')
        else:
            print('Gaussian #' + str(gaus_id) + ' (in src #' + str(src_id) +
                  ', isl #' + str(isl_id) + ', wav #' + str(wav_j) +
                  '): F_tot = ' + str(round(tflux, 3)) + ' Jy, F_peak = ' +
                  str(round(pflux, 4)) + ' Jy/beam')

        # Transmit src_id, gaus_id, and coordinates to SAMP Hub (if we are connected)
        if do_broadcast and samp_key is not None:
            if samp_gaul_table_url is not None:
                func.send_highlight_row(samp_client, samp_key, samp_gaul_table_url, gaus_id)
            if samp_srl_table_url is not None:
                func.send_highlight_row(samp_client, samp_key, samp_srl_table_url, src_id)
            func.send_coords(samp_client, samp_key, g.centre_sky)

        # Change source SED
        # First check that SEDs are being plotted and that the selected Gaussian
        # is from the zeroth wavelet image
        has_sed = False
        for image in images:
            if isinstance(image, str) and image == 'seds' and wav_j == 0:
                has_sed = True
        if not has_sed:
            return
        sed_src = get_src(src_list, src_id)
        if srcid_cur == src_id:
            return
        srcid_cur = src_id
        axes_list = fig.get_axes()
        for axindx, ax in enumerate(axes_list):
            if isinstance(images[axindx], str) and images[axindx] == 'seds':
                plot_sed(sed_src, ax)
    else:
        print('Flagged Gaussian (flag = ' + str(g.flag) + '; use "' +
              "help 'flagging_opts'" + '" for flag meanings)')

    pl.draw()


def on_press(event):
    """Handle keypresses"""
    from .interface import raw_input_no_history

    global img_ch0, img_rms, img_mean, img_gaus_mod, img_shap_mod
    global pixels_per_beam, vmin, vmax, vmin_cur, vmax_cur, img_pi
    global ch0min, ch0max, low, fig, images, src_list, srcid_cur
    global markers
    if event.key == '0':
        print('Resetting limits to defaults (%.4f -- %.4f Jy/beam)'
              % (pow(10, vmin)-low, pow(10, vmax)-low))
        axes_list = fig.get_axes()
        for axindx, ax in enumerate(axes_list):
            if isinstance(images[axindx], N.ndarray):
                im = ax.get_images()[0]
                im.set_clim(vmin, vmax)
        vmin_cur = vmin
        vmax_cur = vmax
        pl.draw()
    if event.key == 'm':
        # Modify scaling
        # First check that there are images to modify
        has_image = False
        for im in images:
            if isinstance(im, N.ndarray):
                has_image = True
        if not has_image:
            return
        minscl = 'a'
        while isinstance(minscl, str):
            try:
                if minscl == '':
                    minscl = pow(10, vmin_cur) - low
                    break
                minscl = float(minscl)
            except ValueError:
                prompt = "Enter min value (current = %.4f Jy/beam) : " % (pow(10, vmin_cur)-low,)
                try:
                    minscl = raw_input_no_history(prompt)
                except RuntimeError:
                    print('Sorry, unable to change scaling.')
                    return
        minscl = N.log10(minscl + low)
        maxscl = 'a'
        while isinstance(maxscl, str):
            try:
                if maxscl == '':
                    maxscl = pow(10, vmax_cur) - low
                    break
                maxscl = float(maxscl)
            except ValueError:
                prompt = "Enter max value (current = %.4f Jy/beam) : " % (pow(10, vmax_cur)-low,)
                try:
                    maxscl = raw_input_no_history(prompt)
                except RuntimeError:
                    print('Sorry, unable to change scaling.')
                    return
        maxscl = N.log10(maxscl + low)
        if maxscl <= minscl:
            print('Max value must be greater than min value!')
            return
        axes_list = fig.get_axes()
        for axindx, ax in enumerate(axes_list):
            if isinstance(images[axindx], N.ndarray):
                im = ax.get_images()[0]
                im.set_clim(minscl, maxscl)
        vmin_cur = minscl
        vmax_cur = maxscl
        pl.draw()
    if event.key == 'c':
        # Change source SED
        # First check that SEDs are being plotted
        has_sed = False
        for image in images:
            if isinstance(image, str) and image == 'seds':
                has_sed = True
        if not has_sed:
            return
        srcid = 'a'
        while isinstance(srcid, str):
            try:
                if srcid == '':
                    srcid = srcid_cur
                    break
                srcid = int(srcid)
            except ValueError:
                prompt = "Enter source ID (current = %i) : " % (srcid_cur,)
                try:
                    srcid = raw_input_no_history(prompt)
                except RuntimeError:
                    print('Sorry, unable to change source.')
                    return
        sed_src = get_src(src_list, srcid)
        if sed_src is None:
            print('Source not found!')
            return
        srcid_cur = srcid
        axes_list = fig.get_axes()
        for axindx, ax in enumerate(axes_list):
            if isinstance(images[axindx], str) and images[axindx] == 'seds':
                plot_sed(sed_src, ax)
        pl.draw()
    if event.key == 'i':
        # Print info about visible region
        has_image = False
        axes_list = fig.get_axes()
        # Get limits of visible region
        for axindx, ax in enumerate(axes_list):
            if isinstance(images[axindx], N.ndarray):
                xmin, xmax = ax.get_xlim()
                ymin, ymax = ax.get_ylim()
                has_image = True
                break
        if not has_image:
            return
        if xmin < 0:
            xmin = 0
        if xmax > img_ch0.shape[0]:
            xmax = img_ch0.shape[0]
        if ymin < 0:
            ymin = 0
        if ymax > img_ch0.shape[1]:
            ymax = img_ch0.shape[1]

        # Ensure slice indices are int, then get various values for the
        # visible region
        xmin = int(xmin)
        xmax = int(xmax)
        ymin = int(ymin)
        ymax = int(ymax)
        flux = N.nansum(img_ch0[xmin:xmax, ymin:ymax])/pixels_per_beam
        mask = N.isnan(img_ch0[xmin:xmax, ymin:ymax])
        num_pix_unmasked = float(N.size(N.where(mask == False), 1))
        mean_rms = N.nansum(img_rms[xmin:xmax, ymin:ymax])/num_pix_unmasked
        mean_map_flux = N.nansum(img_mean[xmin:xmax, ymin:ymax])/pixels_per_beam
        if img_gaus_mod is None:
            gaus_mod_flux = 0.0
        else:
            gaus_mod_flux = N.nansum(img_gaus_mod[xmin:xmax, ymin:ymax])/pixels_per_beam
        print('Visible region (%i:%i, %i:%i) :' % (xmin, xmax, ymin, ymax))
        print('  ch0 flux density from sum of pixels ... : %f Jy'
              % (flux,))
        print('  Background mean map flux density ...... : %f Jy'
              % (mean_map_flux,))
        print('  Gaussian model flux density ........... : %f Jy'
              % (gaus_mod_flux,))
        if img_shap_mod is not None:
            shap_mod_flux = N.nansum(img_shap_mod[xmin:xmax, ymin:ymax])/pixels_per_beam
            print('  Shapelet model flux density ........... : %f Jy'
                  % (shap_mod_flux,))
        print('  Mean rms (from rms map) ............... : %f Jy/beam'
              % (mean_rms,))
    if event.key == 'n':
        # Show/Hide island numbers
        if markers:
            for marker in markers:
                marker.set_visible(not marker.get_visible())
            pl.draw()


# The following functions add ra, dec and flux density to the
# coordinates in the lower-right-hand corner of the figure window.
# Since each axis needs its own function (to return its particular
# flux), we need a separate function for each subplot.
def format_coord_ch0(x, y):
    """Custom coordinate format for ch0 image"""
    global img_ch0
    im = img_ch0
    coord_str = make_coord_str(x, y, im)
    return coord_str


def format_coord_ch0_pi(x, y):
    """Custom coordinate format for ch0 image"""
    global img_pi
    im = img_pi
    coord_str = make_coord_str(x, y, im)
    return coord_str


def format_coord_rms(x, y):
    """Custom coordinate format for rms image"""
    global img_rms
    im = img_rms
    coord_str = make_coord_str(x, y, im)
    return coord_str


def format_coord_mean(x, y):
    """Custom coordinate format for mean image"""
    global img_mean
    im = img_mean
    coord_str = make_coord_str(x, y, im)
    return coord_str


def format_coord_gaus_mod(x, y):
    """Custom coordinate format for Gaussian model image"""
    global img_gaus_mod
    im = img_gaus_mod
    coord_str = make_coord_str(x, y, im)
    return coord_str


def format_coord_shap_mod(x, y):
    """Custom coordinate format for shapelet model image"""
    global img_shap_mod
    im = img_shap_mod
    coord_str = make_coord_str(x, y, im)
    return coord_str


def format_coord_gaus_resid(x, y):
    """Custom coordinate format for Gaussian residual image"""
    global img_gaus_resid
    im = img_gaus_resid
    coord_str = make_coord_str(x, y, im)
    return coord_str


def format_coord_shap_resid(x, y):
    """Custom coordinate format for shapelet residual image"""
    global img_shap_resid
    im = img_shap_resid
    coord_str = make_coord_str(x, y, im)
    return coord_str


def format_coord_psf_maj(x, y):
    """Custom coordinate format for PSF major image"""
    global img_psf_maj
    im = img_psf_maj
    coord_str = make_coord_str(x, y, im, unit='arcsec')
    return coord_str


def format_coord_psf_min(x, y):
    """Custom coordinate format for PSF minor image"""
    global img_psf_min
    im = img_psf_min
    coord_str = make_coord_str(x, y, im, unit='arcsec')
    return coord_str


def format_coord_psf_pa(x, y):
    """Custom coordinate format for PSF pos. ang. image"""
    global img_psf_pa
    im = img_psf_pa
    coord_str = make_coord_str(x, y, im, unit='degrees')
    return coord_str


def xy_to_radec_str(x, y):
    """Converts x, y in image coords to a sexigesimal string"""
    from .output import ra2hhmmss, dec2ddmmss
    global pix2sky
    ra, dec = pix2sky([x, y])

    ra = ra2hhmmss(ra)
    sra = str(ra[0]).zfill(2)+':'+str(ra[1]).zfill(2)+':'+str("%.1f" % (ra[2])).zfill(3)
    dec = dec2ddmmss(dec)
    decsign = ('-' if dec[3] < 0 else '+')
    sdec = decsign+str(dec[0]).zfill(2)+':'+str(dec[1]).zfill(2)+':'+str("%.1f" % (dec[2])).zfill(3)
    return sra, sdec


def make_coord_str(x, y, im, unit='Jy/beam'):
    """Makes the x, y, ra, dec, flux string"""
    rastr, decstr = xy_to_radec_str(x, y)
    col = int(x + 0.5)
    row = int(y + 0.5)
    numcols, numrows = im.shape
    if col >= 0 and col < numcols\
            and row >= 0 and row < numrows:
        z = im[col, row]
        return 'x=%1.1f, y=%1.1f, RA=%s, Dec=%s, F=%+1.4f %s' % (x, y, rastr, decstr, z, unit)
    else:
        return 'x=%1.1f, y=%1.1f' % (x, y)


def plot_sed(src, ax):
    """Plots the SED for source 'src' to axis 'ax'"""
    global sky2pix
    global fig
    ax.cla()
    norm = src.spec_norm
    spin = src.spec_indx
    y = N.array(src.specin_flux)
    ey = N.array(src.specin_fluxE)
    x = N.array(src.specin_freq)
    ax.errorbar(N.log10(x/1e6), N.log10(y), yerr=ey/y, fmt='bo')
    ax.plot(N.log10(x/1e6), N.log10(norm)+N.log10(x/src.specin_freq0)*spin,
            '-g', label="alpha = %.2f" % (spin,))
    pos = sky2pix(src.posn_sky_centroid)
    xpos = int(pos[0])
    ypos = int(pos[1])
    pl.title('SED of source #' + str(src.source_id) + '\n'
             + '(x = ' + str(xpos) + ', y = ' + str(ypos) + ')')
    pl.xlabel('log Frequency (MHz)')
    pl.ylabel('log Flux Density (Jy)')
    pl.legend()


def get_src(src_list, srcid):
    """Returns the source for srcid or None if not found"""
    for src in src_list:
        if src.source_id == srcid:
            return src
    return None