code: def _explore(
      df,
      column=None,
      cmap=None,
      color=None,
      m=None,
      tiles="OpenStreetMap",
      attr=None,
      tooltip=True,
      popup=False,
      highlight=True,
      categorical=False,
      legend=True,
      scheme=None,
      k=5,
      vmin=None,
      vmax=None,
      width="100%",
      height="100%",
      categories=None,
      classification_kwds=None,
      control_scale=True,
      marker_type=None,
      marker_kwds={},
      style_kwds={},
      highlight_kwds={},
      missing_kwds={},
      tooltip_kwds={},
      popup_kwds={},
      legend_kwds={},
      map_kwds={},
      **kwargs,
  ):
      """Interactive map based on GeoPandas and folium/leaflet.js
  
      Generate an interactive leaflet map based on :class:`~geopandas.GeoDataFrame`
  
      Parameters
      ----------
      column : str, np.array, pd.Series (default None)
          The name of the dataframe column, :class:`numpy.array`,
          or :class:`pandas.Series` to be plotted. If :class:`numpy.array` or
          :class:`pandas.Series` are used then it must have same length as dataframe.
      cmap : str, matplotlib.Colormap, branca.colormap or function (default None)
          The name of a colormap recognized by ``matplotlib``, a list-like of colors,
          :class:`matplotlib.colors.Colormap`, a :class:`branca.colormap.ColorMap` or
          function that returns a named color or hex based on the column
          value, e.g.::
  
              def my_colormap(value):  # scalar value defined in 'column'
                  if value > 1:
                      return "green"
                  return "red"
  
      color : str, array-like (default None)
          Named color or a list-like of colors (named or hex).
      m : folium.Map (default None)
          Existing map instance on which to draw the plot.
      tiles : str, xyzservices.TileProvider (default 'OpenStreetMap Mapnik')
          Map tileset to use. Can choose from the list supported by folium, query a
          :class:`xyzservices.TileProvider` by a name from ``xyzservices.providers``,
          pass :class:`xyzservices.TileProvider` object or pass custom XYZ URL.
          The current list of built-in providers (when ``xyzservices`` is not available):
  
          ``["OpenStreetMap", "CartoDB positron", “CartoDB dark_matter"]``
  
          You can pass a custom tileset to Folium by passing a Leaflet-style URL
          to the tiles parameter: ``http://{s}.yourtiles.com/{z}/{x}/{y}.png``.
          Be sure to check their terms and conditions and to provide attribution with
          the ``attr`` keyword.
      attr : str (default None)
          Map tile attribution; only required if passing custom tile URL.
      tooltip : bool, str, int, list (default True)
          Display GeoDataFrame attributes when hovering over the object.
          ``True`` includes all columns. ``False`` removes tooltip. Pass string or list of
          strings to specify a column(s). Integer specifies first n columns to be
          included. Defaults to ``True``.
      popup : bool, str, int, list (default False)
          Input GeoDataFrame attributes for object displayed when clicking.
          ``True`` includes all columns. ``False`` removes popup. Pass string or list of
          strings to specify a column(s). Integer specifies first n columns to be
          included. Defaults to ``False``.
      highlight : bool (default True)
          Enable highlight functionality when hovering over a geometry.
      categorical : bool (default False)
          If ``False``, ``cmap`` will reflect numerical values of the
          column being plotted. For non-numerical columns, this
          will be set to True.
      legend : bool (default True)
          Plot a legend in choropleth plots.
          Ignored if no ``column`` is given.
      scheme : str (default None)
          Name of a choropleth classification scheme (requires ``mapclassify`` >= 2.4.0).
          A :func:`mapclassify.classify` will be used
          under the hood. Supported are all schemes provided by ``mapclassify`` (e.g.
          ``'BoxPlot'``, ``'EqualInterval'``, ``'FisherJenks'``, ``'FisherJenksSampled'``,
          ``'HeadTailBreaks'``, ``'JenksCaspall'``, ``'JenksCaspallForced'``,
          ``'JenksCaspallSampled'``, ``'MaxP'``, ``'MaximumBreaks'``,
          ``'NaturalBreaks'``, ``'Quantiles'``, ``'Percentiles'``, ``'StdMean'``,
          ``'UserDefined'``). Arguments can be passed in ``classification_kwds``.
      k : int (default 5)
          Number of classes
      vmin : None or float (default None)
          Minimum value of ``cmap``. If ``None``, the minimum data value
          in the column to be plotted is used.
      vmax : None or float (default None)
          Maximum value of ``cmap``. If ``None``, the maximum data value
          in the column to be plotted is used.
      width : pixel int or percentage string (default: '100%')
          Width of the folium :class:`~folium.folium.Map`. If the argument
          m is given explicitly, width is ignored.
      height : pixel int or percentage string (default: '100%')
          Height of the folium :class:`~folium.folium.Map`. If the argument
          m is given explicitly, height is ignored.
      categories : list-like
          Ordered list-like object of categories to be used for categorical plot.
      classification_kwds : dict (default None)
          Keyword arguments to pass to mapclassify
      control_scale : bool, (default True)
          Whether to add a control scale on the map.
      marker_type : str, folium.Circle, folium.CircleMarker, folium.Marker (default None)
          Allowed string options are ('marker', 'circle', 'circle_marker'). Defaults to
          folium.CircleMarker.
      marker_kwds: dict (default {})
          Additional keywords to be passed to the selected ``marker_type``, e.g.:
  
          radius : float (default 2 for ``circle_marker`` and 50 for ``circle``))
              Radius of the circle, in meters (for ``circle``) or pixels
              (for ``circle_marker``).
          fill : bool (default True)
              Whether to fill the ``circle`` or ``circle_marker`` with color.
          icon : folium.map.Icon
              the :class:`folium.map.Icon` object to use to render the marker.
          draggable : bool (default False)
              Set to True to be able to drag the marker around the map.
  
      style_kwds : dict (default {})
          Additional style to be passed to folium ``style_function``:
  
          stroke : bool (default True)
              Whether to draw stroke along the path. Set it to ``False`` to
              disable borders on polygons or circles.
          color : str
              Stroke color
          weight : int
              Stroke width in pixels
          opacity : float (default 1.0)
              Stroke opacity
          fill : boolean (default True)
              Whether to fill the path with color. Set it to ``False`` to
              disable filling on polygons or circles.
          fillColor : str
              Fill color. Defaults to the value of the color option
          fillOpacity : float (default 0.5)
              Fill opacity.
          style_function : callable
              Function mapping a GeoJson Feature to a style ``dict``.
  
              * Style properties :func:`folium.vector_layers.path_options`
              * GeoJson features :class:`GeoDataFrame.__geo_interface__`
  
              e.g.::
  
                  lambda x: {"color":"red" if x["properties"]["gdp_md_est"]<10**6
                                               else "blue"}
  
          Plus all supported by :func:`folium.vector_layers.path_options`. See the
          documentation of :class:`folium.features.GeoJson` for details.
  
      highlight_kwds : dict (default {})
          Style to be passed to folium highlight_function. Uses the same keywords
          as ``style_kwds``. When empty, defaults to ``{"fillOpacity": 0.75}``.
      tooltip_kwds : dict (default {})
          Additional keywords to be passed to :class:`folium.features.GeoJsonTooltip`,
          e.g. ``aliases``, ``labels``, or ``sticky``.
      popup_kwds : dict (default {})
          Additional keywords to be passed to :class:`folium.features.GeoJsonPopup`,
          e.g. ``aliases`` or ``labels``.
      legend_kwds : dict (default {})
          Additional keywords to be passed to the legend.
  
          Currently supported customisation:
  
          caption : string
              Custom caption of the legend. Defaults to the column name.
  
          Additional accepted keywords when ``scheme`` is specified:
  
          colorbar : bool (default True)
              An option to control the style of the legend. If True, continuous
              colorbar will be used. If False, categorical legend will be used for bins.
          scale : bool (default True)
              Scale bins along the colorbar axis according to the bin edges (True)
              or use the equal length for each bin (False)
          fmt : string (default "{:.2f}")
              A formatting specification for the bin edges of the classes in the
              legend. For example, to have no decimals: ``{"fmt": "{:.0f}"}``. Applies
              if ``colorbar=False``.
          labels : list-like
              A list of legend labels to override the auto-generated labels.
              Needs to have the same number of elements as the number of
              classes (`k`). Applies if ``colorbar=False``.
          interval : boolean (default False)
              An option to control brackets from mapclassify legend.
              If True, open/closed interval brackets are shown in the legend.
              Applies if ``colorbar=False``.
          max_labels : int, default 10
              Maximum number of colorbar tick labels (requires branca>=0.5.0)
      map_kwds : dict (default {})
          Additional keywords to be passed to folium :class:`~folium.folium.Map`,
          e.g. ``dragging``, or ``scrollWheelZoom``.
  
  
      **kwargs : dict
          Additional options to be passed on to the folium object.
  
      Returns
      -------
      m : folium.folium.Map
          folium :class:`~folium.folium.Map` instance
  
      Examples
      --------
      >>> import geodatasets
      >>> df = geopandas.read_file(
      ...     geodatasets.get_path("geoda.chicago_health")
      ... )
      >>> df.head(2)  # doctest: +SKIP
         ComAreaID  ...                                           geometry
      0         35  ...  POLYGON ((-87.60914 41.84469, -87.60915 41.844...
      1         36  ...  POLYGON ((-87.59215 41.81693, -87.59231 41.816...
  
      [2 rows x 87 columns]
  
      >>> df.explore("Pop2012", cmap="Blues")  # doctest: +SKIP
      """
  
      def _colormap_helper(_cmap, n_resample=None, idx=None):
          """Helper for MPL deprecation - GH#2596"""
          if not n_resample:
              return cm.get_cmap(_cmap)
          else:
              if MPL_361:
                  return cm.get_cmap(_cmap).resampled(n_resample)(idx)
              else:
                  return cm.get_cmap(_cmap, n_resample)(idx)
  
      try:
          import branca as bc
          import folium
          import re
          import matplotlib
          from matplotlib import colors
          import matplotlib.pyplot as plt
          from mapclassify import classify
  
          # isolate MPL version - GH#2596
          MPL_361 = Version(matplotlib.__version__) >= Version("3.6.1")
          if MPL_361:
              from matplotlib import colormaps as cm
          else:
              from matplotlib import cm
  
      except (ImportError, ModuleNotFoundError):
          raise ImportError(
              "The 'folium', 'matplotlib' and 'mapclassify' packages are required for "
              "'explore()'. You can install them using "
              "'conda install -c conda-forge folium matplotlib mapclassify' "
              "or 'pip install folium matplotlib mapclassify'."
          )
  
      # xyservices is an optional dependency
      try:
          import xyzservices
  
          HAS_XYZSERVICES = True
      except (ImportError, ModuleNotFoundError):
          HAS_XYZSERVICES = False
  
      gdf = df.copy()
  
      # convert LinearRing to LineString
      rings_mask = df.geom_type == "LinearRing"
      if rings_mask.any():
          gdf.geometry[rings_mask] = gdf.geometry[rings_mask].apply(
              lambda g: LineString(g)
          )
  
      if gdf.crs is None:
          kwargs["crs"] = "Simple"
          tiles = None
      elif not gdf.crs.equals(4326):
          gdf = gdf.to_crs(4326)
  
      # create folium.Map object
      if m is None:
          # Get bounds to specify location and map extent
          bounds = gdf.total_bounds
          location = kwargs.pop("location", None)
          if location is None:
              x = mean([bounds[0], bounds[2]])
              y = mean([bounds[1], bounds[3]])
              location = (y, x)
              if "zoom_start" in kwargs.keys():
                  fit = False
              else:
                  fit = True
          else:
              fit = False
  
          # get a subset of kwargs to be passed to folium.Map
          for i in _MAP_KWARGS:
              if i in map_kwds:
                  raise ValueError(
                      f"'{i}' cannot be specified in 'map_kwds'. "
                      f"Use the '{i}={map_kwds[i]}' argument instead."
                  )
          map_kwds = {
              **map_kwds,
              **{
                  i: kwargs[i]
                  for i in kwargs.keys()
                  if i in _MAP_KWARGS
              },
          }
  
          if HAS_XYZSERVICES:
              # match provider name string to xyzservices.TileProvider
              if isinstance(tiles, str):
                  try:
                      tiles = xyzservices.providers.query_name(tiles)
                  except ValueError:
                      pass
  
              if isinstance(tiles, xyzservices.TileProvider):
                  attr = attr if attr else tiles.html_attribution
                  if "min_zoom" not in map_kwds:
                      map_kwds["min_zoom"] = tiles.get("min_zoom", 0)
                  if "max_zoom" not in map_kwds:
                      map_kwds["max_zoom"] = tiles.get("max_zoom", 18)
                  tiles = tiles.build_url(scale_factor="{r}")
  
          m = folium.Map(
              location=location,
              control_scale=control_scale,
              tiles=tiles,
              attr=attr,
              width=width,
              height=height,
              **map_kwds,
          )
  
          # fit bounds to get a proper zoom level
          if fit:
              m.fit_bounds(
                  [[bounds[1], bounds[0]], [bounds[3], bounds[2]]]
              )
  
      for map_kwd in _MAP_KWARGS:
          kwargs.pop(map_kwd, None)
  
      nan_idx = None
  
      if column is not None:
          if pd.api.types.is_list_like(column):
              if len(column) != gdf.shape[0]:
                  raise ValueError(
                      "The GeoDataFrame and given column have different number of rows."
                  )
              else:
                  column_name = "__plottable_column"
                  gdf[column_name] = column
                  column = column_name
          elif isinstance(gdf[column].dtype, pd.CategoricalDtype):
              if categories is not None:
                  raise ValueError(
                      "Cannot specify 'categories' when column has categorical dtype"
                  )
              categorical = True
          elif (
              pd.api.types.is_object_dtype(gdf[column])
              or pd.api.types.is_bool_dtype(gdf[column])
              or pd.api.types.is_string_dtype(gdf[column])
              or categories
          ):
              categorical = True
  
          nan_idx = pd.isna(gdf[column])
  
          if categorical:
              cat = pd.Categorical(
                  gdf[column][~nan_idx], categories=categories
              )
              N = len(cat.categories)
              cmap = cmap if cmap else "tab20"
  
              # colormap exists in matplotlib
              if cmap in plt.colormaps():
                  color = np.apply_along_axis(
                      colors.to_hex,
                      1,
                      _colormap_helper(
                          cmap, n_resample=N, idx=cat.codes
                      ),
                  )
                  legend_colors = np.apply_along_axis(
                      colors.to_hex,
                      1,
                      _colormap_helper(
                          cmap, n_resample=N, idx=range(N)
                      ),
                  )
  
              # colormap is matplotlib.Colormap
              elif isinstance(cmap, colors.Colormap):
                  color = np.apply_along_axis(
                      colors.to_hex, 1, cmap(cat.codes)
                  )
                  legend_colors = np.apply_along_axis(
                      colors.to_hex, 1, cmap(range(N))
                  )
  
              # custom list of colors
              elif pd.api.types.is_list_like(cmap):
                  if N > len(cmap):
                      cmap = cmap * (N // len(cmap) + 1)
                  color = np.take(cmap, cat.codes)
                  legend_colors = np.take(cmap, range(N))
  
              else:
                  raise ValueError(
                      "'cmap' is invalid. For categorical plots, pass either valid "
                      "named matplotlib colormap or a list-like of colors."
                  )
  
          elif callable(cmap):
              # List of colors based on Branca colormaps or self-defined functions
              color = [cmap(x) for x in df[column]]
  
          else:
              vmin = gdf[column].min() if vmin is None else vmin
              vmax = gdf[column].max() if vmax is None else vmax
  
              # get bins
              if scheme is not None:
                  if classification_kwds is None:
                      classification_kwds = {}
                  if "k" not in classification_kwds:
                      classification_kwds["k"] = k
  
                  binning = classify(
                      np.asarray(gdf[column][~nan_idx]),
                      scheme,
                      **classification_kwds,
                  )
                  color = np.apply_along_axis(
                      colors.to_hex,
                      1,
                      _colormap_helper(
                          cmap, n_resample=binning.k, idx=binning.yb
                      ),
                  )
  
              else:
                  bins = np.linspace(vmin, vmax, 257)[1:]
                  binning = classify(
                      np.asarray(gdf[column][~nan_idx]),
                      "UserDefined",
                      bins=bins,
                  )
  
                  color = np.apply_along_axis(
                      colors.to_hex,
                      1,
                      _colormap_helper(
                          cmap, n_resample=256, idx=binning.yb
                      ),
                  )
  
      # set default style
      if "fillOpacity" not in style_kwds:
          style_kwds["fillOpacity"] = 0.5
      if "weight" not in style_kwds:
          style_kwds["weight"] = 2
      if "style_function" in style_kwds:
          style_kwds_function = style_kwds["style_function"]
          if not callable(style_kwds_function):
              raise ValueError("'style_function' has to be a callable")
          style_kwds.pop("style_function")
      else:
  
          def _no_style(x):
              return {}
  
          style_kwds_function = _no_style
  
      # specify color
      if color is not None:
          if (
              isinstance(color, str)
              and isinstance(gdf, geopandas.GeoDataFrame)
              and color in gdf.columns
          ):  # use existing column
  
              def _style_color(x):
                  base_style = {
                      "fillColor": x["properties"][color],
                      **style_kwds,
                  }
                  return {
                      **base_style,
                      **style_kwds_function(x),
                  }
  
              style_function = _style_color
          else:  # assign new column
              if isinstance(gdf, geopandas.GeoSeries):
                  gdf = geopandas.GeoDataFrame(geometry=gdf)
  
              if nan_idx is not None and nan_idx.any():
                  nan_color = missing_kwds.pop("color", None)
  
                  gdf["__folium_color"] = nan_color
                  gdf.loc[~nan_idx, "__folium_color"] = color
              else:
                  gdf["__folium_color"] = color
  
              stroke_color = style_kwds.pop("color", None)
              if not stroke_color:
  
                  def _style_column(x):
                      base_style = {
                          "fillColor": x["properties"][
                              "__folium_color"
                          ],
                          "color": x["properties"]["__folium_color"],
                          **style_kwds,
                      }
                      return {
                          **base_style,
                          **style_kwds_function(x),
                      }
  
                  style_function = _style_column
              else:
  
                  def _style_stroke(x):
                      base_style = {
                          "fillColor": x["properties"][
                              "__folium_color"
                          ],
                          "color": stroke_color,
                          **style_kwds,
                      }
                      return {
                          **base_style,
                          **style_kwds_function(x),
                      }
  
                  style_function = _style_stroke
      else:  # use folium default
  
          def _style_default(x):
              return {**style_kwds, **style_kwds_function(x)}
  
          style_function = _style_default
  
      if highlight:
          if "fillOpacity" not in highlight_kwds:
              highlight_kwds["fillOpacity"] = 0.75
  
          def _style_highlight(x):
              return {**highlight_kwds}
  
          highlight_function = _style_highlight
      else:
          highlight_function = None
  
      # define default for points
      if marker_type is None:
          marker_type = "circle_marker"
  
      marker = marker_type
      if isinstance(marker_type, str):
          if marker_type == "marker":
              marker = folium.Marker(**marker_kwds)
          elif marker_type == "circle":
              marker = folium.Circle(**marker_kwds)
          elif marker_type == "circle_marker":
              marker_kwds["radius"] = marker_kwds.get("radius", 2)
              marker_kwds["fill"] = marker_kwds.get("fill", True)
              marker = folium.CircleMarker(**marker_kwds)
          else:
              raise ValueError(
                  "Only 'marker', 'circle', and 'circle_marker' are "
                  "supported as marker values"
              )
  
      # remove additional geometries
      if isinstance(gdf, geopandas.GeoDataFrame):
          non_active_geoms = [
              name
              for name, val in (gdf.dtypes == "geometry").items()
              if val and name != gdf.geometry.name
          ]
          gdf = gdf.drop(columns=non_active_geoms)
  
      # prepare tooltip and popup
      if isinstance(gdf, geopandas.GeoDataFrame):
          # add named index to the tooltip
          if gdf.index.name is not None:
              gdf = gdf.reset_index()
          # specify fields to show in the tooltip
          tooltip = _tooltip_popup(
              "tooltip", tooltip, gdf, **tooltip_kwds
          )
          popup = _tooltip_popup("popup", popup, gdf, **popup_kwds)
      else:
          tooltip = None
          popup = None
      # escape the curly braces {{}} for jinja2 templates
      feature_collection = gdf[
          ~(
              gdf.geometry.isna() | gdf.geometry.is_empty
          )  # drop missing or empty geoms
      ].__geo_interface__
      for feature in feature_collection["features"]:
          for k in feature["properties"]:
              # escape the curly braces in values
              if type(feature["properties"][k]) == str:
                  feature["properties"][k] = re.sub(
                      r"\{{2,}",
                      lambda x: "{% raw %}"
                      + x.group(0)
                      + "{% endraw %}",
                      feature["properties"][k],
                  )
  
      # add dataframe to map
      folium.GeoJson(
          feature_collection,
          tooltip=tooltip,
          popup=popup,
          marker=marker,
          style_function=style_function,
          highlight_function=highlight_function,
          **kwargs,
      ).add_to(m)
  
      if legend:
          # NOTE: overlaps will be resolved in branca #88
          caption = column if not column == "__plottable_column" else ""
          caption = legend_kwds.pop("caption", caption)
          if categorical:
              categories = cat.categories.to_list()
              legend_colors = legend_colors.tolist()
  
              if nan_idx.any() and nan_color:
                  categories.append(missing_kwds.pop("label", "NaN"))
                  legend_colors.append(nan_color)
  
              _categorical_legend(m, caption, categories, legend_colors)
          elif column is not None:
              cbar = legend_kwds.pop("colorbar", True)
              colormap_kwds = {}
              if "max_labels" in legend_kwds:
                  colormap_kwds["max_labels"] = legend_kwds.pop(
                      "max_labels"
                  )
              if scheme:
                  cb_colors = np.apply_along_axis(
                      colors.to_hex,
                      1,
                      _colormap_helper(
                          cmap,
                          n_resample=binning.k,
                          idx=range(binning.k),
                      ),
                  )
                  if cbar:
                      if legend_kwds.pop("scale", True):
                          index = [vmin] + binning.bins.tolist()
                      else:
                          index = None
                      colorbar = bc.colormap.StepColormap(
                          cb_colors,
                          vmin=vmin,
                          vmax=vmax,
                          caption=caption,
                          index=index,
                          **colormap_kwds,
                      )
                  else:
                      fmt = legend_kwds.pop("fmt", "{:.2f}")
                      if "labels" in legend_kwds:
                          categories = legend_kwds["labels"]
                      else:
                          categories = binning.get_legend_classes(fmt)
                          show_interval = legend_kwds.pop(
                              "interval", False
                          )
                          if not show_interval:
                              categories = [c[1:-1] for c in categories]
  
                      if nan_idx.any() and nan_color:
                          categories.append(
                              missing_kwds.pop("label", "NaN")
                          )
                          cb_colors = np.append(cb_colors, nan_color)
                      _categorical_legend(
                          m, caption, categories, cb_colors
                      )
  
              else:
                  if isinstance(cmap, bc.colormap.ColorMap):
                      colorbar = cmap
                  else:
                      mp_cmap = _colormap_helper(cmap)
                      cb_colors = np.apply_along_axis(
                          colors.to_hex, 1, mp_cmap(range(mp_cmap.N))
                      )
  
                      # linear legend
                      if mp_cmap.N > 20:
                          colorbar = bc.colormap.LinearColormap(
                              cb_colors,
                              vmin=vmin,
                              vmax=vmax,
                              caption=caption,
                              **colormap_kwds,
                          )
  
                      # steps
                      else:
                          colorbar = bc.colormap.StepColormap(
                              cb_colors,
                              vmin=vmin,
                              vmax=vmax,
                              caption=caption,
                              **colormap_kwds,
                          )
  
              if cbar:
                  if nan_idx.any() and nan_color:
                      _categorical_legend(
                          m,
                          "",
                          [missing_kwds.pop("label", "NaN")],
                          [nan_color],
                      )
                  m.add_child(colorbar)
  
      return m
  
file_path: geopandas/geopandas/explore.py
imports: from statistics import mean
  import geopandas
  from shapely.geometry import LineString
  import numpy
  import pandas
  from packaging.version import Version
label_desc: Use image node_id 1 for calling a global function with example usage: _explore(df, column, cmap, color, m, tiles, attr, tooltip, popup, highlight, categorical, legend, scheme, k, vmin, vmax, width, height, categories, classification_kwds, control_scale, marker_type, marker_kwds, style_kwds, highlight_kwds, missing_kwds, tooltip_kwds, popup_kwds, legend_kwds, map_kwds) and returns: m, cm, dict, dict, dict, cm, cm, dict, dict, dict