# Astronomical Coordinates (astropy.coordinates) The `astropy.coordinates` package provides tools for representing celestial coordinates and transforming between different coordinate systems. ## Creating Coordinates with SkyCoord The high-level `SkyCoord` class is the recommended interface: ```python from astropy import units as u from astropy.coordinates import SkyCoord # Decimal degrees c = SkyCoord(ra=10.625*u.degree, dec=41.2*u.degree, frame='icrs') # Sexagesimal strings c = SkyCoord(ra='00h42m30s', dec='+41d12m00s', frame='icrs') # Mixed formats c = SkyCoord('00h42.5m +41d12m', unit=(u.hourangle, u.deg)) # Galactic coordinates c = SkyCoord(l=120.5*u.degree, b=-23.4*u.degree, frame='galactic') ``` ## Array Coordinates Process multiple coordinates efficiently using arrays: ```python # Create array of coordinates coords = SkyCoord(ra=[10, 11, 12]*u.degree, dec=[41, -5, 42]*u.degree) # Access individual elements coords[0] coords[1:3] # Array operations coords.shape len(coords) ``` ## Accessing Components ```python c = SkyCoord(ra=10.68*u.degree, dec=41.27*u.degree, frame='icrs') # Access coordinates c.ra # c.dec # c.ra.hour # Convert to hours c.ra.hms # Hours, minutes, seconds tuple c.dec.dms # Degrees, arcminutes, arcseconds tuple ``` ## String Formatting ```python c.to_string('decimal') # '10.68 41.27' c.to_string('dms') # '10d40m48s 41d16m12s' c.to_string('hmsdms') # '00h42m43.2s +41d16m12s' # Custom formatting c.ra.to_string(unit=u.hour, sep=':', precision=2) ``` ## Coordinate Transformations Transform between reference frames: ```python c_icrs = SkyCoord(ra=10.68*u.degree, dec=41.27*u.degree, frame='icrs') # Simple transformations (as attributes) c_galactic = c_icrs.galactic c_fk5 = c_icrs.fk5 c_fk4 = c_icrs.fk4 # Explicit transformations c_icrs.transform_to('galactic') c_icrs.transform_to(FK5(equinox='J1975')) # Custom frame parameters ``` ## Common Coordinate Frames ### Celestial Frames - **ICRS**: International Celestial Reference System (default, most common) - **FK5**: Fifth Fundamental Catalogue (equinox J2000.0 by default) - **FK4**: Fourth Fundamental Catalogue (older, requires equinox specification) - **GCRS**: Geocentric Celestial Reference System - **CIRS**: Celestial Intermediate Reference System ### Galactic Frames - **Galactic**: IAU 1958 galactic coordinates - **Supergalactic**: De Vaucouleurs supergalactic coordinates - **Galactocentric**: Galactic center-based 3D coordinates ### Horizontal Frames - **AltAz**: Altitude-azimuth (observer-dependent) - **HADec**: Hour angle-declination ### Ecliptic Frames - **GeocentricMeanEcliptic**: Geocentric mean ecliptic - **BarycentricMeanEcliptic**: Barycentric mean ecliptic - **HeliocentricMeanEcliptic**: Heliocentric mean ecliptic ## Observer-Dependent Transformations For altitude-azimuth coordinates, specify observation time and location: ```python from astropy.time import Time from astropy.coordinates import EarthLocation, AltAz # Define observer location observing_location = EarthLocation(lat=40.8*u.deg, lon=-121.5*u.deg, height=1060*u.m) # Or use named observatory observing_location = EarthLocation.of_site('Apache Point Observatory') # Define observation time observing_time = Time('2023-01-15 23:00:00') # Transform to alt-az aa_frame = AltAz(obstime=observing_time, location=observing_location) aa = c_icrs.transform_to(aa_frame) print(f"Altitude: {aa.alt}") print(f"Azimuth: {aa.az}") ``` ## Working with Distances Add distance information for 3D coordinates: ```python # With distance c = SkyCoord(ra=10*u.degree, dec=9*u.degree, distance=770*u.kpc, frame='icrs') # Access 3D Cartesian coordinates c.cartesian.x c.cartesian.y c.cartesian.z # Distance from origin c.distance # 3D separation c1 = SkyCoord(ra=10*u.degree, dec=9*u.degree, distance=10*u.pc) c2 = SkyCoord(ra=11*u.degree, dec=10*u.degree, distance=11.5*u.pc) sep_3d = c1.separation_3d(c2) # 3D distance ``` ## Angular Separation Calculate on-sky separations: ```python c1 = SkyCoord(ra=10*u.degree, dec=9*u.degree, frame='icrs') c2 = SkyCoord(ra=11*u.degree, dec=10*u.degree, frame='fk5') # Angular separation (handles frame conversion automatically) sep = c1.separation(c2) print(f"Separation: {sep.arcsec} arcsec") # Position angle pa = c1.position_angle(c2) ``` ## Catalog Matching Match coordinates to catalog sources: ```python # Single target matching catalog = SkyCoord(ra=ra_array*u.degree, dec=dec_array*u.degree) target = SkyCoord(ra=10.5*u.degree, dec=41.2*u.degree) # Find closest match idx, sep2d, dist3d = target.match_to_catalog_sky(catalog) matched_coord = catalog[idx] # Match with maximum separation constraint matches = target.separation(catalog) < 1*u.arcsec ``` ## Named Objects Retrieve coordinates from online catalogs: ```python # Query by name (requires internet) m31 = SkyCoord.from_name("M31") crab = SkyCoord.from_name("Crab Nebula") psr = SkyCoord.from_name("PSR J1012+5307") ``` ## Earth Locations Define observer locations: ```python # By coordinates location = EarthLocation(lat=40*u.deg, lon=-120*u.deg, height=1000*u.m) # By named observatory keck = EarthLocation.of_site('Keck Observatory') vlt = EarthLocation.of_site('Paranal Observatory') # By address (requires internet) location = EarthLocation.of_address('1002 Holy Grail Court, St. Louis, MO') # List available observatories EarthLocation.get_site_names() ``` ## Velocity Information Include proper motion and radial velocity: ```python # Proper motion c = SkyCoord(ra=10*u.degree, dec=41*u.degree, pm_ra_cosdec=15*u.mas/u.yr, pm_dec=5*u.mas/u.yr, distance=150*u.pc) # Radial velocity c = SkyCoord(ra=10*u.degree, dec=41*u.degree, radial_velocity=20*u.km/u.s) # Both c = SkyCoord(ra=10*u.degree, dec=41*u.degree, distance=150*u.pc, pm_ra_cosdec=15*u.mas/u.yr, pm_dec=5*u.mas/u.yr, radial_velocity=20*u.km/u.s) ``` ## Representation Types Switch between coordinate representations: ```python # Cartesian representation c = SkyCoord(x=1*u.kpc, y=2*u.kpc, z=3*u.kpc, representation_type='cartesian', frame='icrs') # Change representation c.representation_type = 'cylindrical' c.rho # Cylindrical radius c.phi # Azimuthal angle c.z # Height # Spherical (default for most frames) c.representation_type = 'spherical' ``` ## Performance Tips 1. **Use arrays, not loops**: Process multiple coordinates as single array 2. **Pre-compute frames**: Reuse frame objects for multiple transformations 3. **Use broadcasting**: Efficiently transform many positions across many times 4. **Enable interpolation**: For dense time sampling, use ErfaAstromInterpolator ```python # Fast approach coords = SkyCoord(ra=ra_array*u.degree, dec=dec_array*u.degree) coords_transformed = coords.transform_to('galactic') # Slow approach (avoid) for ra, dec in zip(ra_array, dec_array): c = SkyCoord(ra=ra*u.degree, dec=dec*u.degree) c_transformed = c.transform_to('galactic') ```