def reference_to_skyoffset_matrix(skyoffset_frame): """Make rotation matrix from reference frame and a skyoffset frame. this function is useful after using astropy to make a rotation matrix *modified from reference_to_skyoffset function in astropy Parameters ---------- skyoffset_frame : SkyCoord frame the rotated frame note: SkyCoords are compatible Returns ------- R : numpy array a 3d rotation matrix """ origin = skyoffset_frame.origin.spherical mat1 = rotation_matrix(-skyoffset_frame.rotation, "x") mat2 = rotation_matrix(-origin.lat, "y") mat3 = rotation_matrix(origin.lon, "z") R = matrix_product(mat1, mat2, mat3) return R
def reference_to_greatcircle(reference_frame, greatcircle_frame): """Convert a reference coordinate to a great circle frame.""" # Define rotation matrices along the position angle vector, and # relative to the origin. pole = greatcircle_frame.pole.transform_to(coord.ICRS) ra0 = greatcircle_frame.ra0 R_rot = rotation_matrix(greatcircle_frame.rotation, 'z') if np.isnan(ra0): R2 = rotation_matrix(pole.dec, 'y') R1 = rotation_matrix(pole.ra, 'z') R = matrix_product(R2, R1) else: xaxis = np.array([np.cos(ra0), np.sin(ra0), 0.]) zaxis = pole.cartesian.xyz.value xaxis[2] = -(zaxis[0] * xaxis[0] + zaxis[1] * xaxis[1]) / zaxis[2] # what? xaxis = xaxis / np.sqrt(np.sum(xaxis**2)) yaxis = np.cross(zaxis, xaxis) R = np.stack((xaxis, yaxis, zaxis)) return matrix_product(R_rot, R)
def camera_to_altaz(camera, altaz): if camera.pointing_direction is None: raise ValueError('Pointing Direction must not be None') x = camera.x.copy() y = camera.y.copy() if camera.rotated is True: x, y = y, -x z = 1 / np.sqrt(1 + (x / focal_length)**2 + (y / focal_length)**2) x *= z / focal_length y *= z / focal_length cartesian = CartesianRepresentation(x, y, z, copy=False) rot_z_az = rotation_matrix(-camera.pointing_direction.az, 'z') rot_y_zd = rotation_matrix(-camera.pointing_direction.zen, 'y') cartesian = cartesian.transform(rot_y_zd) cartesian = cartesian.transform(rot_z_az) altitude = 90 * u.deg - np.arccos(cartesian.z) azimuth = np.arctan2(cartesian.y, cartesian.x) return AltAz( alt=altitude, az=azimuth, location=camera.location, obstime=camera.obstime, )
def fk4_to_gal(fk4coords, galframe): mat1 = rotation_matrix(180 - Galactic._lon0_B1950.degree, 'z') mat2 = rotation_matrix(90 - Galactic._ngp_B1950.dec.degree, 'y') mat3 = rotation_matrix(Galactic._ngp_B1950.ra.degree, 'z') matprec = fk4coords._precession_matrix(fk4coords.equinox, EQUINOX_B1950) return matrix_product(mat1, mat2, mat3, matprec)
def altaz_to_camera(altaz, camera): if camera.pointing_direction is None: raise ValueError('Pointing Direction must not be None') cartesian = altaz.cartesian rot_z_az = rotation_matrix(camera.pointing_direction.az, 'z') rot_y_zd = rotation_matrix(camera.pointing_direction.zen, 'y') cartesian = cartesian.transform(rot_z_az) cartesian = cartesian.transform(rot_y_zd) x = (cartesian.x * focal_length / cartesian.z).copy() y = (cartesian.y * focal_length / cartesian.z).copy() if camera.rotated is True: x, y = -y, x return CameraFrame( x=x, y=y, pointing_direction=camera.pointing_direction, obstime=altaz.obstime, location=camera.location, )
def get_ksi_eta(self, time, star): """ Calculates relative position to star in the orthographic projection. Parameters: time (str, Time): Reference time to calculate the position. It can be a string in the format "yyyy-mm-dd hh:mm:ss.s" or an astropy Time object star (str, SkyCoord): The coordinate of the star in the same reference frame as the ephemeris. It can be a string in the format "hh mm ss.s +dd mm ss.ss" or an astropy SkyCoord object. Returns: ksi, eta (float): on-sky orthographic projection of the observer relative to a star Ksi is in the North-South direction (North positive) Eta is in the East-West direction (East positive) """ time = test_attr(time, Time, 'time') try: star = SkyCoord(star, unit=(u.hourangle, u.deg)) except: raise ValueError( 'star is not an astropy object or a string in the format "hh mm ss.s +dd mm ss.ss"' ) itrs = self.site.get_itrs(obstime=time) gcrs = itrs.transform_to(GCRS(obstime=time)) rz = rotation_matrix(star.ra, 'z') ry = rotation_matrix(-star.dec, 'y') cp = gcrs.cartesian.transform(rz).transform(ry) return cp.y.to(u.km).value, cp.z.to(u.km).value
def reference_plane(self, time): """Compute the orbit at specified times in the two-body barycentric frame aligned with the reference plane coordinate system (XYZ). Parameters ---------- time : array_like, `astropy.time.Time` Array of times as barycentric MJD values, or an Astropy `~astropy.time.Time` object containing the times to evaluate at. """ xyz = self.orbital_plane(time) vxyz = xyz.differentials['s'] xyz = xyz.without_differentials() # Construct rotation matrix to take the orbit from the orbital plane # system (xyz) to the reference plane system (XYZ): R1 = rotation_matrix(-self.omega, axis='z') R2 = rotation_matrix(self.i, axis='x') R3 = rotation_matrix(self.Omega, axis='z') Rot = matrix_product(R3, R2, R1) # Rotate to the reference plane system XYZ = coord.CartesianRepresentation(matrix_product(Rot, xyz.xyz)) VXYZ = coord.CartesianDifferential(matrix_product(Rot, vxyz.d_xyz)) XYZ = XYZ.with_differentials(VXYZ) kw = dict() if self.barycenter is not None: kw['origin'] = self.barycenter.origin return ReferencePlaneFrame(XYZ, **kw)
def add_detector_on_earth(name, longitude, latitude, yangle=0, xangle=None, height=0): """ Add a new detector on the earth Parameters ---------- name: str two-letter name to identify the detector longitude: float Longitude in radians using geodetic coordinates of the detector latitude: float Latitude in radians using geodetic coordinates of the detector yangle: float Azimuthal angle of the y-arm (angle drawn from pointing north) xangle: float Azimuthal angle of the x-arm (angle drawn from point north). If not set we assume a right angle detector following the right-hand rule. height: float The height in meters of the detector above the standard reference ellipsoidal earth """ if xangle is None: # assume right angle detector if no separate xarm direction given xangle = yangle + np.pi / 2.0 # Calculate response in earth centered coordinates # by rotation of response in coordinates aligned # with the detector arms a, b = cos(2 * xangle), sin(2 * xangle) xresp = np.array([[-a, b, 0], [b, a, 0], [0, 0, 0]]) a, b = cos(2 * yangle), sin(2 * yangle) yresp = np.array([[-a, b, 0], [b, a, 0], [0, 0, 0]]) resp = (yresp - xresp) / 4.0 rm1 = rotation_matrix(longitude * units.rad, 'z') rm2 = rotation_matrix((np.pi / 2.0 - latitude) * units.rad, 'y') rm = np.matmul(rm2, rm1) resp = np.matmul(resp, rm) resp = np.matmul(rm.T, resp) loc = coordinates.EarthLocation.from_geodetic(longitude * units.rad, latitude * units.rad, height=height * units.meter) loc = np.array([loc.x.value, loc.y.value, loc.z.value]) _custom_ground_detectors[name] = { 'location': loc, 'response': resp, 'yangle': yangle, 'xangle': xangle, 'height': height, 'xaltitude': 0.0, 'yaltitude': 0.0, }
def fk5_to_gal(fk5coord, galframe): # need precess to J2000 first pmat = fk5coord._precession_matrix(fk5coord.equinox, EQUINOX_J2000) mat1 = rotation_matrix(180 - Galactic._lon0_J2000.degree, 'z') mat2 = rotation_matrix(90 - Galactic._ngp_J2000.dec.degree, 'y') mat3 = rotation_matrix(Galactic._ngp_J2000.ra.degree, 'z') return matrix_product(mat1, mat2, mat3, pmat)
def reference_to_skyoffset_matrix( lon: T.Union[float, u.Quantity], lat: T.Union[float, u.Quantity], rotation: T.Union[float, u.Quantity], ) -> T.Sequence: """Convert a reference coordinate to an sky offset frame [astropy]. Cartesian to Cartesian matrix transform. Parameters ---------- lon : float or |AngleType| or |Quantity| instance For ICRS, the right ascension. If float, assumed degrees. lat : |AngleType| or |Quantity| instance For ICRS, the declination. If float, assumed degrees. rotation : |AngleType| or |Quantity| instance The final rotation of the frame about the ``origin``. The sign of the rotation is the left-hand rule. That is, an object at a particular position angle in the un-rotated system will be sent to the positive latitude (z) direction in the final frame. If float, assumed degrees. Returns ------- ndarray (3x3) matrix. rotates reference Cartesian to skyoffset Cartesian. See Also -------- :func:`~astropy.coordinates.builtin.skyoffset.reference_to_skyoffset` References ---------- .. [astropy] Astropy Collaboration, Robitaille, T., Tollerud, E., Greenfield, P., Droettboom, M., Bray, E., Aldcroft, T., Davis, M., Ginsburg, A., Price-Whelan, A., Kerzendorf, W., Conley, A., Crighton, N., Barbary, K., Muna, D., Ferguson, H., Grollier, F., Parikh, M., Nair, P., Unther, H., Deil, C., Woillez, J., Conseil, S., Kramer, R., Turner, J., Singer, L., Fox, R., Weaver, B., Zabalza, V., Edwards, Z., Azalee Bostroem, K., Burke, D., Casey, A., Crawford, S., Dencheva, N., Ely, J., Jenness, T., Labrie, K., Lim, P., Pierfederici, F., Pontzen, A., Ptak, A., Refsdal, B., Servillat, M., & Streicher, O. (2013). Astropy: A community Python package for astronomy. Astronomy and Astrophysics, 558, A33. """ # Define rotation matrices along the position angle vector, and # relative to the origin. mat1 = rotation_matrix(-rotation, "x") mat2 = rotation_matrix(-lat, "y") mat3 = rotation_matrix(lon, "z") M = matrix_product(mat1, mat2, mat3) return M
def reference_to_skyoffset(reference_frame, telescope_frame): '''Convert a reference coordinate to an sky offset frame.''' # Define rotation matrices along the position angle vector, and # relative to the telescope_pointing. telescope_pointing = telescope_frame.telescope_pointing.spherical mat1 = rotation_matrix(-telescope_pointing.lat, 'y') mat2 = rotation_matrix(telescope_pointing.lon, 'z') return matrix_product(mat1, mat2)
def reference_to_skyoffset(reference_frame, telescope_frame): """Convert a reference coordinate to an sky offset frame.""" # Define rotation matrices along the position angle vector, and # relative to the origin. origin = telescope_frame.origin.spherical mat1 = rotation_matrix(-origin.lat, 'y') mat2 = rotation_matrix(origin.lon, 'z') return matrix_product(mat1, mat2)
def reference_to_skyoffset(reference_frame, telescope_frame): """Convert a reference coordinate to an sky offset frame.""" # Define rotation matrices along the position angle vector, and # relative to the origin. origin = telescope_frame.origin.spherical mat1 = rotation_matrix(-origin.lat, "y") mat2 = rotation_matrix(origin.lon, "z") return matrix_product(mat1, mat2)
def reference_to_skyoffset(reference_frame, skyoffset_frame): """Convert a reference coordinate to an sky offset frame.""" # Define rotation matrices along the position angle vector, and # relative to the origin. origin = skyoffset_frame.origin.spherical mat1 = rotation_matrix(-skyoffset_frame.rotation, 'x') mat2 = rotation_matrix(-origin.lat, 'y') mat3 = rotation_matrix(origin.lon, 'z') return matrix_product(mat1, mat2, mat3)
def cb_crs_to_cb_j2000_eq(cb_crs_coord, _): """Conversion from Celestial Reference System of a Central Body to its Local Equatorial at J2000 Epoch Reference System.""" ra, dec, w = celestial_body_rot_params(None, cb_crs_coord.body_name) body_crs_to_body_eq = matrix_product( rotation_matrix(90 * u.deg - dec, "x"), rotation_matrix(90 * u.deg + ra, "z")) return body_crs_to_body_eq
def body_crs_to_body_eq_matrix(obstime, body_name): """Computes the rotation matrix from Celestial Body CRS to Local Equatorial True-of-Date Reference System.""" ra, dec, w = celestial_body_rot_params(obstime, body_name) body_crs_to_body_eq = matrix_product( rotation_matrix(90 * u.deg - dec, "x"), rotation_matrix(90 * u.deg + ra, "z"), ) return body_crs_to_body_eq
def body_crs_to_body_fixed_matrix(obstime, body_name): """Computes the rotation matrix from Celestial Body CRS to Body Fixed Reference System.""" ra, dec, w = celestial_body_rot_params(obstime, body_name) body_crs_to_body_fixed = matrix_product( rotation_matrix(w, "z"), rotation_matrix(90 * u.deg - dec, "x"), rotation_matrix(90 * u.deg + ra, "z"), ) return body_crs_to_body_fixed
def test_angle_axis(): m1 = rotation_matrix(35*u.deg, 'x') an1, ax1 = angle_axis(m1) assert an1 - 35*u.deg < 1e-10*u.deg assert_allclose(ax1, [1, 0, 0]) m2 = rotation_matrix(-89*u.deg, [1, 1, 0]) an2, ax2 = angle_axis(m2) assert an2 - 89*u.deg < 1e-10*u.deg assert_allclose(ax2, [-2**-0.5, -2**-0.5, 0])
def test_angle_axis(): m1 = rotation_matrix(35 * u.deg, 'x') an1, ax1 = angle_axis(m1) assert an1 - 35 * u.deg < 1e-10 * u.deg assert_allclose(ax1, [1, 0, 0]) m2 = rotation_matrix(-89 * u.deg, [1, 1, 0]) an2, ax2 = angle_axis(m2) assert an2 - 89 * u.deg < 1e-10 * u.deg assert_allclose(ax2, [-2**-0.5, -2**-0.5, 0])
def topo_frame_def(latitude, longitude, moon=True): """ Make a list of strings defining a topocentric frame. This can then be loaded with spiceypy.lmpool. """ if moon: idnum = 1301000 station_name = 'LUNAR-TOPO' relative = 'MOON_ME' else: idnum = 1399000 station_name = 'EARTH-TOPO' relative = 'ITRF93' # The DSS stations are built into SPICE, and they number up to 66. # We will call this station number 98. station_num = 98 idnum += station_num fm_center_id = idnum - 1000000 ecef_to_enu = np.matmul( rotation_matrix(-longitude, 'z', unit='deg'), rotation_matrix(latitude, 'y', unit='deg') ).T # Reorder the axes so that X,Y,Z = E,N,U ecef_to_enu = ecef_to_enu[[2, 1, 0]] mat = " ".join(map("{:.7f}".format, ecef_to_enu.flatten())) fmt_strs = [ "FRAME_{1} = {0}", "FRAME_{0}_NAME = '{1}'", "FRAME_{0}_CLASS = 4", "FRAME_{0}_CLASS_ID = {0}", "FRAME_{0}_CENTER = {2}", "OBJECT_{2}_FRAME = '{1}'", "TKFRAME_{0}_RELATIVE = '{3}'", "TKFRAME_{0}_SPEC = 'MATRIX'", "TKFRAME_{0}_MATRIX = {4}" ] frame_specs = [s.format(idnum, station_name, fm_center_id, relative, mat) for s in fmt_strs] frame_dict = {} for spec in frame_specs: k, v = map(str.strip, spec.split('=')) frame_dict[k] = v latlon = ["{:.4f}".format(l) for l in [latitude, longitude]] return station_name, idnum, frame_dict, latlon
def icrs_to_arbpole(icrs_coord, arbpole_frame): roll = arbpole_frame.roll pole = arbpole_frame.pole # Align z(new) with direction to M31 mat1 = rotation_matrix(-pole.dec, 'y') mat2 = rotation_matrix(pole.ra, 'z') mat3 = rotation_matrix(roll, 'z') mat4 = rotation_matrix(90 * u.degree, 'y') R = matrix_product(mat4, mat1, mat2, mat3) return R
def _get_matrix_vectors(gal_frame, inverse=False): """ Utility function: use the ``inverse`` argument to get the inverse transformation, matrix and offsets to go from external galaxy frame to ICRS. """ # this is based on the astropy Galactocentric class code # shorthand gcf = gal_frame # rotation matrix to align x(ICRS) with the vector to the galaxy center mat0 = np.array([[0., 1., 0.], [0., 0., 1.], [1., 0., 0.]]) # relabel axes so x points in RA, y in dec, z away from us mat1 = rotation_matrix(-gcf.gal_coord.dec, 'y') mat2 = rotation_matrix(gcf.gal_coord.ra, 'z') # construct transformation matrix and use it R = matrix_product(mat0, mat1, mat2) # Now define translation by distance to galaxy center - best defined in current sky coord system defined by R translation = r.CartesianRepresentation(gcf.gal_distance * [0., 0., 1.]) # Now rotate galaxy to right orientation pa_rot = rotation_matrix(-gcf.PA, 'z') inc_rot = rotation_matrix(gcf.inclination, 'y') H = matrix_product(inc_rot, pa_rot) # compute total matrices A = matrix_product(H, R) # Now we transform the translation vector between sky-aligned and galaxy-aligned frames offset = -translation.transform(H) if inverse: # the inverse of a rotation matrix is a transpose, which is much faster # and more stable to compute A = matrix_transpose(A) offset = (-offset).transform(A) # galvel is given in sky-aligned coords, need to transform to ICRS, so use R transpose R = matrix_transpose(R) offset_v = r.CartesianDifferential.from_cartesian( (gcf.galvel_heliocentric).to_cartesian().transform(R)) offset = offset.with_differentials(offset_v) else: # galvel is given in sky-aligned coords, need to transform to gal frame, so use H offset_v = r.CartesianDifferential.from_cartesian( (-gcf.galvel_heliocentric).to_cartesian().transform(H)) offset = offset.with_differentials(offset_v) return A, offset
def get_rot(fr, x0=0.): origin = get_origin(fr) usph_band = coord.UnitSphericalRepresentation( np.random.uniform(0, 360, 10000) * u.deg, np.random.uniform(-1, 1, size=10000) * u.deg) band_icrs = fr.realize_frame(usph_band).transform_to(coord.ICRS) R1 = rotation_matrix(origin.ra, 'z') R2 = rotation_matrix(-origin.dec, 'y') Rtmp = R2 @ R1 roll = get_roll(band_icrs, Rtmp, x0) return [origin.ra.degree, origin.dec.degree, roll.degree]
def _rotation_matrix_hcc_to_hgs(longitude, latitude): # Returns the rotation matrix from HCC to HGS based on the observer longitude and latitude # Permute the axes of HCC to match HGS Cartesian equivalent # HGS_X = HCC_Z # HGS_Y = HCC_X # HGS_Z = HCC_Y axes_matrix = np.array([[0, 0, 1], [1, 0, 0], [0, 1, 0]]) # Rotate in latitude and longitude (sign difference because of direction difference) lat_matrix = rotation_matrix(latitude, 'y') lon_matrix = rotation_matrix(-longitude, 'z') return lon_matrix @ lat_matrix @ axes_matrix
def rotate(self, axis_angle=None, rotmat=None, L_coords=None): """ Rotate the source. The arguments correspond to different rotation types. If supplied together in one function call, they are applied in order: axis_angle, then rotmat, then L_coords. Parameters ---------- axis_angle : 2-tuple First element one of 'x', 'y', 'z' for the axis to rotate about, second element an astropy.units.Quantity with dimensions of angle, indicating the angle to rotate through. rotmat : (3, 3) array-like Rotation matrix. L_coords : 2-tuple First element containing an inclination and second element an azimuthal angle (both astropy.units.Quantity instances with dimensions of angle). The routine will first attempt to identify a preferred plane based on the angular momenta of the central 1/3 of particles in the source. This plane will then be rotated to lie in the 'y-z' plane, followed by a rotation by the azimuthal angle about its angular momentum pole (rotation about 'x'), and finally inclined (rotation about 'y'). """ do_rot = np.eye(3) if axis_angle is not None: do_rot = rotation_matrix(axis_angle[1], axis=axis_angle[0]).dot(do_rot) if rotmat is not None: do_rot = rotmat.dot(do_rot) if L_coords is not None: incl, az_rot = L_coords do_rot = L_align(self.coordinates_g.get_xyz(), self.coordinates_g.differentials['s'].get_d_xyz(), self.mHI_g, frac=.3, Laxis='x').dot(do_rot) do_rot = rotation_matrix(az_rot, axis='x').dot(do_rot) do_rot = rotation_matrix(incl, axis='y').dot(do_rot) self.current_rotation = do_rot.dot(self.current_rotation) self.coordinates_g = self.coordinates_g.transform(do_rot) return
def _icrs_to_fk5_matrix(): """ B-matrix from USNO circular 179. Used by the ICRS->FK5 transformation functions. """ eta0 = -19.9 / 3600000. xi0 = 9.1 / 3600000. da0 = -22.9 / 3600000. m1 = rotation_matrix(-eta0, 'x') m2 = rotation_matrix(xi0, 'y') m3 = rotation_matrix(da0, 'z') return matrix_product(m1, m2, m3)
def tip_axis(coord: UnitSphericalRepresentation, axis_lon: Angle, rot_angle: Angle) -> UnitSphericalRepresentation: """Perform a rotation about an axis perpendicular to the Z-axis. The purpose of this rotation is to move the pole of the coordinate system from one place to another. For example, transforming from a coordinate system where the pole is aligned with the physical pole of a mount to a topocentric coordinate system where the pole is aligned with zenith. Note that this is a true rotation, and the same rotation is applied to all coordinates in the originating coordinate system equally. It is not equivalent to using SkyCoord directional_offset_by() with a fixed position angle and separation since the direction and magnitude of the offset depend on the value of coord. Args: coord: Coordinate to be transformed. axis_lon: Longitude angle of the axis of rotation. rot_angle: Angle of rotation. Returns: Coordinate after transformation. """ rot = rotation_matrix(rot_angle, axis=SkyCoord(axis_lon, 0 * u.deg).represent_as('cartesian').xyz) coord_cart = coord.represent_as(CartesianRepresentation) coord_rot_cart = coord_cart.transform(rot) return coord_rot_cart.represent_as(UnitSphericalRepresentation)
def wcs(target, xobj, yobj, pattern, a = 0, p = 0.25): from astropy.wcs import WCS from astropy.coordinates.matrix_utilities import rotation_matrix from astropy import units as u from astropy.coordinates import SkyCoord header = get_header(pattern) for filename in pattern: angle = a * u.deg plate = p * header['CCDXBIN']*u.arcsec rot_matrix = plate.to(u.deg) * rotation_matrix(angle)[:-1,:-1] #questa matrice orienta l'immagine in modo diverso! #mi pare che, per essere "in linea" con il catalogo, debba ruotare attorno all'asse x w = WCS(header) if 'CTYPE' in header and 'CRVAL' in header and 'CRPIX' in header and 'CD1_1 CD1_2' in header and 'CD2_1 CD2_2' in header and 'NAXIS' in header: #cerco WCS in header pass else: w.wcs.ctype = ["RA---TAN", "DEC--TAN"] w.wcs.cd = w.wcs.cd = [[0, 0.00013888888888888889], [0.00013888888888888889, 0]] if 'RA' in header and 'DEC' in header: c = SkyCoord(header['RA'], header['DEC'], unit=(u.hourangle, u.deg),equinox="J2019.9") w.wcs.crval = [c.ra.degree, c.dec.degree] w.wcs.crpix = [header['NAXIS1']/2, header['NAXIS2']/2] else: o = get_obj(target, xobj, yobj, pattern) c = SkyCoord.from_name(o['obj'],equinox="J2019.9") w.wcs.crval = [c.ra.degree, c.dec.degree] w.wcs.crpix = [header['NAXIS1']/2, header['NAXIS2']/2] #o, in alternativa, xobj e yobj date in input header.extend(w.to_header(), update=True) return(w)
def test_varying_transform_pc(): varying_matrix_lt = [ rotation_matrix(a)[:2, :2] for a in np.linspace(0, 90, 10) ] * u.arcsec vct = VaryingCelestialTransform(crpix=(5, 5) * u.pix, cdelt=(1, 1) * u.arcsec / u.pix, crval_table=(0, 0) * u.arcsec, pc_table=varying_matrix_lt, lon_pole=180 * u.deg) trans5 = vct.transform_at_index(5) assert isinstance(trans5, CompoundModel) # Verify that we have the 5th matrix in the series affine = trans5.left.left.right assert isinstance(affine, m.AffineTransformation2D) assert u.allclose(affine.matrix, varying_matrix_lt[5]) # x.shape=(1,), y.shape=(1,), z.shape=(1,) pixel = (0 * u.pix, 0 * u.pix, 5 * u.pix) world = vct(*pixel) assert np.array(world[0]).shape == () assert u.allclose(world, (359.99804329 * u.deg, 0.00017119 * u.deg)) assert u.allclose(vct.inverse(*world, 5 * u.pix), pixel[:2], atol=0.01 * u.pix)
def test_vct_shape_errors(): pc_table = [rotation_matrix(a)[:2, :2] for a in np.linspace(0, 90, 15)] * u.arcsec pc_table = pc_table.reshape((5, 3, 2, 2)) crval_table = list(zip(np.arange(1, 16), np.arange(16, 31))) * u.arcsec crval_table = crval_table.reshape((5, 3, 2)) kwargs = dict(crpix=(5, 5) * u.pix, cdelt=(1, 1) * u.arcsec / u.pix, lon_pole=180 * u.deg) with pytest.raises(ValueError, match="only be constructed with a one dimensional"): VaryingCelestialTransform(crval_table=crval_table, pc_table=pc_table, **kwargs) with pytest.raises(ValueError, match="only be constructed with a one dimensional"): VaryingCelestialTransformSlit(crval_table=crval_table, pc_table=pc_table, **kwargs) with pytest.raises(ValueError, match="only be constructed with a two dimensional"): VaryingCelestialTransform2D(crval_table=crval_table[0], pc_table=pc_table[0], **kwargs) with pytest.raises(ValueError, match="only be constructed with a two dimensional"): VaryingCelestialTransformSlit2D(crval_table=crval_table[0], pc_table=pc_table[0], **kwargs)
def test_vct_dispatch(): varying_matrix_lt = [ rotation_matrix(a)[:2, :2] for a in np.linspace(0, 90, 15) ] * u.arcsec varying_matrix_lt = varying_matrix_lt.reshape((5, 3, 2, 2)) crval_table = list(zip(np.arange(1, 16), np.arange(16, 31))) * u.arcsec crval_table = crval_table.reshape((5, 3, 2)) kwargs = dict(crpix=(5, 5) * u.pix, cdelt=(1, 1) * u.arcsec / u.pix, lon_pole=180 * u.deg) vct_3d = varying_celestial_transform_from_tables( pc_table=varying_matrix_lt[0], crval_table=crval_table[0], **kwargs) assert isinstance(vct_3d, VaryingCelestialTransform) vct_2d = varying_celestial_transform_from_tables( pc_table=varying_matrix_lt, crval_table=crval_table, **kwargs) assert isinstance(vct_2d, VaryingCelestialTransform2D) with pytest.raises( ValueError, match="Only one or two dimensional lookup tables are supported"): varying_celestial_transform_from_tables( pc_table=varying_matrix_lt[1:].reshape((3, 2, 2, 2, 2)), crval_table=crval_table[1:].reshape((3, 2, 2, 2)), **kwargs)
def from_equatorial(equatorial_coo, fixed_frame): # TODO replace w/ something smart (Sun/Earth special cased) if fixed_frame.body == Sun: assert type(equatorial_coo) == HCRS else: assert equatorial_coo.body == fixed_frame.body r = equatorial_coo.cartesian ra, dec, W = fixed_frame.rot_elements_at_epoch(fixed_frame.obstime) r_trans2 = r.transform(rotation_matrix(90 * u.deg + ra, "z")) r_f = r_trans2.transform(rotation_matrix(90 * u.deg - dec, "x")) r_f = r_f.transform(rotation_matrix(W, "z")) return fixed_frame.realize_frame(r_f)
def test_static_matrix_combine_paths(): """ Check that combined staticmatrixtransform matrices provide the same transformation as using an intermediate transformation. This is somewhat of a regression test for #7706 """ from astropy.coordinates.baseframe import BaseCoordinateFrame from astropy.coordinates.matrix_utilities import rotation_matrix class AFrame(BaseCoordinateFrame): default_representation = r.SphericalRepresentation default_differential = r.SphericalCosLatDifferential t1 = t.StaticMatrixTransform(rotation_matrix(30.*u.deg, 'z'), ICRS, AFrame) t1.register(frame_transform_graph) t2 = t.StaticMatrixTransform(rotation_matrix(30.*u.deg, 'z').T, AFrame, ICRS) t2.register(frame_transform_graph) class BFrame(BaseCoordinateFrame): default_representation = r.SphericalRepresentation default_differential = r.SphericalCosLatDifferential t3 = t.StaticMatrixTransform(rotation_matrix(30.*u.deg, 'x'), ICRS, BFrame) t3.register(frame_transform_graph) t4 = t.StaticMatrixTransform(rotation_matrix(30.*u.deg, 'x').T, BFrame, ICRS) t4.register(frame_transform_graph) c = Galactic(123*u.deg, 45*u.deg) c1 = c.transform_to(BFrame) # direct c2 = c.transform_to(AFrame).transform_to(BFrame) # thru A c3 = c.transform_to(ICRS).transform_to(BFrame) # thru ICRS assert quantity_allclose(c1.lon, c2.lon) assert quantity_allclose(c1.lat, c2.lat) assert quantity_allclose(c1.lon, c3.lon) assert quantity_allclose(c1.lat, c3.lat) for t_ in [t1, t2, t3, t4]: t_.unregister(frame_transform_graph)
def _create_matrix(self, phi, theta, psi, axes_order): matrices = [] for angle, axis in zip([phi, theta, psi], axes_order): if isinstance(angle, u.Quantity): angle = angle.value angle = angle.item() matrices.append(rotation_matrix(angle, axis, unit=u.rad)) result = matrix_product(*matrices[::-1]) return result
def get_matrix_vectors(galactocentric_frame, inverse=False): """ Use the ``inverse`` argument to get the inverse transformation, matrix and offsets to go from Galactocentric to ICRS. """ # shorthand gcf = galactocentric_frame # rotation matrix to align x(ICRS) with the vector to the Galactic center mat1 = rotation_matrix(-gcf.galcen_coord.dec, 'y') mat2 = rotation_matrix(gcf.galcen_coord.ra, 'z') # extra roll away from the Galactic x-z plane mat0 = rotation_matrix(gcf.get_roll0() - gcf.roll, 'x') # construct transformation matrix and use it R = matrix_product(mat0, mat1, mat2) # Now need to translate by Sun-Galactic center distance around x' and # rotate about y' to account for tilt due to Sun's height above the plane translation = r.CartesianRepresentation(gcf.galcen_distance * [1., 0., 0.]) z_d = gcf.z_sun / gcf.galcen_distance H = rotation_matrix(-np.arcsin(z_d), 'y') # compute total matrices A = matrix_product(H, R) # Now we re-align the translation vector to account for the Sun's height # above the midplane offset = -translation.transform(H) if inverse: # the inverse of a rotation matrix is a transpose, which is much faster # and more stable to compute A = matrix_transpose(A) offset = (-offset).transform(A) offset_v = r.CartesianDifferential.from_cartesian( (-gcf.galcen_v_sun).to_cartesian().transform(A)) offset = offset.with_differentials(offset_v) else: offset = offset.with_differentials(gcf.galcen_v_sun) return A, offset
def _ecliptic_rotation_matrix(equinox): # This code calls pmat06 from ERFA, which retrieves the precession # matrix (including frame bias) according to the IAU 2006 model, but # leaves out the nutation. This matches what ERFA does in the ecm06 # function and also brings the results closer to what other libraries # give (see https://github.com/astropy/astropy/pull/6508). However, # notice that this makes the name "TrueEcliptic" misleading, and might # be changed in the future (discussion in the same pull request) jd1, jd2 = get_jd12(equinox, 'tt') rbp = erfa.pmat06(jd1, jd2) obl = erfa.obl06(jd1, jd2)*u.radian return matrix_product(rotation_matrix(obl, 'x'), rbp)
def _make_rotation_matrix_from_reprs(start_representation, end_representation): """ Return the matrix for the direct rotation from one representation to a second representation. The representations need not be normalized first. """ A = start_representation.to_cartesian() B = end_representation.to_cartesian() rotation_axis = A.cross(B) rotation_angle = -np.arccos(A.dot(B) / (A.norm() * B.norm())) # negation is required # This line works around some input/output quirks of Astropy's rotation_matrix() matrix = np.array(rotation_matrix(rotation_angle, rotation_axis.xyz.value.tolist())) return matrix
def _rotate_polygon(lon, lat, lon0, lat0): """ Given a polygon with vertices defined by (lon, lat), rotate the polygon such that the North pole of the spherical coordinates is now at (lon0, lat0). Therefore, to end up with a polygon centered on (lon0, lat0), the polygon should initially be drawn around the North pole. """ # Create a representation object polygon = UnitSphericalRepresentation(lon=lon, lat=lat) # Determine rotation matrix to make it so that the circle is centered # on the correct longitude/latitude. m1 = rotation_matrix(-(0.5 * np.pi * u.radian - lat0), axis='y') m2 = rotation_matrix(-lon0, axis='z') transform_matrix = matrix_product(m2, m1) # Apply 3D rotation polygon = polygon.to_cartesian() polygon = polygon.transform(transform_matrix) polygon = UnitSphericalRepresentation.from_cartesian(polygon) return polygon.lon, polygon.lat
def test_rotation_matrix(): assert_array_equal(rotation_matrix(0*u.deg, 'x'), np.eye(3)) assert_allclose(rotation_matrix(90*u.deg, 'y'), [[0, 0, -1], [0, 1, 0], [1, 0, 0]], atol=1e-12) assert_allclose(rotation_matrix(-90*u.deg, 'z'), [[0, -1, 0], [1, 0, 0], [0, 0, 1]], atol=1e-12) assert_allclose(rotation_matrix(45*u.deg, 'x'), rotation_matrix(45*u.deg, [1, 0, 0])) assert_allclose(rotation_matrix(125*u.deg, 'y'), rotation_matrix(125*u.deg, [0, 1, 0])) assert_allclose(rotation_matrix(-30*u.deg, 'z'), rotation_matrix(-30*u.deg, [0, 0, 1])) assert_allclose(np.dot(rotation_matrix(180*u.deg, [1, 1, 0]), [1, 0, 0]), [0, 1, 0], atol=1e-12) # make sure it also works for very small angles assert_allclose(rotation_matrix(0.000001*u.deg, 'x'), rotation_matrix(0.000001*u.deg, [1, 0, 0]))
def gal_to_mag(): mat1 = rotation_matrix(57.275785782128686*u.deg, 'z') mat2 = rotation_matrix(90*u.deg - MagellanicStreamNidever08._ngp.b, 'y') mat3 = rotation_matrix(MagellanicStreamNidever08._ngp.l, 'z') return matrix_product(mat1, mat2, mat3)
# TODO: remove this. This is a hack required as of astropy v3.1 in order # to have the longitude components wrap at the desired angle def represent_as(self, base, s='base', in_frame_units=False): r = super().represent_as(base, s=s, in_frame_units=in_frame_units) r.lon.wrap_angle = self._default_wrap_angle return r represent_as.__doc__ = coord.BaseCoordinateFrame.represent_as.__doc__ # Define the Euler angles phi = 128.79 * u.degree theta = 54.39 * u.degree psi = 90.70 * u.degree # Generate the rotation matrix using the x-convention (see Goldstein) D = rotation_matrix(phi, "z") C = rotation_matrix(theta, "x") B = rotation_matrix(psi, "z") R = matrix_product(B, C, D) @frame_transform_graph.transform(coord.StaticMatrixTransform, coord.Galactic, OrphanNewberg10) def galactic_to_orp(): """ Compute the transformation from Galactic spherical to heliocentric Orphan coordinates. """ return R # Oph to Galactic coordinates @frame_transform_graph.transform(coord.StaticMatrixTransform, OrphanNewberg10, coord.Galactic)
# In this case we override the names in the spherical representations but don't # do anything with other representations like cartesian or cylindrical. # # Next we have to define the transformation from this coordinate system to some # other built-in coordinate system; we will use Galactic coordinates. We can do # this by defining functions that return transformation matrices, or by simply # defining a function that accepts a coordinate and returns a new coordinate in # the new system. We'll start by constructing the rotation matrix, using the # ``rotation_matrix`` helper function: SGR_PHI = np.radians(180+3.75) # Euler angles (from Law & Majewski 2010) SGR_THETA = np.radians(90-13.46) SGR_PSI = np.radians(180+14.111534) # Generate the rotation matrix using the x-convention (see Goldstein) D = rotation_matrix(SGR_PHI, "z", unit=u.radian) C = rotation_matrix(SGR_THETA, "x", unit=u.radian) B = rotation_matrix(SGR_PSI, "z", unit=u.radian) SGR_MATRIX = np.array(B.dot(C).dot(D)) ############################################################################## # This is done at the module level, since it will be used by both the # transformation from Sgr to Galactic as well as the inverse from Galactic to # Sgr. Now we can define our first transformation function: @frame_transform_graph.transform(coord.FunctionTransform, coord.Galactic, Sagittarius) def galactic_to_sgr(gal_coord, sgr_frame): """ Compute the transformation from Galactic spherical to heliocentric Sgr coordinates. """
def _ecliptic_rotation_matrix(): jd1, jd2 = get_jd12(J2000, J2000.scale) obl = erfa.obl80(jd1, jd2) * u.radian assert obl.to(u.arcsec).value == 84381.448 return rotation_matrix(obl, "x")
def gal_to_supergal(): mat1 = rotation_matrix(90, 'z') mat2 = rotation_matrix(90 - Supergalactic._nsgp_gal.b.degree, 'y') mat3 = rotation_matrix(Supergalactic._nsgp_gal.l.degree, 'z') return matrix_product(mat1, mat2, mat3)
# Next we have to define the transformation from this coordinate system to some # other built-in coordinate system; we will use Galactic coordinates. We can do # this by defining functions that return transformation matrices, or by simply # defining a function that accepts a coordinate and returns a new coordinate in # the new system. Because the transformation to the Sagittarius coordinate # system is just a spherical rotation from Galactic coordinates, we'll just # define a function that returns this matrix. We'll start by constructing the # transformation matrix using pre-deteremined Euler angles and the # ``rotation_matrix`` helper function: SGR_PHI = (180 + 3.75) * u.degree # Euler angles (from Law & Majewski 2010) SGR_THETA = (90 - 13.46) * u.degree SGR_PSI = (180 + 14.111534) * u.degree # Generate the rotation matrix using the x-convention (see Goldstein) D = rotation_matrix(SGR_PHI, "z") C = rotation_matrix(SGR_THETA, "x") B = rotation_matrix(SGR_PSI, "z") A = np.diag([1.,1.,-1.]) SGR_MATRIX = matrix_product(A, B, C, D) ############################################################################## # Since we already constructed the transformation (rotation) matrix above, and # the inverse of a rotation matrix is just its transpose, the required # transformation functions are very simple: @frame_transform_graph.transform(coord.StaticMatrixTransform, coord.Galactic, Sagittarius) def galactic_to_sgr(): """ Compute the transformation matrix from Galactic spherical to heliocentric Sgr coordinates. """
def _ecliptic_rotation_matrix_pulsar(obl): """Here we only do the obliquity angle rotation. Astropy will add the precession-nutation correction. """ return rotation_matrix(obl, 'x')