docstr: Artist used by :meth:`cartopy.mpl.geoaxes.GeoAxes.gridlines`
  to add gridlines and tick labels to a map.
  
  Parameters
  ----------
  axes
      The :class:`cartopy.mpl.geoaxes.GeoAxes` object to be drawn on.
  crs
      The :class:`cartopy.crs.CRS` defining the coordinate system that
      the gridlines are drawn in.
  draw_labels: optional
      Toggle whether to draw labels. For finer control, attributes of
      :class:`Gridliner` may be modified individually. Defaults to False.
  
      - string: "x" or "y" to only draw labels of the respective
        coordinate in the CRS.
      - list: Can contain the side identifiers and/or coordinate
        types to select which ones to draw.
        For all labels one would use
        `["x", "y", "top", "bottom", "left", "right", "geo"]`.
      - dict: The keys are the side identifiers
        ("top", "bottom", "left", "right") and the values are the
        coordinates ("x", "y"); this way you can precisely
        decide what kind of label to draw and where.
        For x labels on the bottom and y labels on the right you
        could pass in `{"bottom": "x", "left": "y"}`.
  
      Note that, by default, x and y labels are not drawn on left/right
      and top/bottom edges respectively, unless explicitly requested.
  
  xlocator: optional
      A :class:`matplotlib.ticker.Locator` instance which will be used
      to determine the locations of the gridlines in the x-coordinate of
      the given CRS. Defaults to None, which implies automatic locating
      of the gridlines.
  ylocator: optional
      A :class:`matplotlib.ticker.Locator` instance which will be used
      to determine the locations of the gridlines in the y-coordinate of
      the given CRS. Defaults to None, which implies automatic locating
      of the gridlines.
  xformatter: optional
      A :class:`matplotlib.ticker.Formatter` instance to format labels
      for x-coordinate gridlines. It defaults to None, which implies the
      use of a :class:`cartopy.mpl.ticker.LongitudeFormatter` initiated
      with the ``dms`` argument, if the crs is of
      :class:`~cartopy.crs.PlateCarree` type.
  yformatter: optional
      A :class:`matplotlib.ticker.Formatter` instance to format labels
      for y-coordinate gridlines. It defaults to None, which implies the
      use of a :class:`cartopy.mpl.ticker.LatitudeFormatter` initiated
      with the ``dms`` argument, if the crs is of
      :class:`~cartopy.crs.PlateCarree` type.
  collection_kwargs: optional
      Dictionary controlling line properties, passed to
      :class:`matplotlib.collections.Collection`. Defaults to None.
  dms: bool
      When default locators and formatters are used,
      ticks are able to stop on minutes and seconds if minutes
      is set to True, and not fraction of degrees.
  x_inline: optional
      Toggle whether the x labels drawn should be inline.
  y_inline: optional
      Toggle whether the y labels drawn should be inline.
  auto_inline: optional
      Set x_inline and y_inline automatically based on projection.
  xlim: optional
      Set a limit for the gridlines so that they do not go all the
      way to the edge of the boundary. xlim can be a single number or
      a (min, max) tuple. If a single number, the limits will be
      (-xlim, +xlim).
  ylim: optional
      Set a limit for the gridlines so that they do not go all the
      way to the edge of the boundary. ylim can be a single number or
      a (min, max) tuple. If a single number, the limits will be
      (-ylim, +ylim).
  rotate_labels: optional, bool, str
      Allow the rotation of non-inline labels.
  
      - False: Do not rotate the labels.
      - True: Rotate the labels parallel to the gridlines.
      - None: no rotation except for some projections (default).
      - A float: Rotate labels by this value in degrees.
  
  xlabel_style: dict
      A dictionary passed through to ``ax.text`` on x label creation
      for styling of the text labels.
  ylabel_style: dict
      A dictionary passed through to ``ax.text`` on y label creation
      for styling of the text labels.
  labels_bbox_style: dict
      bbox style for all text labels
  xpadding: float
      Padding for x labels. If negative, the labels are
      drawn inside the map.
  ypadding: float
      Padding for y labels. If negative, the labels are
      drawn inside the map.
  offset_angle: float
      Difference of angle in degrees from 90 to define when
      a label must be flipped to be more readable.
      For example, a value of 10 makes a vertical top label to be
      flipped only at 100 degrees.
  auto_update: bool
      Whether to redraw the gridlines and labels when the figure is
      updated.
  formatter_kwargs: dict, optional
      Options passed to the default formatters.
      See :class:`~cartopy.mpl.ticker.LongitudeFormatter` and
      :class:`~cartopy.mpl.ticker.LatitudeFormatter`
  
  Notes
  -----
  The "x" and "y" labels for locators and formatters do not necessarily
  correspond to X and Y, but to the first and second coordinates of the
  specified CRS. For the common case of PlateCarree gridlines, these
  correspond to longitudes and latitudes. Depending on the projection
  used for the map, meridians and parallels can cross both the X axis and
  the Y axis.
code: def __init__(
      self,
      axes,
      crs,
      draw_labels=False,
      xlocator=None,
      ylocator=None,
      collection_kwargs=None,
      xformatter=None,
      yformatter=None,
      dms=False,
      x_inline=None,
      y_inline=None,
      auto_inline=True,
      xlim=None,
      ylim=None,
      rotate_labels=None,
      xlabel_style=None,
      ylabel_style=None,
      labels_bbox_style=None,
      xpadding=5,
      ypadding=5,
      offset_angle=25,
      auto_update=False,
      formatter_kwargs=None,
  ):
      """
      Artist used by :meth:`cartopy.mpl.geoaxes.GeoAxes.gridlines`
      to add gridlines and tick labels to a map.
  
      Parameters
      ----------
      axes
          The :class:`cartopy.mpl.geoaxes.GeoAxes` object to be drawn on.
      crs
          The :class:`cartopy.crs.CRS` defining the coordinate system that
          the gridlines are drawn in.
      draw_labels: optional
          Toggle whether to draw labels. For finer control, attributes of
          :class:`Gridliner` may be modified individually. Defaults to False.
  
          - string: "x" or "y" to only draw labels of the respective
            coordinate in the CRS.
          - list: Can contain the side identifiers and/or coordinate
            types to select which ones to draw.
            For all labels one would use
            `["x", "y", "top", "bottom", "left", "right", "geo"]`.
          - dict: The keys are the side identifiers
            ("top", "bottom", "left", "right") and the values are the
            coordinates ("x", "y"); this way you can precisely
            decide what kind of label to draw and where.
            For x labels on the bottom and y labels on the right you
            could pass in `{"bottom": "x", "left": "y"}`.
  
          Note that, by default, x and y labels are not drawn on left/right
          and top/bottom edges respectively, unless explicitly requested.
  
      xlocator: optional
          A :class:`matplotlib.ticker.Locator` instance which will be used
          to determine the locations of the gridlines in the x-coordinate of
          the given CRS. Defaults to None, which implies automatic locating
          of the gridlines.
      ylocator: optional
          A :class:`matplotlib.ticker.Locator` instance which will be used
          to determine the locations of the gridlines in the y-coordinate of
          the given CRS. Defaults to None, which implies automatic locating
          of the gridlines.
      xformatter: optional
          A :class:`matplotlib.ticker.Formatter` instance to format labels
          for x-coordinate gridlines. It defaults to None, which implies the
          use of a :class:`cartopy.mpl.ticker.LongitudeFormatter` initiated
          with the ``dms`` argument, if the crs is of
          :class:`~cartopy.crs.PlateCarree` type.
      yformatter: optional
          A :class:`matplotlib.ticker.Formatter` instance to format labels
          for y-coordinate gridlines. It defaults to None, which implies the
          use of a :class:`cartopy.mpl.ticker.LatitudeFormatter` initiated
          with the ``dms`` argument, if the crs is of
          :class:`~cartopy.crs.PlateCarree` type.
      collection_kwargs: optional
          Dictionary controlling line properties, passed to
          :class:`matplotlib.collections.Collection`. Defaults to None.
      dms: bool
          When default locators and formatters are used,
          ticks are able to stop on minutes and seconds if minutes
          is set to True, and not fraction of degrees.
      x_inline: optional
          Toggle whether the x labels drawn should be inline.
      y_inline: optional
          Toggle whether the y labels drawn should be inline.
      auto_inline: optional
          Set x_inline and y_inline automatically based on projection.
      xlim: optional
          Set a limit for the gridlines so that they do not go all the
          way to the edge of the boundary. xlim can be a single number or
          a (min, max) tuple. If a single number, the limits will be
          (-xlim, +xlim).
      ylim: optional
          Set a limit for the gridlines so that they do not go all the
          way to the edge of the boundary. ylim can be a single number or
          a (min, max) tuple. If a single number, the limits will be
          (-ylim, +ylim).
      rotate_labels: optional, bool, str
          Allow the rotation of non-inline labels.
  
          - False: Do not rotate the labels.
          - True: Rotate the labels parallel to the gridlines.
          - None: no rotation except for some projections (default).
          - A float: Rotate labels by this value in degrees.
  
      xlabel_style: dict
          A dictionary passed through to ``ax.text`` on x label creation
          for styling of the text labels.
      ylabel_style: dict
          A dictionary passed through to ``ax.text`` on y label creation
          for styling of the text labels.
      labels_bbox_style: dict
          bbox style for all text labels
      xpadding: float
          Padding for x labels. If negative, the labels are
          drawn inside the map.
      ypadding: float
          Padding for y labels. If negative, the labels are
          drawn inside the map.
      offset_angle: float
          Difference of angle in degrees from 90 to define when
          a label must be flipped to be more readable.
          For example, a value of 10 makes a vertical top label to be
          flipped only at 100 degrees.
      auto_update: bool
          Whether to redraw the gridlines and labels when the figure is
          updated.
      formatter_kwargs: dict, optional
          Options passed to the default formatters.
          See :class:`~cartopy.mpl.ticker.LongitudeFormatter` and
          :class:`~cartopy.mpl.ticker.LatitudeFormatter`
  
      Notes
      -----
      The "x" and "y" labels for locators and formatters do not necessarily
      correspond to X and Y, but to the first and second coordinates of the
      specified CRS. For the common case of PlateCarree gridlines, these
      correspond to longitudes and latitudes. Depending on the projection
      used for the map, meridians and parallels can cross both the X axis and
      the Y axis.
      """
      super().__init__()
  
      # We do not want the labels clipped to axes.
      self.set_clip_on(False)
      # Backcompat: the LineCollection was previously added directly to the
      # axes, having a default zorder of 2.
      self.set_zorder(2)
  
      #: The :class:`~matplotlib.ticker.Locator` to use for the x
      #: gridlines and labels.
      if xlocator is not None:
          if not isinstance(xlocator, mticker.Locator):
              xlocator = mticker.FixedLocator(xlocator)
          self.xlocator = xlocator
      elif isinstance(crs, PlateCarree):
          self.xlocator = LongitudeLocator(dms=dms)
      else:
          self.xlocator = classic_locator
  
      #: The :class:`~matplotlib.ticker.Locator` to use for the y
      #: gridlines and labels.
      if ylocator is not None:
          if not isinstance(ylocator, mticker.Locator):
              ylocator = mticker.FixedLocator(ylocator)
          self.ylocator = ylocator
      elif isinstance(crs, PlateCarree):
          self.ylocator = LatitudeLocator(dms=dms)
      else:
          self.ylocator = classic_locator
  
      formatter_kwargs = {
          **(formatter_kwargs or {}),
          "dms": dms,
      }
  
      if xformatter is None:
          if isinstance(crs, PlateCarree):
              xformatter = LongitudeFormatter(**formatter_kwargs)
          else:
              xformatter = classic_formatter()
      #: The :class:`~matplotlib.ticker.Formatter` to use for the lon labels.
      self.xformatter = xformatter
  
      if yformatter is None:
          if isinstance(crs, PlateCarree):
              yformatter = LatitudeFormatter(**formatter_kwargs)
          else:
              yformatter = classic_formatter()
      #: The :class:`~matplotlib.ticker.Formatter` to use for the lat labels.
      self.yformatter = yformatter
  
      # Draw label argument
      if isinstance(draw_labels, list):
          # Select to which coordinate it is applied
          if "x" not in draw_labels and "y" not in draw_labels:
              value = True
          elif "x" in draw_labels and "y" in draw_labels:
              value = ["x", "y"]
          elif "x" in draw_labels:
              value = "x"
          else:
              value = "y"
  
          #: Whether to draw labels on the top of the map.
          self.top_labels = value if "top" in draw_labels else False
  
          #: Whether to draw labels on the bottom of the map.
          self.bottom_labels = (
              value if "bottom" in draw_labels else False
          )
  
          #: Whether to draw labels on the left hand side of the map.
          self.left_labels = value if "left" in draw_labels else False
  
          #: Whether to draw labels on the right hand side of the map.
          self.right_labels = value if "right" in draw_labels else False
  
          #: Whether to draw labels near the geographic limits of the map.
          self.geo_labels = value if "geo" in draw_labels else False
  
      elif isinstance(draw_labels, dict):
          self.top_labels = draw_labels.get("top", False)
          self.bottom_labels = draw_labels.get("bottom", False)
          self.left_labels = draw_labels.get("left", False)
          self.right_labels = draw_labels.get("right", False)
          self.geo_labels = draw_labels.get("geo", False)
  
      else:
          self.top_labels = draw_labels
          self.bottom_labels = draw_labels
          self.left_labels = draw_labels
          self.right_labels = draw_labels
          self.geo_labels = draw_labels
  
      for loc in "top", "bottom", "left", "right", "geo":
          value = getattr(self, f"{loc}_labels")
          if isinstance(value, str):
              value = value.lower()
          if not isinstance(value, (list, bool)) and value not in (
              "x",
              "y",
          ):
              raise ValueError(f"Invalid draw_labels argument: {value}")
  
      if auto_inline:
          if isinstance(axes.projection, _X_INLINE_PROJS):
              self.x_inline = True
              self.y_inline = False
          elif isinstance(axes.projection, _POLAR_PROJS):
              self.x_inline = False
              self.y_inline = True
          else:
              self.x_inline = False
              self.y_inline = False
  
      # overwrite auto_inline if necessary
      if x_inline is not None:
          #: Whether to draw x labels inline
          self.x_inline = x_inline
      elif not auto_inline:
          self.x_inline = False
  
      if y_inline is not None:
          #: Whether to draw y labels inline
          self.y_inline = y_inline
      elif not auto_inline:
          self.y_inline = False
  
      # Apply inline args
      if not draw_labels:
          self.inline_labels = False
      elif self.x_inline and self.y_inline:
          self.inline_labels = True
      elif self.x_inline:
          self.inline_labels = "x"
      elif self.y_inline:
          self.inline_labels = "y"
      else:
          self.inline_labels = False
  
      # Gridline limits so that the gridlines don't extend all the way
      # to the edge of the boundary
      self.xlim = xlim
      self.ylim = ylim
  
      #: Whether to draw the x gridlines.
      self.xlines = True
  
      #: Whether to draw the y gridlines.
      self.ylines = True
  
      #: A dictionary passed through to ``ax.text`` on x label creation
      #: for styling of the text labels.
      self.xlabel_style = xlabel_style or {}
  
      #: A dictionary passed through to ``ax.text`` on y label creation
      #: for styling of the text labels.
      self.ylabel_style = ylabel_style or {}
  
      #: bbox style for grid labels
      self.labels_bbox_style = labels_bbox_style or {
          "pad": 0,
          "visible": False,
      }
  
      #: The padding from the map edge to the x labels in points.
      self.xpadding = xpadding
  
      #: The padding from the map edge to the y labels in points.
      self.ypadding = ypadding
  
      #: Control the rotation of labels.
      if rotate_labels is None:
          rotate_labels = (
              axes.projection.__class__ in _ROTATE_LABEL_PROJS
          )
      if not isinstance(rotate_labels, (bool, float, int)):
          raise ValueError("Invalid rotate_labels argument")
      self.rotate_labels = rotate_labels
  
      self.offset_angle = offset_angle
  
      # Current transform
      self.crs = crs
  
      # if the user specifies tick labels at this point, check if they can
      # be drawn. The same check will take place at draw time in case
      # public attributes are changed after instantiation.
      if draw_labels and not (x_inline or y_inline or auto_inline):
          self._assert_can_draw_ticks()
  
      #: The number of interpolation points which are used to draw the
      #: gridlines.
      self.n_steps = 100
  
      #: A dictionary passed through to
      #: ``matplotlib.collections.LineCollection`` on grid line creation.
      self.collection_kwargs = collection_kwargs
  
      #: The x gridlines which were created at draw time.
      self.xline_artists = []
  
      #: The y gridlines which were created at draw time.
      self.yline_artists = []
  
      # List of all labels (Label objects)
      self._all_labels = []
  
      # List of active labels (used in current draw)
      self._labels = []
  
      # Draw status
      self._drawn = False
      self._auto_update = auto_update
  
imports: import itertools
  import operator
  import warnings
  import matplotlib
  import matplotlib.artist
  import matplotlib.collections
  import matplotlib.text
  import matplotlib.ticker
  import matplotlib.transforms
  import numpy
  import shapely.geometry
  import cartopy
  from cartopy.crs import PlateCarree, Projection, _RectangularProjection
  from cartopy.mpl.ticker import LatitudeFormatter, LatitudeLocator, LongitudeFormatter, LongitudeLocator
label_desc: Use image node_id 1 to create a new Gridliner object from inherited base classes: matplotlib.artist with example: obj = Gridliner(axes, crs, draw_labels, xlocator, ylocator, collection_kwargs, xformatter, yformatter, dms, x_inline, y_inline, auto_inline, xlim, ylim, rotate_labels, xlabel_style, ylabel_style, labels_bbox_style, xpadding, ypadding, offset_angle, auto_update, formatter_kwargs)