Demo of Astronomical Interactive Data Analysis Techniques using IPython Notebooks and Ginga

See NOTES at end for more info installation/requirements.

If you want to play with this stuff, download this notebook by the link in the upper RH corner. Save it with an "ipynb" extension.

There are two example FITS files used in this document. You can get them here and here. Put them in the same directory where you downloaded the notebook.

Start this with:

$ export QT_API=pyqt

$ ipython notebook --gui=qt

Note: doesn't play well currently with --pylab=inline, so don't do that


In [1]:
# Requirements:
from ginga.version import version
version

# Get ginga from github (https://github.com/ejeschke/ginga) or
#   pypi (https://pypi.python.org/pypi/ginga)
# Ginga documentation at: http://ginga.readthedocs.org/en/latest/


Out[1]:
'2.0.20140417032430'

In [2]:
from ginga.qtw import ipg

In [3]:
# Create an instance of the 
gq = ipg.nb_start()

A GUI should pop up. From the GUI you can click the "New Viewer" button to start a new viewer, or...


In [4]:
# start a new interactive viewer
gq.new_viewer('v1')

A Ginga viewer window labeled 'v1' should pop up.


In [4]:
%who
# Will show us our variables, including those bound to viewers.


gq	ipg	v1	version	

In [5]:
# Open up a QT console for "scratch work" on the side.
# Any commands you see here can also be done there, and
# it shares all the variables with this session
%qtconsole

# You can also press the "Qt Console" button in the GUI
# Any number of consoles and viewers can be open

In [6]:
# Now load an image into the viewer by dragging a file to the viewer,
# using the "Open File" button or the method below
v1.load('camera.fits')

Now set a pan position by shift-clicking somewhere in the window.


In [7]:
# Let's get the pan position we just set
dx, dy = v1.get_pan()
dx, dy


Out[7]:
(1559.1976744186045, 904.7113402061855)

In [8]:
# Getting values from the FITS header is also easy
img = v1.get_image()
hdr =img.get_header()
hdr['OBJECT']


Out[8]:
'M27'

In [9]:
# What are the coordinates of the pan position?
# This uses astropy.wcs under the hood if you have it installed
img.pixtoradec(dx, dy)


Out[9]:
(299.6320204251492, 22.740336641800475)

In [10]:
# Set cut level algorithm to use
v1.set_autocut_params('zscale', contrast=0.25)
# Auto cut levels on the image
v1.auto_levels()

In [11]:
# Let's do an example of the two-way interactivity
v1.set_drawtype('point')

In [12]:
# delete all objects on the canvas
v1.deleteAllObjects()

Now, in the Ginga window, draw a point using the right mouse button (if you only have one mouse button (e.g. Mac) press and release spacebar, then click and drag)


In [13]:
# get the pixel coordinates of the point we just drew
p = v1.objects[0]
p.x, p.y


Out[13]:
(739.720930232558, 1247.5243836844463)

In [14]:
# Get the RA/DEC in degrees of the point
img.pixtoradec(p.x, p.y)


Out[14]:
(299.68184276106865, 22.759652481682867)

In [15]:
# Get RA/DEC in H M S sign D M S
img.pixtoradec(p.x, p.y, format='hms')


Out[15]:
(19, 58, 43.642262656476305, 1, 22, 45, 34.748934058326995)

In [16]:
# Get RA/DEC in classical string notation
img.pixtoradec(p.x, p.y, format='str')


Out[16]:
('19:58:43.642', '+22:45:34.75')

In [17]:
# Verify we have a valid coordinate system defined
img.wcs.coordsys


Out[17]:
'fk5'

In [18]:
# set a color map on the viewer 
from ginga import cmap
v1.set_cmap(cmap.get_cmap('rainbow3'))

In [19]:
# Image will appear in this output
v1.show()


Out[19]:

In [20]:
# Example of setting another draw type.
v1.deleteAllObjects()
v1.set_drawtype('rectangle')

Now right-drag to draw a small rectangle in the Ginga image. Remember: On a single button pointing device, press and release spacebar, then click and drag.

Try to include some objects.


In [21]:
# Find approximate bright peaks in a sub-area
from ginga.util import iqcalc
iq = iqcalc.IQCalc()

img = v1.get_image()
r = v1.objects[0]
data = img.cutout_data(r.x1, r.y1, r.x2, r.y2)
peaks = iq.find_bright_peaks(data)
peaks[:20]


Out[21]:
[(50.0, 12.0),
 (191.0, 14.0),
 (47.0, 17.0),
 (135.0, 28.0),
 (122.0, 37.0),
 (55.0, 56.0),
 (20.0, 57.0),
 (124.0, 64.0),
 (121.0, 79.0),
 (198.0, 82.0),
 (47.0, 117.0),
 (176.0, 118.0),
 (51.0, 124.0),
 (131.0, 127.0),
 (97.0, 139.0),
 (131.0, 141.0),
 (9.0, 148.0),
 (82.0, 159.0),
 (144.0, 161.0),
 (89.0, 163.0)]

In [22]:
# evaluate peaks to get FWHM, center of each peak, etc.
objs = iq.evaluate_peaks(peaks, data)
# how many did we find with standard thresholding, etc.
# see params for find_bright_peaks() and evaluate_peaks() for details
len(objs)


WARNING:astropy:RuntimeWarning: Number of calls to function has reached maxfev = 800.
WARNING: RuntimeWarning: Number of calls to function has reached maxfev = 800. [scipy.optimize.minpack]
Out[22]:
30

In [23]:
# example of what is returned
o1 = objs[0]
o1


Out[23]:
{'brightness': 415.17567277508226, 'objy': 12.222102751377658, 'objx': 50.41112687476086, 'elipse': 0.9273917754855616, 'pos': 0.9506171632729228, 'background': 491.0, 'y': 12, 'x': 50, 'fwhm_y': 3.2683966848144497, 'fwhm_x': 3.031084204521196, 'fwhm': 3.1519746464878153, 'fwhm_radius': 15, 'skylevel': 555.5500000000001}

In [24]:
# pixel coords are for cutout, so add back in x1, y1 to get full data coords
# RA, DEC of first object
img.pixtoradec(r.x1+o1.objx, r.y1+o1.objy)


Out[24]:
(299.67345458567564, 22.738592954074765)

In [25]:
# Draw circles around all objects
Circle = v1.getDrawClass('circle')
for obj in objs:
    x, y = r.x1+obj.objx, r.y1+obj.objy
    if r.contains(x, y):
        v1.add(Circle(x, y, radius=10, color='yellow'))
        
# set pan and zoom to center
v1.set_pan((r.x1+r.x2)/2, (r.y1+r.y2)/2)
v1.scale_to(0.75, 0.75)

In [26]:
v1.show()


Out[26]:

How about some plots...?


In [27]:
# Load an image from a spectrograph at least 1000x1000 (e.g. spectra.fits)
v1.load('spectra.fits')

In [28]:
# swap XY, flip Y, change colormap back to "ramp"
v1.set_cmap(cmap.get_cmap('ramp'))
v1.transform(False, True, True)

In [29]:
# Programmatically add a line along the figure at designated coordinates
v1.deleteAllObjects()
Line = v1.getDrawClass('line')
l1 = Line(0, 512, 250, 512)
tag = v1.add(l1)

In [30]:
# Set the pan position and zoom to 1:1.  Show what we did.

v1.set_pan(125, 512)
v1.scale_to(1.0, 1.0)

In [31]:
v1.show()


Out[31]:

In [32]:
# Get the pixel values along this line
values = v1.get_pixels_on_line(l1.x1, l1.y1, l1.x2, l1.y2)
values[:10]


Out[32]:
[1231.0,
 1237.0,
 1220.0,
 1233.0,
 1235.0,
 1229.0,
 1229.0,
 1234.0,
 1228.0,
 1237.0]

In [33]:
# Plot the 'cuts'
import matplotlib.pyplot as plt
plt.cla()
plt.plot(values)
plt.ylabel('Pixel value')
# special show method for working with embedded plots and ginga
ipg.showplt()


Out[33]:

In [34]:
# Plot the cuts that we will draw interactively
v1.deleteAllObjects()
v1.set_drawtype('line')

Now draw a line through the image (remember to use right mouse btn or else press space bar first)


In [35]:
# show our line we drew
v1.show()


Out[35]:

In [36]:
def getplot(v1):
    l1 = v1.objects[0]
    values = v1.get_pixels_on_line(l1.x1, l1.y1, l1.x2, l1.y2)
    plt.cla()
    
    plt.plot(values)
    plt.ylabel('Pixel value')
    return ipg.showplt()

In [37]:
getplot(v1)


Out[37]:

Th-th-th-that's all folks!

NOTES:

  • this example assumes that you have a working recent version of ipython w/notebook feature
  • tested on Linux and Mac

Latest Ginga documentation, including detailed installation instructions, can be found here.