Exemple #1
0
def check_hpxmap(hpxmap, pixel, nside):
    # Check if healsparse is installed
    if pixel is None and not hp.isnpixok(hpxmap.shape[-1]):
        msg = "'hpxmap' has invalid dimension: %s" % (hpxmap.shape)
        raise Exception(msg)

    if pixel is not None and nside is None:
        msg = "'nside' must be specified for explicit maps"
        raise Exception(msg)

    if pixel is not None and (hpxmap.shape != pixel.shape):
        msg = "'hpxmap' and 'pixel' must have same shape"
        raise Exception(msg)
Exemple #2
0
def get_face(Imag, face, nested=False):
    #Get a given face in the image
    npix = np.shape(Imag)[0]
    if not hp.isnpixok(npix):
        raise ValueError('Incorrect map size {0}'.format(npix))
    nside = hp.npix2nside(npix)
    taille_face = npix / 12
    cote = int(math.sqrt(taille_face))
    CubeFace = np.zeros((cote, cote))
    if (nested != True):
        NewIm = hp.reorder(Imag, r2n=True)
    else:
        NewIm = Imag
    index = np.zeros((cote, cote))
    index = getAllIndicesForFace(nside, 0, nested=True)
    print("Process Face {0}".format(face))
    CubeFace[:, :] = np.resize(NewIm[index + taille_face * face], (cote, cote))
    return CubeFace
Exemple #3
0
def FilterMap(pixmap, freq, mask=None, lmax=None, pixelwindow=False):
    if mask is None:
        mask = np.ones_like(pixmap)
    else:
        assert hp.isnpixok(mask.size)
    Fl = GetFl(pixmap,freq, mask=mask, lmax=lmax)
    alm_fname='alm_%s.fits'%freq
    if not os.path.exists(alm_fname):
	print "computing alms"
	alm = hp.map2alm(pixmap, lmax=lmax)
	hp.write_alm(alm_fname,alm)
    else:
	print "reading alms",alm_fname
	alm = hp.read_alm(alm_fname)
    if pixelwindow:
	print "Correcting alms for pixelwindow"
        pl=hp.pixwin(hp.npix2nside(pixmap.size))[:lmax+1]
    else: pl=np.ones(len(Fl))
    return hp.alm2map(hp.almxfl(alm, Fl/pl), hp.npix2nside(pixmap.size), lmax=lmax), Fl
Exemple #4
0
def put_all_faces(CubeFace, nested=False):
    #From a cube of face, reconstruct an HEALPix image
    npix = np.size(CubeFace)
    if not hp.isnpixok(npix):
        raise ValueError('Incorrect cube size {0}'.format(npix))
    nside = hp.npix2nside(npix)
    taille_face = npix / 12
    cote = int(math.sqrt(taille_face))
    Imag = np.zeros((npix))
    index = np.zeros((cote, cote))
    index = getAllIndicesForFace(nside, 0, nested=True)
    for face in range(12):
        print("Process Face {0}".format(face))
        Imag[index + taille_face * face] = np.resize(CubeFace[face, :, :],
                                                     (cote, cote))

    if (nested != True):
        NewIm = hp.reorder(Imag, n2r=True)
    else:
        NewIm = Imag
    return NewIm
Exemple #5
0
def get_all_faces(Imag, nested=False):
    #Get the cube of faces
    npix = np.shape(Imag)[0]
    if not hp.isnpixok(npix):
        raise ValueError('Incorrect map size {0}'.format(npix))
    nside = hp.npix2nside(npix)
    taille_face = npix / 12
    cote = int(math.sqrt(taille_face))
    print(cote)
    CubeFace = np.zeros((12, cote, cote))
    if (nested != True):
        NewIm = hp.reorder(Imag, r2n=True)
    else:
        NewIm = Imag
    index = np.zeros((cote, cote))
    index = getAllIndicesForFace(nside, 0, nested=True)
    for face in range(12):
        print("Process Face {0}".format(face))
        CubeFace[face, :, :] = np.resize(NewIm[index + taille_face * face],
                                         (cote, cote))
        #plt.figure(),imshow(np.log10(1+CubeFace[face,:,:]*1e6))
        #plt.title("face {0}".format(face)),plt.colorbar()
    return CubeFace
Exemple #6
0
def getAll2DPatches(Imag,
                    PatchWidth,
                    nested=False,
                    BorderCap=False,
                    Verbose=False):
    #From one image, get all patches
    npix = np.shape(Imag)[0]
    if not hp.isnpixok(npix):
        raise ValueError('Incorrect map size {0}'.format(npix))
    nside = hp.npix2nside(npix)
    if (nside < PatchWidth):
        raise ValueError('Incorrect PatchWidth {0} vs nside {1}'.format(
            PatchWidth, nside))
    Patches2D = np.zeros((npix, PatchWidth, PatchWidth))
    NPatchPerFace = np.zeros((12), dtype='int64')
    NPatchPerSide = nside
    offsetPatchIndex = 0
    #Process all patches without -1
    for f in range(12):
        if (Verbose):
            print("PROCESS FACE {0}".format(f))
        FaceIndices = constructExtendedFaceIndices(nside,
                                                   f,
                                                   PatchWidth,
                                                   nested=nested)
        ExtendedFace = Imag[FaceIndices.astype('int64')]
        FacePatches2D = np.array([
            ExtendedFace[y:y + PatchWidth, x:x + PatchWidth]
            for y in range(nside) for x in range(nside)
            if -1 not in FaceIndices[y:y + PatchWidth, x:x + PatchWidth]
        ])
        NPatchPerFace[f] = np.shape(FacePatches2D)[0]
        Patches2D[offsetPatchIndex:offsetPatchIndex+NPatchPerFace[f]]=\
                                                                FacePatches2D
        offsetPatchIndex = offsetPatchIndex + NPatchPerFace[f]
    Patches2D = Patches2D[0:offsetPatchIndex, :, :]
    return np.array(Patches2D), NPatchPerFace
Exemple #7
0
def read_map(filename, nest=False, hdu=None, h=False, verbose=True):
    """Read a healpix map from a fits file.  Partial-sky files,
    if properly identified, are expanded to full size and filled with UNSEEN.
    Uses fitsio to mirror much (but not all) of the functionality of healpy.read_map
    
    Parameters:
    -----------
    filename : str 
      the fits file name
    nest : bool, optional
      If True return the map in NEST ordering, otherwise in RING ordering;
      use fits keyword ORDERING to decide whether conversion is needed or not
      If None, no conversion is performed.
    hdu : int, optional
      the header number to look at (start at 0)
    h : bool, optional
      If True, return also the header. Default: False.
    verbose : bool, optional
      If True, print a number of diagnostic messages
    
    Returns
    -------
    m [, header] : array, optionally with header appended
      The map read from the file, and the header if *h* is True.
    """

    data, hdr = fitsio.read(filename, header=True, ext=hdu)

    nside = int(hdr.get('NSIDE'))
    if verbose: print('NSIDE = {0:d}'.format(nside))

    if not healpy.isnsideok(nside):
        raise ValueError('Wrong nside parameter.')
    sz = healpy.nside2npix(nside)

    ordering = hdr.get('ORDERING', 'UNDEF').strip()
    if verbose: print('ORDERING = {0:s} in fits file'.format(ordering))

    schm = hdr.get('INDXSCHM', 'UNDEF').strip()
    if verbose: print('INDXSCHM = {0:s}'.format(schm))
    if schm == 'EXPLICIT':
        partial = True
    elif schm == 'IMPLICIT':
        partial = False

    # monkey patch on a field method
    fields = data.dtype.names

    # Could be done more efficiently (but complicated) by reordering first
    if hdr['INDXSCHM'] == 'EXPLICIT':
        m = healpy.UNSEEN * np.ones(sz, dtype=data[fields[1]].dtype)
        m[data[fields[0]]] = data[fields[1]]
    else:
        m = data[fields[0]].ravel()

    if (not healpy.isnpixok(m.size) or (sz > 0 and sz != m.size)) and verbose:
        print('nside={0:d}, sz={1:d}, m.size={2:d}'.format(nside, sz, m.size))
        raise ValueError('Wrong nside parameter.')
    if nest is not None:
        if nest and ordering.startswith('RING'):
            idx = healpy.nest2ring(nside, np.arange(m.size, dtype=np.int32))
            m = m[idx]
            if verbose: print('Ordering converted to NEST')
        elif (not nest) and ordering.startswith('NESTED'):
            idx = healpy.ring2nest(nside, np.arange(m.size, dtype=np.int32))
            m = m[idx]
            if verbose: print('Ordering converted to RING')

    if h: return m, hdr
    else: return m
    def validate(self):
        """Checks for correct input format."""
        if (self.point_source_pos is None) != (self.point_source_flux is None):
            raise ValueError("Either both or neither of point_source_pos and "
                             "point_source_flux must be given.")

        if self.sky_intensity is not None and not healpy.isnpixok(self.n_pix):
            raise ValueError("The sky_intensity map is not compatible with "
                             "healpy.")

        if self.point_source_pos is None and self.sky_intensity is None:
            raise ValueError("You must pass at least one of sky_intensity or "
                             "point_sources.")

        if np.max(self.beam_ids) >= self.n_beams:
            raise ValueError("The number of beams provided must be at least "
                             "as great as the greatest beam_id.")

        if self.point_source_flux is not None:
            if self.point_source_flux.shape[0] != self.sky_freqs.shape[0]:
                raise ValueError("point_source_flux must have the same number "
                                 "of freqs as sky_freqs.")

        if self.point_source_flux is not None:
            flux_shape = self.point_source_flux.shape
            pos_shape = self.point_source_pos.shape
            if (flux_shape[1] != pos_shape[0]):
                raise ValueError("Number of sources in point_source_flux and "
                                 "point_source_pos is different.")

        if (self.sky_intensity is not None
                and self.sky_intensity.shape[0] != self.sky_freqs.shape[0]):
            raise ValueError("sky_intensity has a different number of freqs "
                             "than sky_freqs.")

        if self.sky_intensity is not None and self.sky_intensity.ndim != 2:
            raise ValueError("sky_intensity must be a 2D array (a healpix map "
                             "per frequency).")

        if not self.point_source_ability and self.point_source_pos is not None:
            warnings.warn("This visibility simulator is unable to explicitly "
                          "simulate point sources. Adding point sources to "
                          "diffuse pixels.")
            if self.sky_intensity is None:
                self.sky_intensity = 0
            self.sky_intensity += self.convert_point_sources_to_healpix(
                self.point_source_pos, self.point_source_flux, self.nside
            )

        if not self.diffuse_ability and self.sky_intensity is not None:
            warnings.warn("This visibility simulator is unable to explicitly "
                          "simulate diffuse structure. Converting diffuse "
                          "intensity to approximate points.")

            (pos,
             flux) = self.convert_healpix_to_point_sources(self.sky_intensity)

            if self.point_source_pos is None:
                self.point_source_pos = pos
                self.point_source_flux = flux
            else:
                self.point_source_flux = \
                    np.hstack((self.point_source_flux, flux))
                self.point_source_pos = np.hstack((self.point_source_pos, pos))

            self.sky_intensity = None
def healpix2sine(hpx_array,
                 ra,
                 dec,
                 size,
                 res,
                 hpx_coord='C',
                 return_fits_header=False):
    """
    Generate a SIN (orthographic) projected FITS image from a HEALPix image.

    Parameters
    ----------
    hpx_array: numpy.ndarray
        A HEALPix array (single-column format).
    ra: float, range=[0,360]
        Right ascension at the center of the FITS images in degree.
    dec: float, range=[90,-90]
        Declination at the center of the FITS images in degree.
    size: integer
        Size of the output FITS image in number of pixels.
        Only support a square image.
    res: float
        Angular resolution of the FITS image in degree,
        i.e. 'CDELTi' FITS keyword.
    hpx_coord : {'C', 'E' or 'G'}, optional
        Coordinate of the HEALPix map. 'C' for Celestial (default),
        'E' for Ecliptic, and 'G' for Galactic.
    return_fits_header: bool
        Return FITS header for the projected image and flip x and y dimension
        of the output array to match the FITS standard.
        Default is False.

    """
    # Check if healpix array is valid.
    if not hp.isnpixok(len(hpx_array)):
        raise IOError(
            'Number of pixels in a healpix array must be 12 * nside ** 2.')

    # Create a new WCS object and set up a SIN projection.
    w = WCS(naxis=2)
    w.wcs.crpix = [float(size / 2), float(size / 2)]
    w.wcs.cdelt = [-res, res]
    w.wcs.crval = [ra, dec]
    w.wcs.ctype = ["RA---SIN", "DEC--SIN"]
    w.wcs.cunit = ['deg', 'deg']
    w.wcs.equinox = 2000.0

    # Make pixel array of the projected map.
    x, y = np.mgrid[0:size, 0:size]

    # Convert pixel coordinates to celestial coordinates
    pixra, pixdec = w.wcs_pix2world(x.ravel(), y.ravel(), 0)
    pixra *= np.pi / 180.
    pixdec = np.pi * (90 - pixdec) / 180.  # Healpix dec is 0 to pi.

    # Make a mask out-of-sky values
    valid_pix = np.logical_not(np.isnan(pixra))

    # Remap celestial coordinate to the HELAPix coordinates if needed.
    if hpx_coord != 'C':
        rot = hp.Rotator(coord=['C', hpx_coord])
        pixdec[valid_pix], pixra[valid_pix] = \
            rot(pixdec[valid_pix], pixra[valid_pix])

    # Get the pixel values from a HEALPix map, using Bi-linear interpolation of
    # the 4 nearest neighbors.
    proj_map = np.zeros(size * size)
    proj_map[valid_pix] = hp.get_interp_val(hpx_array, pixdec[valid_pix],
                                            pixra[valid_pix])

    if return_fits_header:
        header = w.to_header()
        return proj_map.reshape((size, size)).T, header
    else:
        return proj_map.reshape((size, size))
Exemple #10
0
def hpx2sin(hpxfile,
            fitsfile,
            ra,
            dec,
            size=7480,
            res=0.015322941176470588,
            hpx_coord='C',
            hpx_array=None,
            hpx_multiplier=1,
            hdr=None):
    """
    Generate a SIN (orthographic) projected FITS images from a HEALPix image.

    Parameters
    ----------
    hpxfile: string
        Name of the input Healpix file.
    fitsfile: string
        Name of the output FITS images
    ra: float, range=[0,360]deg
        Right ascension at the center of the FITS images.
    dec: float, range=[90,-90]deg
        Declination at the center of the FITS images.
    size: integer, optional
        Size of the output FITS image in number of pixels. Default = 7480.
        Only support a square image.
    res: float, optional
        Angular resolution at the center pixel of the FITS image in degree
    hpx_coord : {'C', 'E' or 'G'}, optional
        The coordinates of the healpix map.
        'C' for Celestial (default), 'E' for Ecliptic, and 'G' for Galactic.
    hpx_array: array-like or None, optional
        Array of Healpix pixels.
        If provide, this array will be used instead of reading an array
        from hpxfile. hpxfile will still be used as a reference filename.
    hpx_multiplier: float, optional
        Multiplier to the healpix map before gridding.
    hdr: dict
        Additional FITS header to apply to the output FITS image.
        hdr=dict(KEYWORD1=value1,KEYWORD2=value2, ...), or
        hdr=dict(KEYWORD1=(value1, comment1), KEYWORD2=(value2, comment2), ...)

    Note
    ----
    The default combination of `size` and `res` give a half-sky SIN
    image with ~0.9" resolution, suitable for MWA simulation in MAPS.


    """
    print('hpx2sin {:s} {:s} {:.3f} {:.3f} {:d} {:f}'.format(
        hpxfile, fitsfile, ra, dec, size, res))
    if not hpx_array:
        hpx_array, hpx_hdr = hp.read_map(hpxfile, h=True)
    if not hp.isnpixok(len(hpx_array)):
        raise IOError('Number of pixels in a healpix array '
                      'must be 12 * nside ** 2.')

    # Create a new WCS object and set up a SIN projection.
    w = wcs.WCS(naxis=2)
    w.wcs.crpix = [float(size / 2), float(size / 2)]
    w.wcs.cdelt = [-res, res]
    w.wcs.crval = [ra, dec]
    w.wcs.ctype = ["RA---SIN", "DEC--SIN"]
    w.wcs.cunit = ['deg', 'deg']
    w.wcs.equinox = 2000.0

    # Write out the WCS object as a FITS header, adding additional
    # fits keyword as applied.
    header = w.to_header()
    header['DATE'] = (datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3],
                      'Date of file creation')
    # TODO: This program should keep history from healpix file
    header['HISTORY'] = 'hpx2sin hpxfile fitsfile ra dec size res'
    header['HISTORY'] = 'hpx2sin {:s} {:s} {:.3f} {:.3f} {:d} {:f}'\
        .format(hpxfile, fitsfile, ra, dec, size, res)
    if hdr:
        for key, value in hdr.iteritems():
            header[key] = value

    # Some pixel coordinates of interest.
    x, y = np.mgrid[0:size, 0:size]

    # Convert pixel coordinates to celestial world coordinates
    pixra, pixdec = w.wcs_pix2world(x.ravel(), y.ravel(), 0)
    pixra *= np.pi / 180.
    pixdec = np.pi * (90 - pixdec) / 180.  # Healpix dec is 0 to pi.
    valid_pix = np.logical_not(np.isnan(pixra))

    # Perform coordinate transformation if needed. Convert celestial world
    # coordinates to the Healpix world coordinates to grab the right
    # Healpix pixels.
    if hpx_coord != 'C':
        rot = hp.Rotator(coord=['C', hpx_coord])
        pixdec[valid_pix], pixra[valid_pix] = \
            rot(pixdec[valid_pix], pixra[valid_pix])

    # Get the pixel value from the HEALPix image
    proj_map = np.zeros(size * size)
    proj_map[valid_pix] = hp.get_interp_val(hpx_multiplier * hpx_array,
                                            pixdec[valid_pix],
                                            pixra[valid_pix])

    # Make a HDU object and save the FITS file. Axes in 2D numpy array are
    # ordered slow then fast, opposite to ordering in FITS convention, so
    # we have to save the transposed image array.
    hdu = fits.PrimaryHDU(data=proj_map.reshape((size, size)).T, header=header)
    hdu.writeto(fitsfile, clobber=True)
def smooth_combine(maps_and_weights, variance_maps_and_weights=None, fwhm=np.radians(2.0), degraded_nside=32, spectra=False, smooth_mask=False, spectra_mask=False, base_filename="out", root_folder=".", metadata={}, chi2=False):
    """Combine, smooth, take-spectra, write metadata

    The maps (I or IQU) are first combined with their own weights, then smoothed and degraded.
    This function writes a combined smoothed and degraded map, a spectra 1 or 6 components (not degraded) and a json file with metadata
    
    Parameters
    ----------
    maps_and_weights : list of tuples
        [(map1_array, map1_weight), (map2_array, map2_weight), ...]
        each tuple contains a I or IQU map to be combined with its own weight to give the final map
    variance_maps_and_weights : list of tuples
        same as maps_and_weights but containing variances
    fwhm : double
        smoothing gaussian beam width in radians
    degraded_nside : integer
        nside of the output map
    spectra : bool
        whether to compute and write angular power spectra of the combined map
    smooth_mask, spectra_mask : bool array
        masks for smoothing and spectra, same nside of input maps, masks shoud be true *inside* the masked region. spectra are masked with both masks. Typically smooth_mask should be a point source mask, while spectra_mask a galaxy plane mask.
    base_filename : string
        base filename of the output files
    root_folder : string
        root path of the output files
    metadata : dict
        initial state of the metadata to be written to the json files

    Returns
    -------
    None : all outputs are written to fits files
    """

    log.debug("smooth_combine")
    # check if I or IQU
    is_IQU = len(maps_and_weights[0][0]) == 3
    if not is_IQU:
        assert hp.isnpixok(len(maps_and_weights[0][0])), "Input maps must have either 1 or 3 components"

    combined_map = combine_maps(maps_and_weights)
    for m in combined_map:
        m.mask |= smooth_mask
    if not variance_maps_and_weights is None:
        combined_variance_map = combine_maps(variance_maps_and_weights)
        for m in combined_variance_map:
            m.mask |= smooth_mask

    monopole_I, dipole_I = hp.fit_dipole(combined_map[0], gal_cut=30)
    # remove monopole, only I
    combined_map[0] -= monopole_I

    if spectra:
        # save original masks
        orig_mask = [m.mask.copy() for m in combined_map] 

        # spectra
        log.debug("Anafast")
        for m in combined_map:
            m.mask |= spectra_mask
        # dividing by two in order to recover the same noise as the average map (M1 - M2)/2
        cl = hp.anafast([m/2. for m in combined_map])
        # sky fraction
        sky_frac = (~combined_map[0].mask).sum()/float(len(combined_map[0]))

        if is_IQU:
            for cl_comp in cl:
                cl_comp /= sky_frac
        else:
            cl /= sky_frac

        # write spectra
        log.debug("Write cl: " + base_filename + "_cl.fits")
        try:
            hp.write_cl(os.path.join(root_folder, base_filename + "_cl.fits"), cl)
        except exceptions.NotImplementedError:
            log.error("Write IQU Cls to fits requires more recent version of healpy")
        del cl

        if not variance_maps_and_weights is None:
            # expected cl from white noise
            # /4. to have same normalization of cl
            metadata["whitenoise_cl"] = utils.get_whitenoise_cl(combined_variance_map[0]/4., mask=combined_map[0].mask) / sky_frac
            if is_IQU:
                # /2. is the mean, /4. is the half difference in power
                metadata["whitenoise_cl_P"] = utils.get_whitenoise_cl((combined_variance_map[1] + combined_variance_map[2])/2./4., mask=combined_map[1].mask | combined_map[2].mask) / sky_frac 

        # restore masks
        for m, mask in zip(combined_map, orig_mask):
            m.mask = mask

    # smooth
    log.debug("Smooth")

    smoothed_map = hp.smoothing(combined_map, fwhm=fwhm)

    if not variance_maps_and_weights is None:
        log.debug("Smooth Variance")
        if is_IQU:
            smoothed_variance_map = [utils.smooth_variance_map(var, fwhm=fwhm) for var in combined_variance_map]
            for comp,m,var in zip("IQU", smoothed_map, smoothed_variance_map):
                 metadata["map_chi2_%s" % comp] = np.mean(m**2 / var) 
            for comp,m,var in zip("IQU", combined_map, combined_variance_map):
                 metadata["map_unsm_chi2_%s" % comp] = np.mean(m**2 / var) 
        else:
            smoothed_variance_map = utils.smooth_variance_map(combined_variance_map[0], fwhm=fwhm)
            metadata["map_chi2"] = np.mean(smoothed_map**2 / smoothed_variance_map) 
            metadata["map_unsm_chi2"] = np.mean(combined_map[0]**2 / combined_variance_map[0]) 

        del smoothed_variance_map
    # removed downgrade of variance
    # smoothed_variance_map = hp.ud_grade(smoothed_variance_map, degraded_nside, power=2)

    # fits
    log.info("Write fits map: " + base_filename + "_map.fits")
    smoothed_map = hp.ud_grade(smoothed_map, degraded_nside)
    hp.write_map(os.path.join(root_folder, base_filename + "_map.fits"), smoothed_map)

    # metadata
    metadata["base_file_name"] = base_filename
    metadata["file_name"] = base_filename + "_cl.fits"
    metadata["file_type"] += "_cl"
    metadata["removed_monopole_I"] = monopole_I
    metadata["dipole_I"] = tuple(dipole_I)

    if spectra:
        metadata["sky_fraction"] = sky_frac
        with open(os.path.join(root_folder, base_filename + "_cl.json"), 'w') as f:
            json.dump(metadata, f, indent=4)

    metadata["file_name"] = base_filename + "_map.fits"
    metadata["file_type"] = metadata["file_type"].replace("_cl","_map")

    metadata["smooth_fwhm_deg"] = "%.2f" % np.degrees(fwhm)
    metadata["out_nside"] = degraded_nside
    if is_IQU:
        for comp,m in zip("IQU", smoothed_map):
             metadata["map_p2p_%s" % comp] = m.ptp()
             metadata["map_std_%s" % comp] = m.std()
    else:
        metadata["map_p2p_I"] = smoothed_map.ptp()
        metadata["map_std_I"] = smoothed_map.std()

    with open(os.path.join(root_folder, base_filename + "_map.json"), 'w') as f:
        json.dump(metadata, f, indent=4)
Exemple #12
0
def healpix2sine(hpx_array, ra, dec, size, res, hpx_coord='C',
            return_fits_header=False):
    """
    Generate a SIN (orthographic) projected FITS image from a HEALPix image.

    Parameters
    ----------
    hpx_array: numpy.ndarray
        A HEALPix array (single-column format).
    ra: float, range=[0,360]
        Right ascension at the center of the FITS images in degree.
    dec: float, range=[90,-90]
        Declination at the center of the FITS images in degree.
    size: integer
        Size of the output FITS image in number of pixels.
        Only support a square image.
    res: float
        Angular resolution of the FITS image in degree,
        i.e. 'CDELTi' FITS keyword.
    hpx_coord : {'C', 'E' or 'G'}, optional
        Coordinate of the HEALPix map. 'C' for Celestial (default),
        'E' for Ecliptic, and 'G' for Galactic.
    return_fits_header: bool
        Return FITS header for the projected image and flip x and y dimension
        of the output array to match the FITS standard.
        Default is False.

    """
    # Check if healpix array is valid.
    if not hp.isnpixok(len(hpx_array)):
        raise IOError(
            'Number of pixels in a healpix array must be 12 * nside ** 2.'
        )

    # Create a new WCS object and set up a SIN projection.
    w = WCS(naxis=2)
    w.wcs.crpix = [float(size / 2), float(size / 2)]
    w.wcs.cdelt = [-res, res]
    w.wcs.crval = [ra, dec]
    w.wcs.ctype = ["RA---SIN", "DEC--SIN"]
    w.wcs.cunit = ['deg', 'deg']
    w.wcs.equinox = 2000.0

    # Make pixel array of the projected map.
    x, y = np.mgrid[0:size, 0:size]

    # Convert pixel coordinates to celestial coordinates
    pixra, pixdec = w.wcs_pix2world(x.ravel(), y.ravel(), 0)
    pixra *= np.pi / 180.
    pixdec = np.pi * (90 - pixdec) / 180.  # Healpix dec is 0 to pi.

    # Make a mask out-of-sky values
    valid_pix = np.logical_not(np.isnan(pixra))

    # Remap celestial coordinate to the HELAPix coordinates if needed.
    if hpx_coord != 'C':
        rot = hp.Rotator(coord=['C', hpx_coord])
        pixdec[valid_pix], pixra[valid_pix] = \
            rot(pixdec[valid_pix], pixra[valid_pix])

    # Get the pixel values from a HEALPix map, using Bi-linear interpolation of
    # the 4 nearest neighbors.
    proj_map = np.zeros(size * size)
    proj_map[valid_pix] = hp.get_interp_val(
        hpx_array, pixdec[valid_pix], pixra[valid_pix]
    )

    if return_fits_header:
        header = w.to_header()
        return proj_map.reshape((size, size)).T, header
    else:
        return proj_map.reshape((size, size))