def voronoi(df, projection=None, edgecolor='black',
            clip=None,
            hue=None, scheme=None, k=5, cmap='viridis', categorical=False, vmin=None, vmax=None,
            legend=False, legend_kwargs=None, legend_labels=None,
            extent=None, figsize=(8, 6), ax=None,
            **kwargs):
    """
    Geospatial Voronoi diagram.

    Parameters
    ----------
    df : GeoDataFrame
        The data being plotted.
    projection : geoplot.crs object instance, optional
        A geographic projection. For more information refer to `the tutorial page on projections
        <https://nbviewer.jupyter.org/github/ResidentMario/geoplot/blob/master/notebooks/tutorials/Projections.ipynb>`_.
    hue : None, Series, GeoSeries, iterable, or str, optional
        Applies a colormap to the output points.
    categorical : boolean, optional
        Set to ``True`` if ``hue`` references a categorical variable, and ``False`` (the default) otherwise. Ignored
        if ``hue`` is left unspecified.
    scheme : None or {"quantiles"|"equal_interval"|"fisher_jenks"}, optional
        Controls how the colormap bin edges are determined. Ignored if ``hue`` is left unspecified.
    k : int or None, optional
        Ignored if ``hue`` is left unspecified. Otherwise, if ``categorical`` is False, controls how many colors to
        use (5 is the default). If set to ``None``, a continuous colormap will be used.
    cmap : matplotlib color, optional
        The `matplotlib colormap <http://matplotlib.org/examples/color/colormaps_reference.html>`_ to be used.
        Ignored if ``hue`` is left unspecified.
    vmin : float, optional
        Values below this level will be colored the same threshold value. Defaults to the dataset minimum. Ignored
        if ``hue`` is left unspecified.
    vmax : float, optional
        Values above this level will be colored the same threshold value. Defaults to the dataset maximum. Ignored
        if ``hue`` is left unspecified.
    legend : boolean, optional
        Whether or not to include a legend. Ignored if neither a ``hue`` nor a ``scale`` is specified.
    legend_values : list, optional
        The values to use in the legend. Defaults to equal intervals. For more information see `the Gallery demo
        <https://residentmario.github.io/geoplot/examples/largest-cities-usa.html>`_.
    legend_labels : list, optional
        The names to use in the legend. Defaults to the variable values. For more information see `the Gallery demo
        <https://residentmario.github.io/geoplot/examples/largest-cities-usa.html>`_.
    legend_kwargs : dict, optional
        Keyword arguments to be passed to `the underlying legend <http://matplotlib.org/users/legend_guide.html>`_.
    extent : None or (minx, maxx, miny, maxy), optional
        Used to control plot x-axis and y-axis limits manually.
    figsize : tuple, optional
        An (x, y) tuple passed to ``matplotlib.figure`` which sets the size, in inches, of the resultant plot.
    ax : AxesSubplot or GeoAxesSubplot instance, optional
        A ``matplotlib.axes.AxesSubplot`` or ``cartopy.mpl.geoaxes.GeoAxesSubplot`` instance. Defaults to a new axis.
    kwargs: dict, optional
        Keyword arguments to be passed to the underlying ``matplotlib`` `Line2D objects
        <http://matplotlib.org/api/lines_api.html#matplotlib.lines.Line2D>`_.

    Returns
    -------
    AxesSubplot or GeoAxesSubplot instance
        The axis object with the plot on it.

    Examples
    --------

    The neighborhood closest to a point in space is known as its `Voronoi region
    <https://en.wikipedia.org/wiki/Voronoi_diagram>`_. Every point in a dataset has a Voronoi region, which may be
    either a closed polygon (for inliers) or open infinite region (for points on the edge of the distribution). A
    Voronoi diagram works by dividing a space filled with points into such regions and plotting the result. Voronoi
    plots allow efficient assessmelt of the *density* of points in different spaces, and when combined with a
    colormap can be quite informative of overall trends in the dataset.

    The ``geoplot`` ``voronoi`` is a spatially aware application of this technique. It compares well with the more
    well-known ``choropleth``, which has the advantage of using meaningful regions, but the disadvantage of having
    defined those regions beforehand. ``voronoi`` has fewer requirements and may perform better when the number of
    observations is small. Compare also with the quadtree technique available in ``aggplot``.

    A basic ``voronoi`` specified data and, optionally, a projection. We overlay geometry to aid interpretability.

    .. code-block:: python

        ax = gplt.voronoi(injurious_collisions.head(1000))
        gplt.polyplot(boroughs, ax=ax)

    .. image:: ../figures/voronoi/voronoi-simple.png

    ``hue`` parameterizes the color, and ``cmap`` controls the colormap.

    .. code-block:: python

        ax = gplt.voronoi(injurious_collisions.head(1000), hue='NUMBER OF PERSONS INJURED', cmap='Reds')
        gplt.polyplot(boroughs, ax=ax)

    .. image:: ../figures/voronoi/voronoi-cmap.png

    Add a ``clip`` of iterable geometries to trim the ``voronoi`` against local geography.

    .. code-block:: python

        ax = gplt.voronoi(injurious_collisions.head(1000), hue='NUMBER OF PERSONS INJURED', cmap='Reds',
                          clip=boroughs.geometry)
        gplt.polyplot(boroughs, ax=ax)

    .. image:: ../figures/voronoi/voronoi-clip.png

    ``legend`` adds a a ``matplotlib`` `Legend
    <http://matplotlib.org/api/legend_api.html#matplotlib.legend.Legend>`_. This can be tuned even further using the
    ``legend_kwargs`` argument. Other keyword parameters are passed to the underlying ``matplotlib`` `Polygon patches
    <http://matplotlib.org/api/patches_api.html#matplotlib.patches.Polygon>`_.

    .. code-block:: python

        ax = gplt.voronoi(injurious_collisions.head(1000), hue='NUMBER OF PERSONS INJURED', cmap='Reds',
                          clip=boroughs.geometry,
                          legend=True, legend_kwargs={'loc': 'upper left'},
                          linewidth=0.5, edgecolor='white',
                         )
        gplt.polyplot(boroughs, ax=ax)

    .. image:: ../figures/voronoi/voronoi-kwargs.png

    Change the number of bins by specifying an alternative ``k`` value. To use a continuous colormap, explicitly
    specify ``k=None``.  You can change the binning sceme with ``scheme``. The default is ``quantile``, which bins
    observations into classes of different sizes but the same numbers of observations. ``equal_interval`` will
    creates bins that are the same size, but potentially containing different numbers of observations. The more
    complicated ``fisher_jenks`` scheme is an intermediate between the two.

    .. code-block:: python

        ax = gplt.voronoi(injurious_collisions.head(1000),
                          hue='NUMBER OF PERSONS INJURED', cmap='Reds', k=5, scheme='fisher_jenks',
                          clip=boroughs.geometry,
                          legend=True, legend_kwargs={'loc': 'upper left'},
                          linewidth=0.5, edgecolor='white',
                         )
        gplt.polyplot(boroughs, ax=ax)

    .. image:: ../figures/voronoi/voronoi-scheme.png

    If your variable of interest is already `categorical
    <http://pandas.pydata.org/pandas-docs/stable/categorical.html>`_, specify ``categorical=True`` to
    use the labels in your dataset directly.

    .. code-block:: python

        ax = gplt.voronoi(injurious_collisions.head(1000), hue='NUMBER OF PERSONS INJURED', cmap='Reds',
             edgecolor='white', clip=boroughs.geometry,
             linewidth=0.5, categorical=True
             )
        gplt.polyplot(boroughs, linewidth=1, ax=ax)

    .. image:: ../figures/voronoi/voronoi-multiparty.png
    """

    # Initialize the figure.
    fig = _init_figure(ax, figsize)

    if projection:
        # Properly set up the projection.
        projection = projection.load(df, {
            'central_longitude': lambda df: np.mean(np.array([p.x for p in df.geometry.centroid])),
            'central_latitude': lambda df: np.mean(np.array([p.y for p in df.geometry.centroid]))
        })

        # Set up the axis.
        if not ax:
            ax = plt.subplot(111, projection=projection)

    else:
        if not ax:
            ax = plt.gca()

    # Clean up patches.
    _lay_out_axes(ax, projection)

    # Immediately return if input geometry is empty.
    if len(df.geometry) == 0:
        return ax

    # Set extent.
    xs, ys = [p.x for p in df.geometry.centroid], [p.y for p in df.geometry.centroid]
    extrema = np.min(xs), np.max(xs), np.min(ys), np.max(ys)
    _set_extent(ax, projection, extent, extrema)

    # Validate hue input.
    hue = _validate_hue(df, hue)

    # Generate the coloring information, if needed. Follows one of two schemes, categorical or continuous,
    # based on whether or not ``k`` is specified (``hue`` must be specified for either to work).
    if k is not None:
        # Categorical colormap code path.
        categorical, k, scheme = _validate_buckets(categorical, k, scheme)

        if hue is not None:
            cmap, categories, hue_values = _discrete_colorize(categorical, hue, scheme, k, cmap, vmin, vmax)
            colors = [cmap.to_rgba(v) for v in hue_values]

        else:
            colors = ['None']*len(df)

    elif k is None and hue is not None:
        # Continuous colormap code path.
        hue_values = hue
        cmap = _continuous_colormap(hue_values, cmap, vmin, vmax)
        colors = [cmap.to_rgba(v) for v in hue_values]

    elif 'facecolor' in kwargs:
        colors = [kwargs.pop('facecolor')]*len(df)
    else:
        colors = ['None']*len(df)

    # Finally we draw the features.
    geoms = _build_voronoi_polygons(df)
    if projection:
        for color, geom in zip(colors, geoms):
            features = ShapelyFeature([geom], ccrs.PlateCarree())
            ax.add_feature(features, facecolor=color, edgecolor=edgecolor, **kwargs)

        if clip is not None:
            clip_geom = _get_clip(ax.get_extent(crs=ccrs.PlateCarree()), clip)
            feature = ShapelyFeature([clip_geom], ccrs.PlateCarree())
            ax.add_feature(feature, facecolor=(1,1,1), linewidth=0, zorder=100)

    else:
        for color, geom in zip(colors, geoms):
            feature = descartes.PolygonPatch(geom, facecolor=color, edgecolor=edgecolor, **kwargs)
            ax.add_patch(feature)

        if clip is not None:
            clip_geom = _get_clip(ax.get_xlim() + ax.get_ylim(), clip)
            ax = polyplot(gpd.GeoSeries(clip_geom), facecolor='white', linewidth=0, zorder=100,
                          extent=ax.get_xlim() + ax.get_ylim(), ax=ax)

    # Add a legend, if appropriate.
    if legend and k is not None:
        _paint_hue_legend(ax, categories, cmap, legend_labels, legend_kwargs, figure=True)
    elif legend and k is None and hue is not None:
        _paint_colorbar_legend(ax, hue_values, cmap, legend_kwargs)

    return ax
Geospatial Voronoi diagram.

    Parameters
    ----------
    df : GeoDataFrame
        The data being plotted.
    projection : geoplot.crs object instance, optional
        A geographic projection. For more information refer to `the tutorial page on projections
        <https://nbviewer.jupyter.org/github/ResidentMario/geoplot/blob/master/notebooks/tutorials/Projections.ipynb>`_.
    hue : None, Series, GeoSeries, iterable, or str, optional
        Applies a colormap to the output points.
    categorical : boolean, optional
        Set to ``True`` if ``hue`` references a categorical variable, and ``False`` (the default) otherwise. Ignored
        if ``hue`` is left unspecified.
    scheme : None or {"quantiles"|"equal_interval"|"fisher_jenks"}, optional
        Controls how the colormap bin edges are determined. Ignored if ``hue`` is left unspecified.
    k : int or None, optional
        Ignored if ``hue`` is left unspecified. Otherwise, if ``categorical`` is False, controls how many colors to
        use (5 is the default). If set to ``None``, a continuous colormap will be used.
    cmap : matplotlib color, optional
        The `matplotlib colormap <http://matplotlib.org/examples/color/colormaps_reference.html>`_ to be used.
        Ignored if ``hue`` is left unspecified.
    vmin : float, optional
        Values below this level will be colored the same threshold value. Defaults to the dataset minimum. Ignored
        if ``hue`` is left unspecified.
    vmax : float, optional
        Values above this level will be colored the same threshold value. Defaults to the dataset maximum. Ignored
        if ``hue`` is left unspecified.
    legend : boolean, optional
        Whether or not to include a legend. Ignored if neither a ``hue`` nor a ``scale`` is specified.
    legend_values : list, optional
        The values to use in the legend. Defaults to equal intervals. For more information see `the Gallery demo
        <https://residentmario.github.io/geoplot/examples/largest-cities-usa.html>`_.
    legend_labels : list, optional
        The names to use in the legend. Defaults to the variable values. For more information see `the Gallery demo
        <https://residentmario.github.io/geoplot/examples/largest-cities-usa.html>`_.
    legend_kwargs : dict, optional
        Keyword arguments to be passed to `the underlying legend <http://matplotlib.org/users/legend_guide.html>`_.
    extent : None or (minx, maxx, miny, maxy), optional
        Used to control plot x-axis and y-axis limits manually.
    figsize : tuple, optional
        An (x, y) tuple passed to ``matplotlib.figure`` which sets the size, in inches, of the resultant plot.
    ax : AxesSubplot or GeoAxesSubplot instance, optional
        A ``matplotlib.axes.AxesSubplot`` or ``cartopy.mpl.geoaxes.GeoAxesSubplot`` instance. Defaults to a new axis.
    kwargs: dict, optional
        Keyword arguments to be passed to the underlying ``matplotlib`` `Line2D objects
        <http://matplotlib.org/api/lines_api.html#matplotlib.lines.Line2D>`_.

    Returns
    -------
    AxesSubplot or GeoAxesSubplot instance
        The axis object with the plot on it.

    Examples
    --------

    The neighborhood closest to a point in space is known as its `Voronoi region
    <https://en.wikipedia.org/wiki/Voronoi_diagram>`_. Every point in a dataset has a Voronoi region, which may be
    either a closed polygon (for inliers) or open infinite region (for points on the edge of the distribution). A
    Voronoi diagram works by dividing a space filled with points into such regions and plotting the result. Voronoi
    plots allow efficient assessmelt of the *density* of points in different spaces, and when combined with a
    colormap can be quite informative of overall trends in the dataset.

    The ``geoplot`` ``voronoi`` is a spatially aware application of this technique. It compares well with the more
    well-known ``choropleth``, which has the advantage of using meaningful regions, but the disadvantage of having
    defined those regions beforehand. ``voronoi`` has fewer requirements and may perform better when the number of
    observations is small. Compare also with the quadtree technique available in ``aggplot``.

    A basic ``voronoi`` specified data and, optionally, a projection. We overlay geometry to aid interpretability.

    .. code-block:: python

        ax = gplt.voronoi(injurious_collisions.head(1000))
        gplt.polyplot(boroughs, ax=ax)

    .. image:: ../figures/voronoi/voronoi-simple.png

    ``hue`` parameterizes the color, and ``cmap`` controls the colormap.

    .. code-block:: python

        ax = gplt.voronoi(injurious_collisions.head(1000), hue='NUMBER OF PERSONS INJURED', cmap='Reds')
        gplt.polyplot(boroughs, ax=ax)

    .. image:: ../figures/voronoi/voronoi-cmap.png

    Add a ``clip`` of iterable geometries to trim the ``voronoi`` against local geography.

    .. code-block:: python

        ax = gplt.voronoi(injurious_collisions.head(1000), hue='NUMBER OF PERSONS INJURED', cmap='Reds',
                          clip=boroughs.geometry)
        gplt.polyplot(boroughs, ax=ax)

    .. image:: ../figures/voronoi/voronoi-clip.png

    ``legend`` adds a a ``matplotlib`` `Legend
    <http://matplotlib.org/api/legend_api.html#matplotlib.legend.Legend>`_. This can be tuned even further using the
    ``legend_kwargs`` argument. Other keyword parameters are passed to the underlying ``matplotlib`` `Polygon patches
    <http://matplotlib.org/api/patches_api.html#matplotlib.patches.Polygon>`_.

    .. code-block:: python

        ax = gplt.voronoi(injurious_collisions.head(1000), hue='NUMBER OF PERSONS INJURED', cmap='Reds',
                          clip=boroughs.geometry,
                          legend=True, legend_kwargs={'loc': 'upper left'},
                          linewidth=0.5, edgecolor='white',
                         )
        gplt.polyplot(boroughs, ax=ax)

    .. image:: ../figures/voronoi/voronoi-kwargs.png

    Change the number of bins by specifying an alternative ``k`` value. To use a continuous colormap, explicitly
    specify ``k=None``.  You can change the binning sceme with ``scheme``. The default is ``quantile``, which bins
    observations into classes of different sizes but the same numbers of observations. ``equal_interval`` will
    creates bins that are the same size, but potentially containing different numbers of observations. The more
    complicated ``fisher_jenks`` scheme is an intermediate between the two.

    .. code-block:: python

        ax = gplt.voronoi(injurious_collisions.head(1000),
                          hue='NUMBER OF PERSONS INJURED', cmap='Reds', k=5, scheme='fisher_jenks',
                          clip=boroughs.geometry,
                          legend=True, legend_kwargs={'loc': 'upper left'},
                          linewidth=0.5, edgecolor='white',
                         )
        gplt.polyplot(boroughs, ax=ax)

    .. image:: ../figures/voronoi/voronoi-scheme.png

    If your variable of interest is already `categorical
    <http://pandas.pydata.org/pandas-docs/stable/categorical.html>`_, specify ``categorical=True`` to
    use the labels in your dataset directly.

    .. code-block:: python

        ax = gplt.voronoi(injurious_collisions.head(1000), hue='NUMBER OF PERSONS INJURED', cmap='Reds',
             edgecolor='white', clip=boroughs.geometry,
             linewidth=0.5, categorical=True
             )
        gplt.polyplot(boroughs, linewidth=1, ax=ax)

    .. image:: ../figures/voronoi/voronoi-multiparty.png