Exemplo n.º 1
0
    def setup_class(self):
        self.data = np.arange(20.).reshape(5, 4)
        self.position = SkyCoord('13h11m29.96s -01d19m18.7s', frame='icrs')
        wcs = WCS(naxis=2)
        rho = np.pi / 3.
        scale = 0.05 / 3600.
        wcs.wcs.cd = [[scale * np.cos(rho), -scale * np.sin(rho)],
                      [scale * np.sin(rho), scale * np.cos(rho)]]
        wcs.wcs.ctype = ['RA---TAN', 'DEC--TAN']
        wcs.wcs.crval = [
            self.position.ra.to_value(u.deg),
            self.position.dec.to_value(u.deg)
        ]
        wcs.wcs.crpix = [3, 3]
        self.wcs = wcs

        # add SIP
        sipwcs = wcs.deepcopy()
        sipwcs.wcs.ctype = ['RA---TAN-SIP', 'DEC--TAN-SIP']
        a = np.array(
            [[0, 0, 5.33092692e-08, 3.73753773e-11, -2.02111473e-13],
             [0, 2.44084308e-05, 2.81394789e-11, 5.17856895e-13, 0.0],
             [-2.41334657e-07, 1.29289255e-10, 2.35753629e-14, 0.0, 0.0],
             [-2.37162007e-10, 5.43714947e-13, 0.0, 0.0, 0.0],
             [-2.81029767e-13, 0.0, 0.0, 0.0, 0.0]])
        b = np.array(
            [[0, 0, 2.99270374e-05, -2.38136074e-10, 7.23205168e-13],
             [0, -1.71073858e-07, 6.31243431e-11, -5.16744347e-14, 0.0],
             [6.95458963e-06, -3.08278961e-10, -1.75800917e-13, 0.0, 0.0],
             [3.51974159e-11, 5.60993016e-14, 0.0, 0.0, 0.0],
             [-5.92438525e-13, 0.0, 0.0, 0.0, 0.0]])
        sipwcs.sip = Sip(a, b, None, None, wcs.wcs.crpix)
        sipwcs.wcs.set()
        self.sipwcs = sipwcs
Exemplo n.º 2
0
    def extract(self, cutout_region):
        data = self.data
        data_shape = data.shape
        position, shape = self._get_position_shape(data_shape, cutout_region)
        logger.debug('Position {} and Shape {}'.format(position, shape))

        # No pixels specified, so return the entire HDU
        if (not position and not shape) or shape == data_shape:
            logger.debug('Returning entire HDU data for {}'.format(
                cutout_region.get_extension()))
            cutout_data = data
        else:
            logger.debug('Cutting out {} at {} for extension {} from  \
            {}.'.format(shape, position, cutout_region.get_extension(),
                        data.shape))
            cutout_data, position = extract_array(data,
                                                  shape,
                                                  position,
                                                  mode='partial',
                                                  return_position=True)

        if self.wcs:
            cutout_shape = cutout_data.shape
            output_wcs = deepcopy(self.wcs)
            wcs_crpix = output_wcs.wcs.crpix
            l_wcs_crpix = len(wcs_crpix)
            ranges = cutout_region.get_ranges()

            logger.debug('Adjusting WCS.')

            for idx, _ in enumerate(ranges):
                if idx < l_wcs_crpix:
                    wcs_crpix[idx] -= (ranges[idx][0] - 1.0)

            output_wcs._naxis = list(cutout_shape)

            if self.wcs.sip:
                curr_sip = self.wcs.sip
                output_wcs.sip = Sip(curr_sip.a, curr_sip.b, curr_sip.ap,
                                     curr_sip.bp, wcs_crpix[0:2])

            logger.debug('WCS adjusted.')
        else:
            logger.debug('No WCS present.')
            output_wcs = None

        return CutoutResult(data=cutout_data,
                            wcs=output_wcs,
                            wcs_crpix=wcs_crpix)
Exemplo n.º 3
0
    def set_wcs(self, x0):
        """
        x0 = [0:crpix1, 1:crpix2, 2:cdelt1, 3:cdelt2, 4:pc, 5:pc, 6:pc, 7:pc, sip... ]
        """
        # Referece Pixel
        self.w.wcs.crpix = [x0[0], x0[1]]

        # Set the cdelt values
        self.w.wcs.cdelt = [x0[2], x0[3]]

        # Set the pc matrix
        self.w.wcs.pc = x0[4:8].reshape((2, 2))

        # Make a new SIP
        if np.size(x0) > 8:
            a = x0[self.a_ind].reshape((self.a_order + 1, self.a_order + 1))
            b = x0[self.b_ind].reshape((self.b_order + 1, self.b_order + 1))
            self.w.sip = Sip(a, b, self.sip_zeros_a, self.sip_zeros_b,
                             self.w.wcs.crpix)
Exemplo n.º 4
0
    def extract(self, cutout_region):
        data = self.data
        data_shape = data.shape
        position, shape = self._get_position_shape(data_shape, cutout_region)
        self.logger.debug('Position {} and Shape {}'.format(position, shape))

        # No pixels specified, so return the entire HDU
        if (not position and not shape) or shape == data_shape:
            self.logger.debug('Returning entire HDU data for {}'.format(
                cutout_region.get_extension()))
            cutout_data = data
        else:
            self.logger.debug('Cutting out {} at {} for extension {} from {}.'.format(
                shape, position, cutout_region.get_extension(), data.shape))
            cutout_data, position = extract_array(data, shape, position, mode='partial', return_position=True)

        if self.wcs is not None:
            cutout_shape = cutout_data.shape
            output_wcs = deepcopy(self.wcs)
            wcs_crpix = output_wcs.wcs.crpix
            ranges = cutout_region.get_ranges()
            l_ranges = len(ranges)

            while len(wcs_crpix) < l_ranges:
                wcs_crpix = np.append(wcs_crpix, 1.0)

            for idx, _ in enumerate(ranges):
                wcs_crpix[idx] -= (ranges[idx][0] - 1)

            output_wcs._naxis = list(cutout_shape)

            if self.wcs.sip is not None:
                curr_sip = self.wcs.sip
                output_wcs.sip = Sip(curr_sip.a, curr_sip.b,
                                     curr_sip.ap, curr_sip.bp,
                                     wcs_crpix[0:2])
        else:
            output_wcs = None

        return CutoutResult(data=cutout_data, wcs=output_wcs, wcs_crpix=wcs_crpix)
Exemplo n.º 5
0
def fit_wcs_from_points(xy,
                        world_coords,
                        proj_point='center',
                        projection='TAN',
                        sip_degree=None):  # pragma: no cover
    """
    Given two matching sets of coordinates on detector and sky,
    compute the WCS.

    Fits a WCS object to matched set of input detector and sky coordinates.
    Optionally, a SIP can be fit to account for geometric
    distortion. Returns an `~astropy.wcs.WCS` object with the best fit
    parameters for mapping between input pixel and sky coordinates.

    The projection type (default 'TAN') can passed in as a string, one of
    the valid three-letter projection codes - or as a WCS object with
    projection keywords already set. Note that if an input WCS has any
    non-polynomial distortion, this will be applied and reflected in the
    fit terms and coefficients. Passing in a WCS object in this way essentially
    allows it to be refit based on the matched input coordinates and projection
    point, but take care when using this option as non-projection related
    keywords in the input might cause unexpected behavior.

    Notes
    ------
    - The fiducial point for the spherical projection can be set to 'center'
      to use the mean position of input sky coordinates, or as an
      `~astropy.coordinates.SkyCoord` object.
    - Units in all output WCS objects will always be in degrees.
    - If the coordinate frame differs between `~astropy.coordinates.SkyCoord`
      objects passed in for ``world_coords`` and ``proj_point``, the frame for
      ``world_coords``  will override as the frame for the output WCS.
    - If a WCS object is passed in to ``projection`` the CD/PC matrix will
      be used as an initial guess for the fit. If this is known to be
      significantly off and may throw off the fit, set to the identity matrix
      (for example, by doing wcs.wcs.pc = [(1., 0.,), (0., 1.)])

    Parameters
    ----------
    xy : tuple of two `numpy.ndarray`
        x & y pixel coordinates.
    world_coords : `~astropy.coordinates.SkyCoord`
        Skycoord object with world coordinates.
    proj_point : 'center' or ~astropy.coordinates.SkyCoord`
        Defaults to 'center', in which the geometric center of input world
        coordinates will be used as the projection point. To specify an exact
        point for the projection, a Skycoord object with a coordinate pair can
        be passed in. For consistency, the units and frame of these coordinates
        will be transformed to match ``world_coords`` if they don't.
    projection : str or `~astropy.wcs.WCS`
        Three letter projection code, of any of standard projections defined
        in the FITS WCS standard. Optionally, a WCS object with projection
        keywords set may be passed in.
    sip_degree : None or int
        If set to a non-zero integer value, will fit SIP of degree
        ``sip_degree`` to model geometric distortion. Defaults to None, meaning
        no distortion corrections will be fit.

    Returns
    -------
    wcs : `~astropy.wcs.WCS`
        The best-fit WCS to the points given.
    """

    from astropy.coordinates import SkyCoord  # here to avoid circular import
    import astropy.units as u
    from astropy.wcs import Sip
    from scipy.optimize import least_squares

    xp, yp = xy
    try:
        lon, lat = world_coords.data.lon.deg, world_coords.data.lat.deg
    except AttributeError:
        unit_sph = world_coords.unit_spherical
        lon, lat = unit_sph.lon.deg, unit_sph.lat.deg

    # verify input
    if (proj_point != 'center') and (type(proj_point) != type(world_coords)):
        raise ValueError("proj_point must be set to 'center', or an" +
                         "`~astropy.coordinates.SkyCoord` object with " +
                         "a pair of points.")
    if proj_point != 'center':
        assert proj_point.size == 1

    proj_codes = [
        'AZP', 'SZP', 'TAN', 'STG', 'SIN', 'ARC', 'ZEA', 'AIR', 'CYP', 'CEA',
        'CAR', 'MER', 'SFL', 'PAR', 'MOL', 'AIT', 'COP', 'COE', 'COD', 'COO',
        'BON', 'PCO', 'TSC', 'CSC', 'QSC', 'HPX', 'XPH'
    ]
    if type(projection) == str:
        if projection not in proj_codes:
            raise ValueError(
                "Must specify valid projection code from list of " +
                "supported types: ", ', '.join(proj_codes))
        # empty wcs to fill in with fit values
        wcs = celestial_frame_to_wcs(frame=world_coords.frame,
                                     projection=projection)
    else:  #if projection is not string, should be wcs object. use as template.
        wcs = copy.deepcopy(projection)
        wcs.cdelt = (1., 1.)  # make sure cdelt is 1
        wcs.sip = None

    # Change PC to CD, since cdelt will be set to 1
    if wcs.wcs.has_pc():
        wcs.wcs.cd = wcs.wcs.pc
        wcs.wcs.__delattr__('pc')

    if (type(sip_degree) != type(None)) and (type(sip_degree) != int):
        raise ValueError("sip_degree must be None, or integer.")

    # set pixel_shape to span of input points
    wcs.pixel_shape = (xp.max() + 1 - xp.min(), yp.max() + 1 - yp.min())

    # determine CRVAL from input
    close = lambda l, p: p[np.argmin(np.abs(l))]
    if str(proj_point) == 'center':  # use center of input points
        sc1 = SkyCoord(lon.min() * u.deg, lat.max() * u.deg)
        sc2 = SkyCoord(lon.max() * u.deg, lat.min() * u.deg)
        pa = sc1.position_angle(sc2)
        sep = sc1.separation(sc2)
        midpoint_sc = directional_offset_by(sc1, pa, sep / 2)
        wcs.wcs.crval = ((midpoint_sc.data.lon.deg, midpoint_sc.data.lat.deg))
        wcs.wcs.crpix = ((xp.max() + xp.min()) / 2.,
                         (yp.max() + yp.min()) / 2.)
    elif proj_point is not None:  # convert units, initial guess for crpix
        proj_point.transform_to(world_coords)
        wcs.wcs.crval = (proj_point.data.lon.deg, proj_point.data.lat.deg)
        wcs.wcs.crpix = (close(lon - wcs.wcs.crval[0],
                               xp), close(lon - wcs.wcs.crval[1], yp))

    # fit linear terms, assign to wcs
    # use (1, 0, 0, 1) as initial guess, in case input wcs was passed in
    # and cd terms are way off.
    p0 = np.concatenate([wcs.wcs.cd.flatten(), wcs.wcs.crpix.flatten()])

    xpmin, xpmax, ypmin, ypmax = xp.min(), xp.max(), yp.min(), yp.max()
    if xpmin == xpmax: xpmin, xpmax = xpmin - 0.5, xpmax + 0.5
    if ypmin == ypmax: ypmin, ypmax = ypmin - 0.5, ypmax + 0.5

    fit = least_squares(
        _linear_wcs_fit,
        p0,
        args=(lon, lat, xp, yp, wcs),
        bounds=[[-np.inf, -np.inf, -np.inf, -np.inf, xpmin, ypmin],
                [np.inf, np.inf, np.inf, np.inf, xpmax, ypmax]])
    wcs.wcs.crpix = np.array(fit.x[4:6])
    wcs.wcs.cd = np.array(fit.x[0:4].reshape((2, 2)))

    # fit SIP, if specified. Only fit forward coefficients
    if sip_degree:
        degree = sip_degree
        if '-SIP' not in wcs.wcs.ctype[0]:
            wcs.wcs.ctype = [x + '-SIP' for x in wcs.wcs.ctype]

        coef_names = [
            '{0}_{1}'.format(i, j) for i in range(degree + 1)
            for j in range(degree + 1)
            if (i + j) < (degree + 1) and (i + j) > 1
        ]
        p0 = np.concatenate((np.array(wcs.wcs.crpix), wcs.wcs.cd.flatten(),
                             np.zeros(2 * len(coef_names))))

        fit = least_squares(_sip_fit,
                            p0,
                            args=(lon, lat, xp, yp, wcs, degree, coef_names))
        coef_fit = (list(fit.x[6:6 + len(coef_names)]),
                    list(fit.x[6 + len(coef_names):]))

        # put fit values in wcs
        wcs.wcs.cd = fit.x[2:6].reshape((2, 2))
        wcs.wcs.crpix = fit.x[0:2]

        a_vals = np.zeros((degree + 1, degree + 1))
        b_vals = np.zeros((degree + 1, degree + 1))

        for coef_name in coef_names:
            a_vals[int(coef_name[0])][int(coef_name[2])] = coef_fit[0].pop(0)
            b_vals[int(coef_name[0])][int(coef_name[2])] = coef_fit[1].pop(0)

        wcs.sip = Sip(a_vals, b_vals, np.zeros((degree + 1, degree + 1)),
                      np.zeros((degree + 1, degree + 1)), wcs.wcs.crpix)

    return wcs
Exemplo n.º 6
0
    def __init__(self, data, position, size, wcs=None, mode='trim',
                 fill_value=np.nan, copy=False):
        if wcs is None:
            wcs = getattr(data, 'wcs', None)

        if isinstance(position, SkyCoord):
            if wcs is None:
                raise ValueError('wcs must be input if position is a '
                                 'SkyCoord')
            position = skycoord_to_pixel(position, wcs, mode='all')  # (x, y)

        if np.isscalar(size):
            size = np.repeat(size, 2)

        # special handling for a scalar Quantity
        if isinstance(size, u.Quantity):
            size = np.atleast_1d(size)
            if len(size) == 1:
                size = np.repeat(size, 2)

        if len(size) > 2:
            raise ValueError('size must have at most two elements')

        shape = np.zeros(2).astype(int)
        pixel_scales = None
        # ``size`` can have a mixture of int and Quantity (and even units),
        # so evaluate each axis separately
        for axis, side in enumerate(size):
            if not isinstance(side, u.Quantity):
                shape[axis] = int(np.round(size[axis]))     # pixels
            else:
                if side.unit == u.pixel:
                    shape[axis] = int(np.round(side.value))
                elif side.unit.physical_type == 'angle':
                    if wcs is None:
                        raise ValueError('wcs must be input if any element '
                                         'of size has angular units')
                    if pixel_scales is None:
                        pixel_scales = u.Quantity(
                            proj_plane_pixel_scales(wcs), wcs.wcs.cunit[axis])
                    shape[axis] = int(np.round(
                        (side / pixel_scales[axis]).decompose()))
                else:
                    raise ValueError('shape can contain Quantities with only '
                                     'pixel or angular units')

        data = np.asanyarray(data)
        # reverse position because extract_array and overlap_slices
        # use (y, x), but keep the input position
        pos_yx = position[::-1]

        cutout_data, input_position_cutout = extract_array(
            data, tuple(shape), pos_yx, mode=mode, fill_value=fill_value,
            return_position=True)
        if copy:
            cutout_data = np.copy(cutout_data)
        self.data = cutout_data

        self.input_position_cutout = input_position_cutout[::-1]    # (x, y)
        slices_original, slices_cutout = overlap_slices(
            data.shape, shape, pos_yx, mode=mode)

        self.slices_original = slices_original
        self.slices_cutout = slices_cutout

        self.shape = self.data.shape
        self.input_position_original = position
        self.shape_input = shape

        ((self.ymin_original, self.ymax_original),
         (self.xmin_original, self.xmax_original)) = self.bbox_original

        ((self.ymin_cutout, self.ymax_cutout),
         (self.xmin_cutout, self.xmax_cutout)) = self.bbox_cutout

        # the true origin pixel of the cutout array, including any
        # filled cutout values
        self._origin_original_true = (
            self.origin_original[0] - self.slices_cutout[1].start,
            self.origin_original[1] - self.slices_cutout[0].start)

        if wcs is not None:
            self.wcs = deepcopy(wcs)
            self.wcs.wcs.crpix -= self._origin_original_true
            self.wcs.array_shape = self.data.shape
            if wcs.sip is not None:
                self.wcs.sip = Sip(wcs.sip.a, wcs.sip.b,
                                   wcs.sip.ap, wcs.sip.bp,
                                   wcs.sip.crpix - self._origin_original_true)
        else:
            self.wcs = None
Exemplo n.º 7
0
def solve_field(engine,
                xy,
                flux=None,
                width=None,
                height=None,
                ra_hours=0,
                dec_degs=0,
                radius=180,
                min_scale=0.1,
                max_scale=10,
                parity=None,
                sip_order=3,
                crpix_center=True,
                max_sources=None,
                retry_lost=True,
                callback=None):
    """
    Obtain astrometric solution given XY coordinates of field stars

    :param :class:`Solver` engine: Astrometry.net engine solver instance
    :param array_like xy: (n x 2) array of 0-based X and Y pixel coordinates
        of stars
    :param array_like flux: optional n-element array of star fluxes
    :param int width: image width in pixels; defaults to the maximum minus
        minimum X coordinate of stars
    :param int height: image height in pixels; defaults to the maximum minus
        minimum Y coordinate of stars
    :param float ra_hours: optional RA of image center in hours; default: 0
    :param float dec_degs: optional Dec of image center in degrees; default: 0
    :param float radius: optional field search radius in degrees; default: 180
        (search over the whole sky)
    :param float min_scale: optional minimum pixel scale in arcseconds per
        pixel; default: 0.1
    :param float max_scale: optional maximum pixel scale in arcseconds per
        pixel; default: 10
    :param bool | None parity: image parity (sign of coordinate transformation
        matrix determinant): True = normal parity, False = flipped image, None
        (default) = try both
    :param int sip_order: order of SIP distortion terms; default: 3;
        0 - disable calculation of distortion
    :param bool crpix_center: set reference pixel to image center
    :param int max_sources: use only the given number of brightest sources;
        0/""/None (default) = no limit
    :param bool retry_lost: if solution failed, retry in the "lost in space"
        mode, i.e. without coordinate restrictions (`radius` = 180) and with
        opposite parity, unless the initial search already had these
        restrictions disabled
    :param callable callback: optional callable that is regularly called
        by the solver, accepts no arguments, and returns 0 to interrupt
        the solution and 1 otherwise

    :return: astrometric solution object; its `wcs` attribute is set to None if
        solution was not found
    :rtype: :class:`Solution`
    """
    solver = engine.solver
    ra = float(ra_hours) * 15
    dec = float(dec_degs)
    r = float(radius)

    # Set timer callback if requested
    if callback is not None:
        an_engine.set_timer_callback(
            solver,
            ctypes.cast(
                ctypes.CFUNCTYPE(ctypes.c_int)(callback),
                ctypes.c_voidp).value)
    else:
        an_engine.set_timer_callback(solver, 0)

    # Set field star position array
    n = len(xy)
    xy = numpy.asanyarray(xy)
    field = an_engine.starxy_new(n, flux is not None, False)
    if flux is not None:
        flux = numpy.asanyarray(flux)
        if len(flux) != n:
            raise ValueError(
                'Flux array must be of the same length as XY array')
        if max_sources:
            order = numpy.argsort(flux)[::-1]
            xy, flux = xy[order], flux[order]
            del order
        an_engine.starxy_set_flux_array(field, flux)
    an_engine.starxy_set_xy_array(field, xy.ravel())
    an_engine.solver_set_field(solver, field)

    try:
        # Initialize solver parameters
        if width:
            minx, maxx = 0, int(width) - 1
        else:
            minx, maxx = xy[:, 0].min(), xy[:, 0].max()
        if height:
            miny, maxy = 0, int(height) - 1
        else:
            miny, maxy = xy[:, 1].min(), xy[:, 1].max()
        an_engine.solver_set_field_bounds(solver, minx, maxx, miny, maxy)
        solver.quadsize_min = 0.1 * min(maxx - minx + 1, maxy - miny + 1)

        if crpix_center != '':
            solver.set_crpix = solver.set_crpix_center = int(crpix_center)

        an_engine.solver_set_radec(solver, ra, dec, r)

        solver.funits_lower = float(min_scale)
        solver.funits_upper = float(max_scale)

        solver.logratio_tokeep = numpy.log(1e12)
        solver.distance_from_quad_bonus = True

        if parity is None or parity == '':
            solver.parity = an_engine.PARITY_BOTH
        elif int(parity):
            solver.parity = an_engine.PARITY_NORMAL
        else:
            solver.parity = an_engine.PARITY_FLIP

        enable_sip = sip_order and int(sip_order) >= 2
        if enable_sip:
            solver.do_tweak = True
            solver.tweak_aborder = int(sip_order)
            solver.tweak_abporder = int(sip_order) + 1
        else:
            solver.do_tweak = False

        if max_sources:
            solver.endobj = max_sources
        else:
            solver.endobj = 0

        # Find indexes needed to solve the field
        fmin = solver.quadsize_min * min_scale
        fmax = numpy.hypot(width, height) * max_scale
        indices = []
        for index in engine.indexes:
            if fmin > index.index_scale_upper or \
                    fmax < index.index_scale_lower:
                continue
            if not an_engine.index_is_within_range(index, ra, dec, r):
                continue

            indices.append(index)

        if not len(indices):
            raise ValueError(
                'No indexes found for the given scale and position')

        # Sort indices by scale (larger scales/smaller indices first - should
        # be faster) then by distance from expected position
        indices.sort(key=lambda _idx: (
            -_idx.index_scale_upper,
            an_engine.healpix_distance_to_radec(_idx.healpix, _idx.hpnside, ra,
                                                dec)[0]
            if _idx.healpix >= 0 else 0,
        ))
        an_engine.solver_clear_indexes(solver)
        for index in indices:
            an_engine.solver_add_index(solver, index)

        # Run the solver
        an_engine.solver_run(solver)
        sol = Solution()

        if solver.have_best_match:
            best_match = solver.best_match
            sol.log_odds = solver.best_logodds
            sol.n_match = best_match.nmatch
            sol.n_conflict = best_match.nconflict
            sol.n_field = best_match.nfield
            if best_match.index is not None:
                sol.index_name = best_match.index.indexname
        else:
            best_match = None

        if solver.best_match_solves:
            # Get WCS parameters of best solution
            sol.wcs = WCS(naxis=2)

            wcs_ctype = ('RA---TAN', 'DEC--TAN')
            if enable_sip:
                sip = best_match.sip
                wcstan = sip.wcstan

                a_order, b_order = sip.a_order, sip.b_order
                if a_order > 0 or b_order > 0:
                    ap_order, bp_order = sip.ap_order, sip.bp_order
                    maxorder = an_engine.SIP_MAXORDER
                    a = array_from_swig(sip.a,
                                        (maxorder, maxorder))[:a_order +
                                                              1, :a_order + 1]
                    b = array_from_swig(sip.b,
                                        (maxorder, maxorder))[:b_order +
                                                              1, :b_order + 1]
                    if a.any() or b.any():
                        ap = array_from_swig(
                            sip.ap,
                            (maxorder, maxorder))[:ap_order + 1, :ap_order + 1]
                        bp = array_from_swig(
                            sip.bp,
                            (maxorder, maxorder))[:bp_order + 1, :bp_order + 1]
                        sol.wcs.sip = Sip(a, b, ap, bp, sol.wcs.wcs.crpix)
                        wcs_ctype = ('RA---TAN-SIP', 'DEC--TAN-SIP')
            else:
                wcstan = best_match.wcstan
            sol.wcs.wcs.ctype = wcs_ctype
            sol.wcs.wcs.crpix = array_from_swig(wcstan.crpix, (2, ))
            sol.wcs.wcs.crval = array_from_swig(wcstan.crval, (2, ))
            sol.wcs.wcs.cd = array_from_swig(wcstan.cd, (2, 2))
        elif retry_lost and (radius < 180 or parity is not None):
            # When no solution was found, retry with all constraints relaxed
            an_engine.solver_cleanup_field(solver)
            return solve_field(engine,
                               xy,
                               flux,
                               width,
                               height,
                               0,
                               0,
                               180,
                               min_scale,
                               max_scale,
                               None,
                               sip_order,
                               crpix_center,
                               max_sources,
                               retry_lost=False)

        return sol
    finally:
        # Cleanup and make solver ready for the next solution
        an_engine.solver_cleanup_field(solver)
        an_engine.solver_clear_indexes(solver)
Exemplo n.º 8
0
    def format_wcs(self, cutout_shape):
        """
        Re-calculate the CRPIX values for the WCS.  The SIP values are also
        re-calculated, if present.

        CRPIX values are tricky and there exists some black magic math in here,
        not to mention some values are set differently to accommodate the subtle
        variations with Python 2/3 float values.

        Parameters
        ----------
        cutout_shape:   The tuple containing the bounding values (shape) of the
        resulting cutout.

        Returns
        -------
        The formatted WCS object.
        """
        output_wcs = deepcopy(self.wcs)
        wcs_crpix = output_wcs.wcs.crpix
        l_wcs_crpix = len(wcs_crpix)

        logger.debug('Adjusting WCS.')

        for idx, cutout_region in enumerate(cutout_shape):
            if idx < l_wcs_crpix:
                curr_val = wcs_crpix[idx]
                start = cutout_region.start
                step = cutout_region.step

                if start:
                    wcs_crpix[idx] -= float(ceil(start + 0.5))

                if step is not None:
                    logger.debug('Taking step {} into account.'.format(step))
                    wcs_crpix[idx] /= step

                if start:
                    wcs_crpix[idx] += 1.0

                logger.debug(
                    'Adjusted wcs_crpix val from {} to {}'.format(
                        curr_val, wcs_crpix[idx]))

        if output_wcs.wcs.has_pc():
            pc = output_wcs.wcs.pc
            for i in range(output_wcs.wcs.naxis):
                for j in range(output_wcs.wcs.naxis):
                    step = cutout_shape[j].step
                    if step:
                        pc[i][j] *= step
        elif output_wcs.wcs.has_cd():
            cd = output_wcs.wcs.cd
            for i in range(output_wcs.wcs.naxis):
                for j in range(output_wcs.wcs.naxis):
                    step = cutout_shape[j].step
                    if step:
                        cd[i][j] *= step

        if self.wcs.sip:
            curr_sip = self.wcs.sip
            output_wcs.sip = Sip(curr_sip.a, curr_sip.b,
                                 curr_sip.ap, curr_sip.bp,
                                 wcs_crpix[0:2])

        logger.debug('WCS adjusted.')

        return output_wcs