code: import numpy as np
  
  import astropy.units as u
  import astropy.wcs.utils
  from astropy.coordinates import (
      ITRS,
      BaseCoordinateFrame,
      CartesianRepresentation,
      SkyCoord,
      SphericalRepresentation,
  )
  from astropy.wcs import WCS
  
  from sunpy import log
  from .frames import (
      BaseCoordinateFrame,
      Heliocentric,
      HeliographicCarrington,
      HeliographicStonyhurst,
      Helioprojective,
      SunPyBaseCoordinateFrame,
  )
  
  __all__ = ['solar_wcs_frame_mapping', 'solar_frame_to_wcs_mapping']
  
  try:
      # TODO: Remove vendored version after Astropy 5.0
      from astropy.wcs.utils import obsgeo_to_frame
  except ImportError:
      def obsgeo_to_frame(obsgeo, obstime):
          """
          Convert a WCS obsgeo property into an `~builtin_frames.ITRS` coordinate frame.
  
          Parameters
          ----------
          obsgeo : array-like
              A shape ``(6, )`` array representing ``OBSGEO-[XYZ], OBSGEO-[BLH]`` as
              returned by ``WCS.wcs.obsgeo``.
  
          obstime : time-like
              The time assiociated with the coordinate, will be passed to
              `~.builtin_frames.ITRS` as the obstime keyword.
  
          Returns
          -------
          `~.builtin_frames.ITRS`
              An `~.builtin_frames.ITRS` coordinate frame
              representing the coordinates.
  
          Notes
          -----
  
          The obsgeo array as accessed on a `.WCS` object is a length 6 numpy array
          where the first three elements are the coordinate in a cartesian
          representation and the second 3 are the coordinate in a spherical
          representation.
  
          This function priorities reading the cartesian coordinates, and will only
          read the spherical coordinates if the cartesian coordinates are either all
          zero or any of the cartesian coordinates are non-finite.
  
          In the case where both the spherical and cartesian coordinates have some
          non-finite values the spherical coordinates will be returned with the
          non-finite values included.
  
          """
          if (obsgeo is None
              or len(obsgeo) != 6
              or np.all(np.array(obsgeo) == 0)
              or np.all(~np.isfinite(obsgeo))
          ):  # NOQA
              raise ValueError(f"Can not parse the 'obsgeo' location ({obsgeo}). "
                               "obsgeo should be a length 6 non-zero, finite numpy array")
  
          # If the cartesian coords are zero or have NaNs in them use the spherical ones
          if np.all(obsgeo[:3] == 0) or np.any(~np.isfinite(obsgeo[:3])):
              data = SphericalRepresentation(*(obsgeo[3:] * (u.deg, u.deg, u.m)))
  
          # Otherwise we assume the cartesian ones are valid
          else:
              data = CartesianRepresentation(*obsgeo[:3] * u.m)
  
          return ITRS(data, obstime=obstime)
  
  
  def solar_wcs_frame_mapping(wcs):
      """
      This function registers the coordinates frames to their FITS-WCS coordinate
      type values in the `astropy.wcs.utils.wcs_to_celestial_frame` registry.
  
      Parameters
      ----------
      wcs : astropy.wcs.WCS
  
      Returns
      -------
      astropy.coordinates.BaseCoordinateFrame
      """
  
      if hasattr(wcs, "coordinate_frame"):
          return wcs.coordinate_frame
  
      dateobs = wcs.wcs.dateobs or None
  
      # Get observer coordinate from the WCS auxillary information
      required_attrs = {HeliographicStonyhurst: ['hgln_obs', 'hglt_obs', 'dsun_obs'],
                        HeliographicCarrington: ['crln_obs', 'hglt_obs', 'dsun_obs']}
  
      # Get rsun from the WCS auxillary information
      rsun = wcs.wcs.aux.rsun_ref
      if rsun is not None:
          rsun *= u.m
  
      # TODO: remove these errors in sunpy 4.1
      bad_attrs = [f'.{attr}' for attr in ['rsun', 'heliographic_observer']
                   if hasattr(wcs, attr)]
      if len(bad_attrs):
          raise ValueError(f"The {' and '.join(bad_attrs)} attribute(s) on a WCS "
                           "are no longer supported.")
  
      observer = None
      for frame, attr_names in required_attrs.items():
          attrs = [getattr(wcs.wcs.aux, attr_name) for attr_name in attr_names]
          if all([attr is not None for attr in attrs]):
              kwargs = {'obstime': dateobs}
              if rsun is not None:
                  kwargs['rsun'] = rsun
              if issubclass(frame, HeliographicCarrington):
                  kwargs['observer'] = 'self'
  
              observer = frame(attrs[0] * u.deg,
                               attrs[1] * u.deg,
                               attrs[2] * u.m,
                               **kwargs)
  
      # Read the observer out of obsgeo for ground based observers
      if observer is None:
          try:
              observer = obsgeo_to_frame(wcs.wcs.obsgeo, dateobs)
              observer = SkyCoord(observer, rsun=rsun)
          except ValueError as e:
              # The helper function assumes you know the obsgeo coords you are
              # parsing are good, we are not sure, so catch the error.
  
              # This approach could lead to an invalid observer (i.e. one of the
              # coords being NaN), but only if the WCS has been constructed like that.
              log.debug(f"Could not parse obsgeo coordinates from WCS:\n{e}")
  
      # Collect all of the possible frame attributes, although some may be removed later
      frame_args = {'obstime': dateobs}
      if observer is not None:
          frame_args['observer'] = observer
      if rsun is not None:
          frame_args['rsun'] = rsun
  
      frame_class = _sunpy_frame_class_from_ctypes(wcs.wcs.ctype)
  
      if frame_class:
          if frame_class == HeliographicStonyhurst:
              frame_args.pop('observer', None)
          if frame_class == Heliocentric:
              frame_args.pop('rsun', None)
  
          return frame_class(**frame_args)
  
  
  def _sunpy_frame_class_from_ctypes(ctypes):
      # Truncate the ctype to the first four letters
      ctypes = {c[:4] for c in ctypes}
  
      mapping = {
          Helioprojective: {'HPLN', 'HPLT'},
          HeliographicStonyhurst: {'HGLN', 'HGLT'},
          HeliographicCarrington: {'CRLN', 'CRLT'},
          Heliocentric: {'SOLX', 'SOLY'},
      }
  
      for frame_class, ctype_pair in mapping.items():
          if ctype_pair <= ctypes:
              return frame_class
  
  
  def _set_wcs_aux_obs_coord(wcs, obs_frame):
      """
      Set (in-place) observer coordinate information on a WCS.
  
      Parameters
      ----------
      wcs : astropy.wcs.WCS
      obs_frame : astropy.coordinates.SkyCoord, astropy.coordinates.CoordinateFrame
      """
      # Sometimes obs_coord can be a SkyCoord, so convert down to a frame
      if hasattr(obs_frame, 'frame'):
          obs_frame = obs_frame.frame
  
      if isinstance(obs_frame, HeliographicStonyhurst):
          wcs.wcs.aux.hgln_obs = obs_frame.lon.to_value(u.deg)
      elif isinstance(obs_frame, HeliographicCarrington):
          wcs.wcs.aux.crln_obs = obs_frame.lon.to_value(u.deg)
      else:
          raise ValueError('obs_coord must be in a Stonyhurst or Carrington frame')
      # These two keywords are the same for Carrington and Stonyhurst
      wcs.wcs.aux.hglt_obs = obs_frame.lat.to_value(u.deg)
      wcs.wcs.aux.dsun_obs = obs_frame.radius.to_value(u.m)
  
  
  def solar_frame_to_wcs_mapping(frame, projection='TAN'):
      """
      For a given frame, this function returns the corresponding WCS object.
      It registers the WCS coordinates types from their associated frame in the
      `astropy.wcs.utils.celestial_frame_to_wcs` registry.
  
      Parameters
      ----------
      frame : astropy.coordinates.BaseCoordinateFrame
      projection : str, optional
  
      Returns
      -------
      astropy.wcs.WCS
      """
      wcs = WCS(naxis=2)
  
      if hasattr(frame, 'rsun'):
          wcs.wcs.aux.rsun_ref = frame.rsun.to_value(u.m)
  
      if hasattr(frame, 'observer') and frame.observer is not None:
          if isinstance(frame.observer, BaseCoordinateFrame):
              observer = frame.observer
          elif frame.observer == 'self':
              observer = frame
          _set_wcs_aux_obs_coord(wcs, observer)
  
      if isinstance(frame, SunPyBaseCoordinateFrame):
  
          if frame.obstime:
              wcs.wcs.dateobs = frame.obstime.utc.isot
  
          if isinstance(frame, Helioprojective):
              xcoord = 'HPLN' + '-' + projection
              ycoord = 'HPLT' + '-' + projection
              wcs.wcs.cunit = ['arcsec', 'arcsec']
          elif isinstance(frame, Heliocentric):
              xcoord = 'SOLX'
              ycoord = 'SOLY'
              wcs.wcs.cunit = ['deg', 'deg']
          elif isinstance(frame, HeliographicCarrington):
              xcoord = 'CRLN' + '-' + projection
              ycoord = 'CRLT' + '-' + projection
              wcs.wcs.cunit = ['deg', 'deg']
          elif isinstance(frame, HeliographicStonyhurst):
              xcoord = 'HGLN' + '-' + projection
              ycoord = 'HGLT' + '-' + projection
              wcs.wcs.cunit = ['deg', 'deg']
  
      else:
          return None
  
      wcs.wcs.ctype = [xcoord, ycoord]
  
      return wcs
  
  
  astropy.wcs.utils.WCS_FRAME_MAPPINGS.append([solar_wcs_frame_mapping])
  astropy.wcs.utils.FRAME_WCS_MAPPINGS.append([solar_frame_to_wcs_mapping])
  
apis: numpy.array numpy.all numpy.isfinite