def test_angle_repr(): assert 'Angle' in repr(Angle(0, u.deg)) assert 'Longitude' in repr(Longitude(0, u.deg)) assert 'Latitude' in repr(Latitude(0, u.deg)) a = Angle(0, u.deg) repr(a)
def to_selenodetic(self): """Convert to selenodetic coordinates (lat, lon, height). Height is in reference to a sphere with radius `_lunar_radius`, centered at the center of mass. Returns ------- (lon, lat, height) : tuple The tuple contains instances of `~astropy.coordinates.Longitude`, `~astropy.coordinates.Latitude`, and `~astropy.units.Quantity` """ self_xyz = self.to(u.meter).view(self._array_dtype, np.ndarray) self_xyz = np.atleast_2d(self_xyz) gps_p = np.sqrt(self_xyz[:, 0]**2 + self_xyz[:, 1]**2) lat = np.arctan2(self_xyz[:, 2], gps_p) lon = np.arctan2(self_xyz[:, 1], self_xyz[:, 0]) height = np.linalg.norm(self_xyz, axis=1) - self._lunar_radius return GeodeticLocation( Longitude(lon * u.radian, u.degree, wrap_angle=180. * u.degree, copy=False), Latitude(lat * u.radian, u.degree, copy=False), u.Quantity(height * u.meter, self.unit, copy=False))
def setup(self): self.lon = Longitude([0., 45., 90., 135., 180., -180, -90, -45], u.deg, wrap_angle=180*u.deg) self.lat = Latitude([+0., 30., 60., +90., -90., -60., -30., 0.], u.deg) self.h = u.Quantity([0.1, 0.5, 1.0, -0.5, -1.0, +4.2, -11., -.1], u.m) self.location = EarthLocation.from_geodetic(self.lon, self.lat, self.h) self.x, self.y, self.z = self.location.to_geocentric()
def from_selenodetic(cls, lon, lat, height=0.): """ Location on the Moon, from latitude and longitude. Parameters ---------- lon : `~astropy.coordinates.Longitude` or float Lunar East longitude. Can be anything that initialises an `~astropy.coordinates.Angle` object (if float, in degrees). lat : `~astropy.coordinates.Latitude` or float Lunar latitude. Can be anything that initialises an `~astropy.coordinates.Latitude` object (if float, in degrees). height : `~astropy.units.Quantity` or float, optional Height above reference sphere (if float, in meters; default: 0). The reference sphere is a sphere of radius 1737.1 kilometers, from the center of mass of the Moon. Raises ------ astropy.units.UnitsError If the units on ``lon`` and ``lat`` are inconsistent with angular ones, or that on ``height`` with a length. ValueError If ``lon``, ``lat``, and ``height`` do not have the same shape, or Notes ----- latitude is defined relative to an equator 90 degrees off from the mean rotation axis. Longitude is defined relative to a prime meridian, which is itself given by the mean position of the "sub-Earth" point on the lunar surface. """ lon = Longitude(lon, u.degree, wrap_angle=180 * u.degree, copy=False) lat = Latitude(lat, u.degree, copy=False) # don't convert to m by default, so we can use the height unit below. if not isinstance(height, u.Quantity): height = u.Quantity(height, u.m, copy=False) if not lon.shape == lat.shape: raise ValueError("Inconsistent quantity shapes: {}, {}".format( str(lon.shape), str(lat.shape))) # get selenocentric coordinates. Have to give one-dimensional array. lunar_radius = u.Quantity(cls._lunar_radius, u.m, copy=False) Npts = lon.size xyz = np.zeros((Npts, 3)) xyz[:, 0] = ((lunar_radius + height) * np.cos(lat) * np.cos(lon)) xyz[:, 1] = ((lunar_radius + height) * np.cos(lat) * np.sin(lon)) xyz[:, 2] = ((lunar_radius + height) * np.sin(lat)) xyz = np.squeeze(xyz) self = xyz.ravel().view(cls._location_dtype, cls).reshape(xyz.shape[:-1]) self._unit = u.meter return self.to(height.unit)
def position(): lon = Longitude([0., 45., 90., 135., 180., -180, -90, -45], u.deg, wrap_angle=180*u.deg) lat = Latitude([+0., 30., 60., +90., -90., -60., -30., 0.], u.deg) h = u.Quantity([0.1, 0.5, 1.0, -0.5, -1.0, +4.2, -11., -.1], u.m) return lon, lat, h
def test_angle_format_roundtripping(): """ Ensures that the string representation of an angle can be used to create a new valid Angle. """ a1 = Angle(0, unit=u.radian) a2 = Angle(10, unit=u.degree) a3 = Angle(0.543, unit=u.degree) a4 = Angle('1d2m3.4s') assert Angle(str(a1)).degree == a1.degree assert Angle(str(a2)).degree == a2.degree assert Angle(str(a3)).degree == a3.degree assert Angle(str(a4)).degree == a4.degree # also check Longitude/Latitude ra = Longitude('1h2m3.4s') dec = Latitude('1d2m3.4s') assert_allclose(Angle(str(ra)).degree, ra.degree) assert_allclose(Angle(str(dec)).degree, dec.degree)
def test_longitude(): # Default wrapping at 360d with an array input lon = Longitude(['370d', '88d']) assert np.all(lon == Longitude(['10d', '88d'])) assert np.all(lon == Angle(['10d', '88d'])) # conserve type on unit change and keep wrap_angle (closes #1423) angle = lon.to('hourangle') assert type(angle) is Longitude assert angle.wrap_angle == lon.wrap_angle angle = lon[0] assert type(angle) is Longitude assert angle.wrap_angle == lon.wrap_angle angle = lon[1:] assert type(angle) is Longitude assert angle.wrap_angle == lon.wrap_angle # but not on calculations angle = lon / 2. assert np.all(angle == Angle(['5d', '44d'])) assert type(angle) is Angle assert not hasattr(angle, 'wrap_angle') angle = lon * 2. + 400 * u.deg assert np.all(angle == Angle(['420d', '576d'])) assert type(angle) is Angle # Test setting a mutable value and having it wrap lon[1] = -10 * u.deg assert np.all(lon == Angle(['10d', '350d'])) # Test wrapping and try hitting some edge cases lon = Longitude(np.array([0, 0.5, 1.0, 1.5, 2.0]) * np.pi, unit=u.radian) assert np.all(lon.degree == np.array([0., 90, 180, 270, 0])) lon = Longitude(np.array([0, 0.5, 1.0, 1.5, 2.0]) * np.pi, unit=u.radian, wrap_angle='180d') assert np.all(lon.degree == np.array([0., 90, -180, -90, 0])) # Wrap on setting wrap_angle property (also test auto-conversion of wrap_angle to an Angle) lon = Longitude(np.array([0, 0.5, 1.0, 1.5, 2.0]) * np.pi, unit=u.radian) lon.wrap_angle = '180d' assert np.all(lon.degree == np.array([0., 90, -180, -90, 0])) lon = Longitude('460d') assert lon == Angle('100d') lon.wrap_angle = '90d' assert lon == Angle('-260d') # check that if we initialize a longitude with another longitude, # wrap_angle is kept by default lon2 = Longitude(lon) assert lon2.wrap_angle == lon.wrap_angle # but not if we explicitly set it lon3 = Longitude(lon, wrap_angle='180d') assert lon3.wrap_angle == 180 * u.deg # check for problem reported in #2037 about Longitude initializing to -0 lon = Longitude(0, u.deg) lonstr = lon.to_string() assert not lonstr.startswith('-') # also make sure dtype is correctly conserved assert Longitude(0, u.deg, dtype=float).dtype == np.dtype(float) assert Longitude(0, u.deg, dtype=int).dtype == np.dtype(int) # Test errors when trying to interoperate with latitudes. with pytest.raises(TypeError) as excinfo: lat = Latitude(10, 'deg') lon = Longitude(lat) assert "A Longitude angle cannot be created from a Latitude angle" in str( excinfo.value) with pytest.raises(TypeError) as excinfo: lat = Latitude(10, 'deg') lon = Longitude([20], 'deg') lon[0] = lat assert "A Latitude angle cannot be assigned to a Longitude angle" in str( excinfo.value) # Check we can work around the Lat vs Long checks by casting explicitly to Angle. lat = Latitude(10, 'deg') lon = Longitude(Angle(lat)) assert lon.value == 10.0 # Check setitem. lat = Latitude(10, 'deg') lon = Longitude([20], 'deg') lon[0] = Angle(lat) assert lon.value[0] == 10.0
def test_latitude(): with pytest.raises(ValueError): lat = Latitude(['91d', '89d']) with pytest.raises(ValueError): lat = Latitude('-91d') lat = Latitude(['90d', '89d']) # check that one can get items assert lat[0] == 90 * u.deg assert lat[1] == 89 * u.deg # and that comparison with angles works assert np.all(lat == Angle(['90d', '89d'])) # check setitem works lat[1] = 45. * u.deg assert np.all(lat == Angle(['90d', '45d'])) # but not with values out of range with pytest.raises(ValueError): lat[0] = 90.001 * u.deg with pytest.raises(ValueError): lat[0] = -90.001 * u.deg # these should also not destroy input (#1851) assert np.all(lat == Angle(['90d', '45d'])) # conserve type on unit change (closes #1423) angle = lat.to('radian') assert type(angle) is Latitude # but not on calculations angle = lat - 190 * u.deg assert type(angle) is Angle assert angle[0] == -100 * u.deg lat = Latitude('80d') angle = lat / 2. assert type(angle) is Angle assert angle == 40 * u.deg angle = lat * 2. assert type(angle) is Angle assert angle == 160 * u.deg angle = -lat assert type(angle) is Angle assert angle == -80 * u.deg # Test errors when trying to interoperate with longitudes. with pytest.raises(TypeError) as excinfo: lon = Longitude(10, 'deg') lat = Latitude(lon) assert "A Latitude angle cannot be created from a Longitude angle" in str( excinfo.value) with pytest.raises(TypeError) as excinfo: lon = Longitude(10, 'deg') lat = Latitude([20], 'deg') lat[0] = lon assert "A Longitude angle cannot be assigned to a Latitude angle" in str( excinfo.value) # Check we can work around the Lat vs Long checks by casting explicitly to Angle. lon = Longitude(10, 'deg') lat = Latitude(Angle(lon)) assert lat.value == 10.0 # Check setitem. lon = Longitude(10, 'deg') lat = Latitude([20], 'deg') lat[0] = Angle(lon) assert lat.value[0] == 10.0
def test_radec(): """ Tests creation/operations of Longitude and Latitude objects """ ''' Longitude and Latitude are objects that are subclassed from Angle. As with Angle, Longitude and Latitude can parse any unambiguous format (tuples, formatted strings, etc.). The intention is not to create an Angle subclass for every possible coordinate object (e.g. galactic l, galactic b). However, equatorial Longitude/Latitude are so prevalent in astronomy that it's worth creating ones for these units. They will be noted as "special" in the docs and use of the just the Angle class is to be used for other coordinate systems. ''' with pytest.raises(u.UnitsError): ra = Longitude("4:08:15.162342") # error - hours or degrees? with pytest.raises(u.UnitsError): ra = Longitude("-4:08:15.162342") # the "smart" initializer allows >24 to automatically do degrees, but the # Angle-based one does not # TODO: adjust in 0.3 for whatever behavior is decided on # ra = Longitude("26:34:15.345634") # unambiguous b/c hours don't go past 24 # assert_allclose(ra.degree, 26.570929342) with pytest.raises(u.UnitsError): ra = Longitude("26:34:15.345634") # ra = Longitude(68) with pytest.raises(u.UnitsError): ra = Longitude(68) with pytest.raises(u.UnitsError): ra = Longitude(12) with pytest.raises(ValueError): ra = Longitude("garbage containing a d and no units") ra = Longitude("12h43m23s") assert_allclose(ra.hour, 12.7230555556) ra = Longitude((56, 14, 52.52), unit=u.degree) # can accept tuples # TODO: again, fix based on >24 behavior # ra = Longitude((56,14,52.52)) with pytest.raises(u.UnitsError): ra = Longitude((56, 14, 52.52)) with pytest.raises(u.UnitsError): ra = Longitude((12, 14, 52)) # ambiguous w/o units ra = Longitude((12, 14, 52), unit=u.hour) ra = Longitude([56, 64, 52.2], unit=u.degree) # ...but not arrays (yet) # Units can be specified ra = Longitude("4:08:15.162342", unit=u.hour) # TODO: this was the "smart" initializer behavior - adjust in 0.3 appropriately # Where Longitude values are commonly found in hours or degrees, declination is # nearly always specified in degrees, so this is the default. # dec = Latitude("-41:08:15.162342") with pytest.raises(u.UnitsError): dec = Latitude("-41:08:15.162342") dec = Latitude("-41:08:15.162342", unit=u.degree) # same as above
def test_latitude_nan(): # Check that passing a NaN to Latitude doesn't raise a warning Latitude([0, np.nan, 1] * u.deg)