Example #1
0
def generate_safe_locations(sourcemask, Nperradius=1):
    """Given a bright source mask, generate SAFE (BADSKY) locations at its periphery.

    Parameters
    ----------
    sourcemask : :class:`recarray`
        A recarray containing a bright mask as made by, e.g.,
        :mod:`desitarget.brightmask.make_bright_star_mask` or
        :mod:`desitarget.brightmask.make_bright_source_mask`.
    Nperradius : :class:`int`, optional, defaults to 1 per arcsec of radius
        The number of safe locations to generate scaled by the radius of each mask
        in ARCSECONDS (i.e. the number of positions per arcsec of radius).

    Returns
    -------
    ra : array_like.
        The Right Ascensions of the SAFE (BADSKY) locations.
    dec : array_like.
        The Declinations of the SAFE (BADSKY) locations.

    Notes
    -----
        - See `Tech Note 2346`_ for details.
    """

    # ADM the radius of each mask in arcseconds with a 0.1% kick to
    # ADM ensure that positions are beyond the mask edges.
    radius = sourcemask["IN_RADIUS"]*1.001

    # ADM determine the number of SAFE locations to assign to each
    # ADM mask given the passed number of locations per unit radius.
    Nsafe = np.ceil(radius*Nperradius).astype('i')

    # ADM need to differentiate targets that are in ellipse-on-the-sky masks
    # ADM from targets that are in circle-on-the-sky masks.
    rex_or_psf = _rexlike(sourcemask["TYPE"]) | _psflike(sourcemask["TYPE"])
    w_ellipse = np.where(~rex_or_psf)
    w_circle = np.where(rex_or_psf)

    # ADM set up an array to hold coordinates around the mask peripheries.
    ras, decs = np.array([]), np.array([])

    # ADM generate the safe location for circular masks (which is quicker).
    if len(w_circle[0]) > 0:
        circras, circdecs = circle_boundaries(sourcemask[w_circle]["RA"],
                                              sourcemask[w_circle]["DEC"],
                                              radius[w_circle], Nsafe[w_circle])
        ras, decs = np.concatenate((ras, circras)), np.concatenate((decs, circdecs))

    # ADM generate the safe location for elliptical masks
    # ADM (which is slower as it requires a loop).
    if len(w_ellipse[0]) > 0:
        for w in w_ellipse[0]:
            ellras, elldecs = ellipse_boundary(sourcemask[w]["RA"],
                                               sourcemask[w]["DEC"], radius[w],
                                               sourcemask[w]["E1"],
                                               sourcemask[w]["E2"], Nsafe[w])
            ras, decs = np.concatenate((ras, ellras)), np.concatenate((decs, elldecs))

    return ras, decs
Example #2
0
def plot_mask(mask, limits=None, radius="IN_RADIUS", show=True):
    """Plot a mask or masks.

    Parameters
    ----------
    mask : :class:`recarray`
        A mask, as constructed by, e.g. :func:`make_bright_star_mask()`.
    limits : :class:`list`, optional
        RA/Dec plot limits in the form [ramin, ramax, decmin, decmax].
    radius : :class: `str`, optional
        Which mask radius to plot (``IN_RADIUS`` or ``NEAR_RADIUS``).
    show : :class:`boolean`
        If ``True``, then display the plot, Otherwise, just execute the
        plot commands so it can be added to or saved to file later.

    Returns
    -------
    Nothing
    """
    # ADM make this work even for a single mask.
    mask = np.atleast_1d(mask)

    # ADM set up the plot.
    fig, ax = plt.subplots(1, figsize=(8, 8))

    plt.xlabel('RA (o)')
    plt.ylabel('Dec (o)')

    # ADM set up some default plot limits if they weren't passed.
    if limits is None:
        maskra, maskdec, tol = mask["RA"], mask["DEC"], mask[radius]/3600.
        limits = [np.max(maskra-tol), np.min(maskra+tol),
                  np.min(maskdec-tol), np.max(maskdec+tol)]
    ax.axis(limits)

    # ADM only consider a limited mask range corresponding to a few
    # ADM times the largest mask radius beyond the requested limits.
    # ADM remember that the passed mask sizes are in arcseconds.
    tol = 3.*np.max(mask[radius])/3600.
    # ADM the np.min/np.max combinations are to guard against people
    # ADM passing flipped RAs (so RA increases to the east).
    ii = ((mask["RA"] > np.min(limits[:2])-tol) &
          (mask["RA"] < np.max(limits[:2])+tol) &
          (mask["DEC"] > np.min(limits[-2:])-tol) &
          (mask["DEC"] < np.max(limits[-2:])+tol))
    if np.sum(ii) == 0:
        msg = 'No mask entries within specified limits ({})'.format(limits)
        log.error(msg)
        raise ValueError(msg)
    else:
        mask = mask[ii]

    # ADM create ellipse polygons for each entry in the mask and
    # ADM make a list of matplotlib patches for them.
    patches = []
    for i, ellipse in enumerate(mask):
        # ADM create points on the ellipse boundary.
        ras, decs = ellipse_boundary(
            ellipse["RA"], ellipse["DEC"],
            ellipse[radius], ellipse["E1"], ellipse["E2"])
        polygon = Polygon(np.array(list(zip(ras, decs))), True)
        patches.append(polygon)

    p = PatchCollection(patches, alpha=0.4, facecolors='b', edgecolors='b')
    ax.add_collection(p)

    if show:
        plt.show()

    return
Example #3
0
def plot_mask(mask, limits=None, radius="IN_RADIUS", show=True):
    """Make a plot of a mask and either display it or retain the plot object for over-plotting.

    Parameters
    ----------
    mask : :class:`recarray`
        A mask constructed by ``make_bright_source_mask``
        (or read in from file in the ``make_bright_source_mask`` format).
    limits : :class:`list`, optional
        The RA/Dec limits of the plot in the form [ramin, ramax, decmin, decmax].
    radius : :class: `str`, optional
        Which mask radius to plot (``IN_RADIUS`` or ``NEAR_RADIUS``). Both can be plotted
        by calling this function twice with show=False and then with ``over=True``.
    show : :class:`boolean`
        If ``True``, then display the plot, Otherwise, just execute the plot commands
        so it can be added to, shown or saved to file later.

    Returns
    -------
    Nothing
    """
    # ADM set up the default log.
    from desiutil.log import get_logger, DEBUG
    log = get_logger(DEBUG)

    # ADM make this work even for a single mask.
    mask = np.atleast_1d(mask)

    # ADM set up the plot.
    fig, ax = plt.subplots(1, figsize=(8, 8))

    plt.xlabel('RA (o)')
    plt.ylabel('Dec (o)')

    # ADM set up some default plot limits if they weren't passed.
    if limits is None:
        maskra, maskdec, tol = mask["RA"], mask["DEC"], mask[radius] / 3600.
        limits = [
            np.max(maskra - tol),
            np.min(maskra + tol),
            np.min(maskdec - tol),
            np.max(maskdec + tol)
        ]
    ax.axis(limits)

    # ADM only consider a limited mask range corresponding to a few
    # ADM times the largest mask radius beyond the requested limits.
    # ADM remember that the passed mask sizes are in arcseconds.
    tol = 3. * np.max(mask[radius]) / 3600.
    # ADM the np.min/np.max combinations are to guard against people
    # ADM passing flipped RAs (so RA increases to the east).
    w = np.where((mask["RA"] > np.min(limits[:2]) - tol)
                 & (mask["RA"] < np.max(limits[:2]) + tol)
                 & (mask["DEC"] > np.min(limits[-2:]) - tol)
                 & (mask["DEC"] < np.max(limits[-2:]) + tol))
    if len(w[0]) == 0:
        log.error(
            'No mask entries within specified limits ({})'.format(limits))
    else:
        mask = mask[w]

    # ADM create ellipse polygons for each entry in the mask and
    # ADM make a list of matplotlib patches for them.
    patches = []
    for i, ellipse in enumerate(mask):
        # ADM create points on the ellipse boundary.
        ras, decs = ellipse_boundary(ellipse["RA"], ellipse["DEC"],
                                     ellipse[radius], ellipse["E1"],
                                     ellipse["E2"])
        polygon = Polygon(np.array(list(zip(ras, decs))), True)
        patches.append(polygon)

    p = PatchCollection(patches, alpha=0.4, facecolors='b', edgecolors='b')
    ax.add_collection(p)

    if show:
        plt.show()

    return