Ejemplo n.º 1
0
    def __init__(self, input_, component=None, filetype='sdf', getshape=False):

        # TODO: this should be using python NDF!
        # If input is a string, assume it is either an NDF file or a
        # FITS file, and read in the object
        frameset = None
        shape = None
        # TODO: (Should useNDF interface once that is easy to install)
        if isinstance(input_, str) and filetype == 'sdf':
            try:
                hdsloc = hds.open(input_, 'READ')
                if component:
                    comps = component.split('.')
                    for c in comps:
                        hdsloc = hdsloc.find(c)

                astwcs = hdsloc.find('WCS').find('DATA').get()
                astwcs = [i.decode() for i in astwcs]
                astwcs = ''.join([
                    i[1:] if i.startswith('+') else '\n' + i for i in astwcs
                ]).split('\n')
                chan = Ast.Channel(astwcs)
                frameset = chan.read()
            except:
                logger.error('Could not get a frameset from %s', input_)
                hdsloc.annul()
                raise

            # Get the shape of the data.
            if getshape:
                try:
                    data = hdsloc.find('DATA_ARRAY').find('DATA').get()
                    shape = data.shape
                except:
                    logger.debug('No data component found in input file.')
                    shape = None

            hdsloc.annul()

        # If its a fits file, try and read a
        elif isinstance(input_, str) and filetype == 'fits':
            from astropy.io import fits
            from starlink import Atl
            hdulist = fits.open(input_)
            if not component:
                component = 0
            (frameset, encoding) = Atl.readfitswcs(hdulist[component])
            shape = hdulist[component].shape
            print('shape is', shape)
            hdulist.close()

        # Create low level object from frameset and shape
        print(type(frameset), type(shape))
        self._low_level_wcs = AstWCSLowLevel(frameset, arrayshape=shape)
        self._info_string = None
Ejemplo n.º 2
0
# This script is featured on pyast issue page:
# https://github.com/timj/starlink-pyast/issues/8
# It also constructs the file tanflip.fits, which we use in the test suite.
# PyAst natively flips the order of RA and Dec when writing this file as a TAN WCS.
# This was a kind of input that wasn't otherwise featured in our test suite, but is
# apparently allowed by the fits standard.  So I added it.

import starlink.Atl as Atl
import starlink.Ast as Ast
import astropy.io.fits as pyfits
import numpy

# http://fits.gsfc.nasa.gov/registry/tpvwcs/tpv.fits
hdu = pyfits.open('tpv.fits')[0]
fc = Ast.FitsChan(Atl.PyFITSAdapter(hdu))
wcs = fc.read()

# A random test position.  The "true" RA, Dec values are taken from ds9.
'033009.340034', '-284350.811107', 418, 78, 2859.53882
x = 418
y = 78
true_ra = (3 + 30 / 60. + 9.340034 / 3600.) * numpy.pi / 12.
true_dec = -(28 + 43 / 60. + 50.811107 / 3600.) * numpy.pi / 180.

ra1, dec1 = wcs.tran(numpy.array([[x], [y]]))
print 'Initial read of tpv.fits:'
print 'error in ra = ', (ra1 - true_ra) * 180. * 3600. / numpy.pi, 'arcsec'
print 'error in dec = ', (dec1 - true_dec) * 180. * 3600. / numpy.pi, 'arcsec'

# Now cycle through writing and reading to a file
Ejemplo n.º 3
0
def wcsalign(hdu_in, header, outname=None, clobber=False):
    """
    Align one FITS image to a specified header

    Requires pyast.

    Parameters
    ----------
    hdu_in : `~astropy.io.fits.PrimaryHDU`
        The HDU to reproject (must have header & data)
    header : `~astropy.io.fits.Header`
        The target header to project to
    outname : str (optional)
        The filename to write to.
    clobber : bool
        Overwrite the file ``outname`` if it exists

    Returns
    -------
    The reprojected fits.PrimaryHDU

    Credits
    -------
    Written by David Berry and adapted to functional form by Adam Ginsburg
    ([email protected])
    """
    try:
        import starlink.Ast as Ast
        import starlink.Atl as Atl
    except ImportError:
        raise ImportError("starlink could not be imported, wcsalign is not "
                          "available")

    #  Create objects that will transfer FITS header cards between an AST
    #  FitsChan and the fits header describing the primary HDU of the
    #  supplied FITS file.
    adapter_in = Atl.PyFITSAdapter(hdu_in)
    hdu_ref = pyfits.PrimaryHDU(header=header)
    adapter_ref = Atl.PyFITSAdapter(hdu_ref)

    #  Create a FitsChan for each and use the above adapters to copy all the
    #  header cards into it.
    fitschan_in = Ast.FitsChan(adapter_in, adapter_in)
    fitschan_ref = Ast.FitsChan(adapter_ref, adapter_ref)

    #  Get the flavour of FITS-WCS used by the header cards currently in the
    #  input FITS file. This is so that we can use the same flavour when we
    #  write out the modified WCS.
    encoding = fitschan_in.Encoding

    #  Read WCS information from the two FitsChans. Additionally, this removes
    #  all WCS information from each FitsChan. The returned wcsinfo object
    #  is an AST FrameSet, in which the current Frame describes WCS coordinates
    #  and the base Frame describes pixel coodineates. The FrameSet includes a
    #  Mapping that specifies the transformation between the two Frames.
    wcsinfo_in = fitschan_in.read()
    wcsinfo_ref = fitschan_ref.read()

    #  Check that the input FITS header contained WCS in a form that can be
    #  understood by AST.
    if wcsinfo_in is None:
        raise ValueError(
            "Failed to read WCS information from {0}".format(hdu_in))

    #  This is restricted to 2D arrays, so check theinput FITS file has 2
    #  pixel axes (given by Nin) and 2 WCS axes (given by Nout).
    elif wcsinfo_in.Nin != 2 or wcsinfo_in.Nout != 2:
        raise ValueError("{0} is not 2-dimensional".format(hdu_in))

    #  Check the reference FITS file in the same way.
    elif wcsinfo_ref is None:
        raise ValueError(
            "Failed to read WCS information from {0}".format(hdu_ref))

    elif wcsinfo_ref.Nin != 2 or wcsinfo_ref.Nout != 2:
        raise ValueError("{0} is not 2-dimensional".format(hdu_ref))

    #  Proceed if the WCS information was read OK.

    #  Attempt to get a mapping from pixel coords in the input FITS file to
    #  pixel coords in the reference fits file, with alignment occuring by
    #  preference in the current WCS frame. Since the pixel coordinate frame
    #  will be the base frame in each Frameset, we first invert the
    #  FrameSets. This is because the Convert method aligns current Frames,
    #  not base frames.
    wcsinfo_in.invert()
    wcsinfo_ref.invert()
    alignment_fs = wcsinfo_in.convert(wcsinfo_ref)

    #  Invert them again to put them back to their original state (i.e.
    #  base frame = pixel coords, and current Frame = WCS coords).
    wcsinfo_in.invert()
    wcsinfo_ref.invert()

    #  Check alignment was possible.
    if alignment_fs is None:
        raise Exception("Cannot find a common coordinate system shared by "
                        "{0} and {1}".format(hdu_in, hdu_ref))

    else:
        #  Get the lower and upper bounds of the input image in pixel
        #  indices.  All FITS arrays by definition have lower pixel bounds of
        #  [1, 1] (unlike NDFs). Note, unlike fits AST uses FITS ordering for
        #  storing pixel axis values in an array (i.e. NAXIS1 first, NAXIS2
        #  second, etc).
        lbnd_in = [1, 1]
        ubnd_in = [fitschan_in["NAXIS1"], fitschan_in["NAXIS2"]]

        #  Find the pixel bounds of the input image within the pixel coordinate
        #  system of the reference fits file.
        (lb1, ub1, xl, xu) = alignment_fs.mapbox(lbnd_in, ubnd_in, 1)
        (lb2, ub2, xl, xu) = alignment_fs.mapbox(lbnd_in, ubnd_in, 2)

        #  Calculate the bounds of the output image.
        lbnd_out = [int(lb1), int(lb2)]
        ubnd_out = [int(ub1), int(ub2)]

        #  Unlike NDFs, FITS images cannot have an arbitrary pixel origin so
        #  we need to ensure that the bottom left corner of the input image
        #  gets mapped to pixel [1,1] in the output. To do this we, extract the
        #  mapping from the alignment FrameSet and add on a ShiftMap (a mapping
        #  that just applies a shift to each axis).
        shift = [1 - lbnd_out[0], 1 - lbnd_out[1]]

        alignment_mapping = alignment_fs.getmapping()
        shiftmap = Ast.ShiftMap(shift)
        total_map = Ast.CmpMap(alignment_mapping, shiftmap)

        #  Modify the pixel bounds of the output image to take account of this
        #  shift of origin.
        lbnd_out[0] += shift[0]
        lbnd_out[1] += shift[1]
        ubnd_out[0] += shift[0]
        ubnd_out[1] += shift[1]

        #  Get the value used to represent missing pixel values
        if "BLANK" in fitschan_in:
            badval = fitschan_in["BLANK"]
            flags = Ast.USEBAD
        else:
            badval = 0
            flags = 0

        # Resample the data array using the above mapping.
        # total_map was pixmap; is this right?
        (npix, out, out_var) = total_map.resample(lbnd_in, ubnd_in,
                                                  hdu_in.data, None,
                                                  Ast.LINEAR, None, flags,
                                                  0.05, 1000, badval, lbnd_out,
                                                  ubnd_out, lbnd_out, ubnd_out)

        #  Store the aligned data in the primary HDU, and update the NAXISi
        #  keywords to hold the number of pixels along each edge of the
        #  rotated image.
        hdu_in.data = out
        fitschan_in["NAXIS1"] = ubnd_out[0] - lbnd_out[0] + 1
        fitschan_in["NAXIS2"] = ubnd_out[1] - lbnd_out[1] + 1

    #  The WCS to store in the output is the same as the reference WCS
    #  except for the extra shift of origin. So use the above shiftmap to
    #  remap the pixel coordinate frame in the reference WCS FrameSet. We
    #  can then use this FrameSet as the output FrameSet.
    wcsinfo_ref.remapframe(Ast.BASE, shiftmap)

    #  Attempt to write the modified WCS information to the primary HDU (i.e.
    #  convert the FrameSet to a set of FITS header cards stored in the
    #  FITS file). Indicate that we want to use original flavour of FITS-WCS.
    fitschan_in.Encoding = encoding
    fitschan_in.clear('Card')

    if fitschan_in.write(wcsinfo_ref) == 0:
        raise Exception("Failed to convert the aligned WCS to Fits-WCS")

    #  If successful, force the FitsChan to copy its contents into the
    #  fits header, then write the changed data and header to the output
    #  FITS file.
    else:
        fitschan_in.writefits()

    if outname is not None:
        hdu_in.writeto(outname, clobber=clobber)

    return hdu_in
Ejemplo n.º 4
0
from __future__ import print_function

try:
    from astrop.io import fits as pyfits
except ImportError:
    import pyfits
import starlink.Atl as Atl
import starlink.Ast as Ast
import matplotlib.pyplot

#  Use pyfits to open a test files file
ffile = pyfits.open( 'starlink/ast/test/cobe.fit' )

#  Use matplotlib to plot an annotated grid of the WCS coords
Atl.plotfitswcs( matplotlib.pyplot.figure(figsize=(8,8)).add_subplot(111),
                 [ 0.1, 0.1, 0.9, 0.9 ], ffile )
matplotlib.pyplot.show()

#  Create a FitsChan telling it to use the pyfits primary hdu as the
#  external data source and sink. Note, we take the default value of
#  "True" for the "clear" property when creating the PyFITSADapater,
#  which means the PyFITS header will be cleared immediately before
#  the FitsChan.writefits() method writes to it.
adapter = Atl.PyFITSAdapter(ffile)
fc = Ast.FitsChan( adapter, adapter )

#  Read the FrameSet from the FitsChan. This will read all headers from
#  the pyfits hdu into the FitsChan, create a FrameSet from the WCS
#  headers, and remove all WCS-related headers from the FitsChan (but not
#  the pyfits primary hdu as yet).
fs = fc.read()
Ejemplo n.º 5
0
def rotate(infile, outfile, angle, xcen=None, ycen=None):
    #  Open the FITS file using pyfits. A list of the HDUs in the FITS file is
    #  returned.
    hdu_list = pyfits.open(infile)

    #  Create an object that will transfer FITS header cards between an AST
    #  FitsChan and the PyFITS header describing the primary HDU of the
    #  supplied FITS file.
    adapter = Atl.PyFITSAdapter(hdu_list[0])

    #  Create a FitsChan and use the above adapter to copy all the header
    #  cards into it.
    fitschan = Ast.FitsChan(adapter, adapter)

    #  Get the flavour of FITS-WCS used by the header cards currently in the
    #  FitsChan. This is so that we can use the same flavour when we write
    #  out the modified WCS.
    encoding = fitschan.Encoding

    #  Read WCS information from the FitsChan. Additionally, this removes all
    #  WCS information from the FitsChan. The returned wcsinfo object
    #  is an AST FrameSet, in which the current Frame describes WCS coordinates
    #  and the base Frame describes pixel coodineates. The FrameSet includes a
    #  Mapping that specifies the transformation between the two Frames.
    wcsinfo = fitschan.read()

    #  Check that the FITS header contained WCS in a form that can be
    #  understood by AST.
    if wcsinfo == None:
        print("Failed to read WCS information from {0}".format(infile))

    #  Rotation is restricted to 2D arrays, so check the FITS file has 2 pixel
    #  axes (given by Nin) and 2 WCS axes (given by Nout).
    elif wcsinfo.Nin != 2 or wcsinfo.Nout != 2:
        print("{0} is not 2-dimensional".format(infile))

    #  Proceed if the WCS information was read OK.
    else:

        #  Get the lower and upper bounds of the input image in pixel indices.
        #  All FITS arrays by definition have lower pixel bounds of [1,1] (unlike
        #  NDFs). Note, unlike pyfits AST uses FITS ordering for storing pixel axis
        #  values in an array (i.e. NAXIS1 first, NAXIS2 second, etc).
        lbnd_in = [1, 1]
        ubnd_in = [fitschan["NAXIS1"], fitschan["NAXIS2"]]

        #  Get the rotation angle and convert from degrees to radians.
        angle = float(angle) * Ast.DD2R

        #  Construct a MatrixMap that rotates by the required angle, converted to radians.
        sinang = math.sin(angle)
        cosang = math.cos(angle)
        pixmap = Ast.MatrixMap([[cosang, sinang], [-sinang, cosang]])

        #  If supplied, get the centre of rotation.
        if (xcen is not None) and (ycen is not None):
            #  Create aShiftMap to shift the origin and combine it with the
            #  MatrixMap, to give the desired centre of rotation (shift,
            #  rotate, then shift back again).
            shiftmap = Ast.ShiftMap([-xcen, -ycen])
            pixmap = Ast.CmpMap(shiftmap, pixmap)
            shiftmap.invert()
            pixmap = Ast.CmpMap(pixmap, shiftmap)

            # The dimensions of the output array are the same as the input array.
            lbnd_out = lbnd_in
            ubnd_out = ubnd_in

        #  If no centre of rotation was specified, we use the MatrixMap
        #  unchanged, and determine the bounds of the smallest output image that
        #  will hold the entire rotated input image. These are with respect to
        #  the pixel coordinates of the input array, and so some corners may have
        #  negative pixel coordinates.
        else:
            (lb1, ub1, xl, xu) = pixmap.mapbox(lbnd_in, ubnd_in, 1)
            (lb2, ub2, xl, xu) = pixmap.mapbox(lbnd_in, ubnd_in, 2)
            lbnd_out = [int(lb1), int(lb2)]
            ubnd_out = [int(ub1), int(ub2)]

        #  Get the value used to represent missing pixel values
        if "BLANK" in fitschan:
            badval = fitschan["BLANK"]
            flags = Ast.USEBAD
        else:
            badval = 0
            flags = 0

        # Resample the data array using the above mapping.
        (npix, out, out_var) = pixmap.resample(lbnd_in, ubnd_in,
                                               hdu_list[0].data, None,
                                               Ast.LINEAR, None, flags, 0.05,
                                               1000, badval, lbnd_out,
                                               ubnd_out, lbnd_out, ubnd_out)

        #  Store the rotated data in the HDU, and update the NAXISi keywords
        #  to hold the number of pixels along each edge of the rotated image.
        hdu_list[0].data = out
        fitschan["NAXIS1"] = ubnd_out[0] - lbnd_out[0] + 1
        fitschan["NAXIS2"] = ubnd_out[1] - lbnd_out[1] + 1

        #  Move the pixel origin of the output from "lbnd_out" to (1,1). Create a
        #  suitable ShiftMap, and append it to the total "old pixel coord" to
        #  "new pixel coords" mapping.
        shiftmap = Ast.ShiftMap([-lbnd_out[0], -lbnd_out[1]])
        pixmap = Ast.CmpMap(pixmap, shiftmap)

        #  Re-map the base Frame (i.e. the pixel coordinates Frame) so that it
        #  refers to the new data grid instead of the old one.
        wcsinfo.remapframe(Ast.BASE, pixmap)

        #  Attempt to write the modified WCS information to the primary HDU (i.e.
        #  convert the FrameSet to a set of FITS header cards stored in the
        #  FITS file). Indicate that we want to use original flavour of FITS-WCS.
        fitschan.Encoding = encoding
        fitschan.clear('Card')
        if fitschan.write(wcsinfo) == 0:
            print("Failed to convert the rotated WCS to Fits-WCS")

        #  If successfull, force the FitsCHan to copy its contents into the
        #  PyFITS header, then write the changed data and header to the output
        #  FITS file.
        else:
            fitschan.writefits()
            hdu_list.writeto(outfile, clobber=True, output_verify='ignore')
Ejemplo n.º 6
0
#     - Requires pyast version 2.3

import pyfits
import sys
import math
import starlink.Ast as Ast
import starlink.Atl as Atl

#  Open the FITS file using pyfits. A list of the HDUs in the FITS file is
#  returned.
hdu_list = pyfits.open( sys.argv[1] )

#  Create an object that will transfer FITS header cards between an AST
#  FitsChan and the PyFITS header describing the primary HDU of the
#  supplied FITS file.
adapter = Atl.PyFITSAdapter( hdu_list[ 0 ] )

#  Create a FitsChan and use the above adapter to copy all the header
#  cards into it.
fitschan = Ast.FitsChan( adapter, adapter )

#  Get the flavour of FITS-WCS used by the header cards currently in the
#  FitsChan. This is so that we can use the same flavour when we write
#  out the modified WCS.
encoding = fitschan.Encoding

#  Read WCS information from the FitsChan. Additionally, this removes all
#  WCS information from the FitsChan. The returned wcsinfo object
#  is an AST FrameSet, in which the current Frame describes WCS coordinates
#  and the base Frame describes pixel coodineates. The FrameSet includes a
#  Mapping that specifies the transformation between the two Frames.
Ejemplo n.º 7
0
# This script is featured on pyast issue page:
# https://github.com/timj/starlink-pyast/issues/8
# It also constructs the file tanflip.fits, which we use in the test suite.
# PyAst natively flips the order of RA and Dec when writing this file as a TAN WCS.
# This was a kind of input that wasn't otherwise featured in our test suite, but is
# apparently allowed by the fits standard.  So I added it.

import starlink.Atl as Atl
import starlink.Ast as Ast
import astropy.io.fits as pyfits
import numpy

# http://fits.gsfc.nasa.gov/registry/tpvwcs/tpv.fits
hdu = pyfits.open('tpv.fits')[0]
fc = Ast.FitsChan(Atl.PyFITSAdapter(hdu))
wcs = fc.read()

# A random test position.  The "true" RA, Dec values are taken from ds9.
'033009.340034', '-284350.811107', 418, 78, 2859.53882
x = 418
y = 78
true_ra = (3 + 30/60. + 9.340034/3600.) * numpy.pi / 12.
true_dec = -(28 + 43/60. + 50.811107/3600.) * numpy.pi / 180.

ra1, dec1 = wcs.tran( numpy.array([ [x], [y] ]))
print 'Initial read of tpv.fits:'
print 'error in ra = ',(ra1-true_ra) * 180.*3600./numpy.pi, 'arcsec'
print 'error in dec = ',(dec1-true_dec) * 180.*3600./numpy.pi, 'arcsec'

# Now cycle through writing and reading to a file