Plotting a Catalog over a Kepler Full Frame Image File

This tutorial demonstrates how to access the WCS (World Coordinate System) from a full frame image file and use this data to plot a catalog of objects over the FFI.

Table of Contents

[Getting the Data](#data_ID)
[File Information](#fileinfo_ID)
[Displaying Image Data](#image_ID)
[Overplotting Objects](#overplot_ID)
[Additional Resources](#resources_ID)
[About this Notebook](#about_ID)


Full Frame Image file background: A Full Frame Image (FFI) contains values for every pixel in each of the 84 channels. Standard calibrations, such as flat fields, blacks, and smears have been applied to the calibrated FFIs. These files also contain a World Coordinate System (WCS) that attaches RA and Dec coordinates to pixel x and y values.

Some notes about the file: kplr2009170043915_ffi-cal.fits
The filename contains phrases for identification, where

  • kplr = Kepler
  • 2009170043915 = year 2009, day 170, time 04:39:15
  • ffi-cal = calibrated FFI image

Defining some terms:

  • HDU: Header Data Unit; a FITS file is made up of Header or Data units that contain information, data, and metadata relating to the file. The first HDU is called the primary, and anything that follows is considered an extension.
  • TIC: TESS Input Catalog; a catalog of luminous sources on the sky to be used by the TESS mission. We will use the TIC in this notebook to query a catalog of objects that we will then plot over an image from Kepler.
  • WCS: World Coordinate System; coordinates attached to each pixel of an N-dimensional image of a FITS file. For example, a specified celestial RA and Dec associated with pixel location in the image.

For more information about the Kepler mission and collected data, visit the Kepler archive page. To read more details about light curves and important data terms, look in the Kepler archive manual.


Let's start by importing some libraries to the environment:

  • numpy to handle array functions
  • fits for accessing fits files
  • astropy.wcs WCS to project the World Coordinate System on the plot
  • astropy.table Table for creating tidy tables of the data
  • matplotlib.pyplot for plotting data
In [1]:
%matplotlib inline
import numpy as np
from import fits
from astropy.wcs import WCS
from astropy.table import Table
import matplotlib.pyplot as plt

Getting the Data

Start by importing libraries from Astroquery. For a longer, more detailed description using of Astroquery, please visit this tutorial or read the Astroquery documentation.

In [2]:
from astroquery.mast import Mast
from astroquery.mast import Observations

Next, we need to find the data file. This is similar to searching for the data using the MAST Portal in that we will be using certain keywords to find the file. The object we are looking for is kplr2009170043915, collected by the Kepler spacecraft. We are searching for an FFI file of this object:

In [3]:
kplrObs = Observations.query_criteria(obs_id="kplr2009170043915_84", obs_collection="KeplerFFI")
kplrProds = Observations.get_product_list(kplrObs[0])
yourProd = Observations.filter_products(kplrProds, extension='kplr2009170043915_ffi-cal.fits', 
Table masked=True length=1
4000002693KeplerFFIimagekplr2009170043915_84Full Frame Image (FFI)Cmast:KeplerFFI/url/missions/kepler/ffi/kplr2009170043915_ffi-cal.fitsSCIENCEMinimum Recommended Products----Kepler----kplr2009170043915_ffi-cal.fits4078828804000002693PUBLIC

Now that we've found the data file, we can download it using the reults shown in the table above:

In [4]:
Observations.download_products(yourProd, mrp_only=False, cache=False)
Table length=1
Local PathStatusMessageURL
./mastDownload/KeplerFFI/kplr2009170043915_84/kplr2009170043915_ffi-cal.fitsERRORHTTPError: 404 Client Error: Not Found for url:

Reading FITS Extensions

Now that we have the file, we can start working with the data. We will begin by assigning a shorter name to the file to make it easier to use. Then, using the info function from, we can see some information about the FITS Header Data Units:

In [5]:
filename = "./mastDownload/KeplerFFI/kplr2009170043915_84/kplr2009170043915_ffi-cal.fits"

FileNotFoundErrorTraceback (most recent call last)
<ipython-input-5-89608f4c7155> in <module>
      1 filename = "./mastDownload/KeplerFFI/kplr2009170043915_84/kplr2009170043915_ffi-cal.fits"
----> 2

/opt/conda/envs/notebooks_env/lib/python3.6/site-packages/astropy/io/fits/ in info(filename, output, **kwargs)
    737         kwargs['ignore_missing_end'] = True
--> 739     f = fitsopen(filename, mode=mode, **kwargs)
    740     try:
    741         ret =

/opt/conda/envs/notebooks_env/lib/python3.6/site-packages/astropy/io/fits/hdu/ in fitsopen(name, mode, memmap, save_backup, cache, lazy_load_hdus, **kwargs)
    150     return HDUList.fromfile(name, mode, memmap, save_backup, cache,
--> 151                             lazy_load_hdus, **kwargs)

/opt/conda/envs/notebooks_env/lib/python3.6/site-packages/astropy/io/fits/hdu/ in fromfile(cls, fileobj, mode, memmap, save_backup, cache, lazy_load_hdus, **kwargs)
    388         return cls._readfrom(fileobj=fileobj, mode=mode, memmap=memmap,
    389                              save_backup=save_backup, cache=cache,
--> 390                              lazy_load_hdus=lazy_load_hdus, **kwargs)
    392     @classmethod

/opt/conda/envs/notebooks_env/lib/python3.6/site-packages/astropy/io/fits/hdu/ in _readfrom(cls, fileobj, data, mode, memmap, save_backup, cache, lazy_load_hdus, **kwargs)
   1037             if not isinstance(fileobj, _File):
   1038                 # instantiate a FITS file object (ffo)
-> 1039                 fileobj = _File(fileobj, mode=mode, memmap=memmap, cache=cache)
   1040             # The Astropy mode is determined by the _File initializer if the
   1041             # supplied mode was None

/opt/conda/envs/notebooks_env/lib/python3.6/site-packages/astropy/utils/ in wrapper(*args, **kwargs)
    519                             kwargs[new_name[i]] = value
--> 521             return function(*args, **kwargs)
    523         return wrapper

/opt/conda/envs/notebooks_env/lib/python3.6/site-packages/astropy/io/fits/ in __init__(self, fileobj, mode, memmap, overwrite, cache)
    176             self._open_fileobj(fileobj, mode, overwrite)
    177         elif isinstance(fileobj, str):
--> 178             self._open_filename(fileobj, mode, overwrite)
    179         else:
    180             self._open_filelike(fileobj, mode, overwrite)

/opt/conda/envs/notebooks_env/lib/python3.6/site-packages/astropy/io/fits/ in _open_filename(self, filename, mode, overwrite)
    554         if not self._try_read_compressed(, magic, mode, ext=ext):
--> 555             self._file = fileobj_open(, IO_FITS_MODES[mode])
    556             self.close_on_error = True

/opt/conda/envs/notebooks_env/lib/python3.6/site-packages/astropy/io/fits/ in fileobj_open(filename, mode)
    395     """
--> 397     return open(filename, mode, buffering=0)

FileNotFoundError: [Errno 2] No such file or directory: './mastDownload/KeplerFFI/kplr2009170043915_84/kplr2009170043915_ffi-cal.fits'
  • No. 0 (Primary):
    This HDU contains meta-data related to the entire file.
  • No. 1-84 (Image):
    Each of the 84 image extensions contains an array that can be plotted as an image. We will plot one in this tutorial along with catalog data.

Let's say we wanted to see more information about the header and extensions than what the command gave us. For example, we can access information stored in the header of any of the Image extensions (No.1 - 84, MOD.OUT). The following line opens the FITS file, writes the first HDU extension into header1, and then closes the file. Only 24 rows of data are displayed here but you can view them all by adjusting the range:

In [ ]:
with as hdulist: 
    header1 = hdulist[1].header

Displaying Image Data

First, let's find the WCS information associated with the FITS file we are using. One way to do this is to access the header and print the rows containing the relevant data (54 - 65). This gives us the reference coordinates (CRVAL1, CRVAL2) that correspond to the reference pixels:

In [ ]:
with as hdulist: 
    header1 = hdulist[1].header

Let's pick an image HDU and display its array. We can also choose to print the length of the array to get an idea of the dimensions of the image:

In [ ]:
with as hdulist:
    imgdata = hdulist[1].data

We can now plot this array as an image:

In [ ]:
fig = plt.figure(figsize=(16,8))

Now that we've seen the image and the WCS information, we can plot FFI with a WCS projection. To do this, first we will access the file header and assign a WCS object. Then we will plot the image with the projection, and add labels and a grid for usability:

In [ ]:
hdu =[1]
wcs = WCS(hdu.header)

fig = plt.figure(figsize=(16,8))
ax = plt.subplot(projection=wcs) 
im = ax.imshow(,, origin='lower', clim=(0,20000))

plt.title('FFI with WCS Projection')
ax.set_xlabel('RA [deg]')
ax.set_ylabel('Dec [deg]')
ax.grid(color='white', ls='solid')

Getting the Catalog Data

Now that we have an image, we can use astroquery to retrieve a catalog of objects and overlay it onto the image. First, we will start with importing catalog data from astroquery:

In [ ]:
from astroquery.mast import Catalogs

We will query a catalog of objects from TIC (TESS Input Catalog). For more information about TIC, follow this link. Our search will be centered on the same RA and Declination listed in the header of the FFI image and will list objects within a 1 degree radius of that location. It might take a couple seconds longer than usual for this cell to run:

why tic??? explain...

In [ ]:
catalogData = Catalogs.query_region("290.4620065226813  38.32946356799192", radius="0.7 deg", catalog="TIC")
dattab = Table(catalogData)

Let's isolate the RA and Dec columns into a separate table for creating a plot. We will can also filter our results to include only sources brigther than 15 magnitudes in B, which will give us a more managable amount of sources for plotting:

In [ ]:
radec = (catalogData['ra','dec','Bmag'])
mask = radec['Bmag'] < 15.0
mag_radec = radec[mask]

We can plot this table to get an idea of what the catalog looks like visually:

In [ ]:
fig = plt.figure(figsize=(8,8))
ax = fig.add_subplot(111)
plt.scatter(mag_radec['ra'], mag_radec['dec'], facecolors='none', edgecolors='c', linewidths=0.5)

Overplotting Objects

Now that we have a way to display an FFI file and a catalog of objects, we can put the two pieces of data on the same plot. To do this, we will project the World Coordinate System (WCS) as a grid in units of degrees, minutes, and seconds onto the image. Then, we will create a scatter plot of the catalog, similar to the one above, although here we will transform its coordinate values into ICRS (International Celestial Reference System) to be compatible with the WCS projection:

In [ ]:
hdu =[1]
wcs = WCS(hdu.header)

fig = plt.figure(figsize=(20,10))
ax = plt.subplot(projection=wcs) 
im = ax.imshow(,, origin='lower', clim=(0,20000))

plt.title('FFI with TIC Catalog Objects')
ax.set_xlabel('RA [deg]')
ax.set_ylabel('Dec [deg]')
ax.grid(color='white', ls='solid')

ax.scatter(mag_radec['ra'], mag_radec['dec'],
           facecolors='none', edgecolors='c', linewidths=0.5,
           transform=ax.get_transform('icrs')) # This is needed when projecting onto axes with WCS info

The catalog is displayed here as blue circles that highlight certain objects common in both the Kepler FFI and the TIC search. The image remains in x, y pixel values while the grid is projected in degrees based on the WCS. The projection works off of WCS data in the FFI header to create an accurate grid displaying RA and Dec coordinates that correspond to the original pixel values. The catalog data is transformed into ICRS coordinates in order to work compatibly with the other plotted data.

Aditional Resources

For more information about the MAST archive and details about mission data:

Kepler Archive Page (MAST)
Kepler Archive Manual
Exo.MAST website
TESS Archive Page (MAST)

About this Notebook

Author: Josie Bunnell, STScI SASP Intern
Updated On: 08/13/2018

In [ ]: