Note

This page was generated from gallery/plotting_with_geoplot.ipynb.
Interactive online version: Binder badge

Plotting with Geoplot and GeoPandas#

Geoplot is a Python library providing a selection of easy-to-use geospatial visualizations. It is built on top of the lower-level CartoPy, covered in a separate section of this tutorial, and is designed to work with GeoPandas input.

This example is a brief tour of the geoplot API. For more details on the library refer to its documentation.

First we’ll load in the data using GeoPandas.

[1]:
import geopandas
import geoplot

world = geopandas.read_file(
    geopandas.datasets.get_path('naturalearth_lowres')
)
boroughs = geopandas.read_file(
    geoplot.datasets.get_path('nyc_boroughs')
)
collisions = geopandas.read_file(
    geoplot.datasets.get_path('nyc_injurious_collisions')
)
ERROR 1: PROJ: proj_create_from_database: Open of /home/docs/checkouts/readthedocs.org/user_builds/geopandas/conda/v0.12.0/share/proj failed

Plotting with Geoplot#

We start out by replicating the basic GeoPandas world plot using Geoplot.

[2]:
geoplot.polyplot(world, figsize=(8, 4))
/home/docs/checkouts/readthedocs.org/user_builds/geopandas/conda/v0.12.0/lib/python3.10/site-packages/geoplot/geoplot.py:885: ShapelyDeprecationWarning: Iteration over multi-part geometries is deprecated and will be removed in Shapely 2.0. Use the `geoms` property to access the constituent parts of a multi-part geometry.
  for subgeom in geom:
[2]:
<AxesSubplot: >
../_images/gallery_plotting_with_geoplot_3_2.png

Geoplot can re-project data into any of the map projections provided by CartoPy (see the list here).

[3]:
# use the Orthographic map projection (e.g. a world globe)
ax = geoplot.polyplot(
    world, projection=geoplot.crs.Orthographic(), figsize=(8, 4)
)
ax.outline_patch.set_visible(True)
/home/docs/checkouts/readthedocs.org/user_builds/geopandas/conda/v0.12.0/lib/python3.10/site-packages/geoplot/geoplot.py:703: UserWarning: Plot extent lies outside of the Orthographic projection's viewport. Defaulting to global extent.
  warnings.warn(
/tmp/ipykernel_3733/975099287.py:5: DeprecationWarning: The outline_patch property is deprecated. Use GeoAxes.spines['geo'] or the default Axes properties instead.
  ax.outline_patch.set_visible(True)
Error in callback <function _draw_all_if_interactive at 0x7f552bd5fbe0> (for post_execute):
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/v0.12.0/lib/python3.10/site-packages/matplotlib/pyplot.py:119, in _draw_all_if_interactive()
    117 def _draw_all_if_interactive():
    118     if matplotlib.is_interactive():
--> 119         draw_all()

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/v0.12.0/lib/python3.10/site-packages/matplotlib/_pylab_helpers.py:132, in Gcf.draw_all(cls, force)
    130 for manager in cls.get_all_fig_managers():
    131     if force or manager.canvas.figure.stale:
--> 132         manager.canvas.draw_idle()

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/v0.12.0/lib/python3.10/site-packages/matplotlib/backend_bases.py:2054, in FigureCanvasBase.draw_idle(self, *args, **kwargs)
   2052 if not self._is_idle_drawing:
   2053     with self._idle_draw_cntx():
-> 2054         self.draw(*args, **kwargs)

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/v0.12.0/lib/python3.10/site-packages/matplotlib/backends/backend_agg.py:408, in FigureCanvasAgg.draw(self)
    404 # Acquire a lock on the shared font cache.
    405 with RendererAgg.lock, \
    406      (self.toolbar._wait_cursor_for_draw_cm() if self.toolbar
    407       else nullcontext()):
--> 408     self.figure.draw(self.renderer)
    409     # A GUI class may be need to update a window using this draw, so
    410     # don't forget to call the superclass.
    411     super().draw()

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/v0.12.0/lib/python3.10/site-packages/matplotlib/artist.py:74, in _finalize_rasterization.<locals>.draw_wrapper(artist, renderer, *args, **kwargs)
     72 @wraps(draw)
     73 def draw_wrapper(artist, renderer, *args, **kwargs):
---> 74     result = draw(artist, renderer, *args, **kwargs)
     75     if renderer._rasterizing:
     76         renderer.stop_rasterizing()

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/v0.12.0/lib/python3.10/site-packages/matplotlib/artist.py:51, in allow_rasterization.<locals>.draw_wrapper(artist, renderer)
     48     if artist.get_agg_filter() is not None:
     49         renderer.start_filter()
---> 51     return draw(artist, renderer)
     52 finally:
     53     if artist.get_agg_filter() is not None:

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/v0.12.0/lib/python3.10/site-packages/matplotlib/figure.py:3074, in Figure.draw(self, renderer)
   3071         # ValueError can occur when resizing a window.
   3073 self.patch.draw(renderer)
-> 3074 mimage._draw_list_compositing_images(
   3075     renderer, self, artists, self.suppressComposite)
   3077 for sfig in self.subfigs:
   3078     sfig.draw(renderer)

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/v0.12.0/lib/python3.10/site-packages/matplotlib/image.py:131, in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
    129 if not_composite or not has_images:
    130     for a in artists:
--> 131         a.draw(renderer)
    132 else:
    133     # Composite any adjacent images together
    134     image_group = []

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/v0.12.0/lib/python3.10/site-packages/matplotlib/artist.py:51, in allow_rasterization.<locals>.draw_wrapper(artist, renderer)
     48     if artist.get_agg_filter() is not None:
     49         renderer.start_filter()
---> 51     return draw(artist, renderer)
     52 finally:
     53     if artist.get_agg_filter() is not None:

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/v0.12.0/lib/python3.10/site-packages/cartopy/mpl/geoaxes.py:543, in GeoAxes.draw(self, renderer, **kwargs)
    535 """
    536 Extend the standard behaviour of :func:`matplotlib.axes.Axes.draw`.
    537
   (...)
    540 been set.
    541 """
    542 # Shared processing steps
--> 543 self._draw_preprocess(renderer)
    545 # XXX This interface needs a tidy up:
    546 #       image drawing on pan/zoom;
    547 #       caching the resulting image;
    548 #       buffering the result by 10%...;
    549 if not self._done_img_factory:

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/v0.12.0/lib/python3.10/site-packages/cartopy/mpl/geoaxes.py:509, in GeoAxes._draw_preprocess(self, renderer)
    506 # If data has been added (i.e. autoscale hasn't been turned off)
    507 # then we should autoscale the view.
    508 if self.get_autoscale_on() and self.ignore_existing_data_limits:
--> 509     self.autoscale_view()
    511 # Adjust location of background patch so that new gridlines below are
    512 # clipped correctly.
    513 self.patch._adjust_location()

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/v0.12.0/lib/python3.10/site-packages/cartopy/mpl/geoaxes.py:946, in GeoAxes.autoscale_view(self, tight, scalex, scaley)
    943 matplotlib.axes.Axes.autoscale_view(self, tight=tight,
    944                                     scalex=scalex, scaley=scaley)
    945 # Limit the resulting bounds to valid area.
--> 946 if scalex and self._autoscaleXon:
    947     bounds = self.get_xbound()
    948     self.set_xbound(max(bounds[0], self.projection.x_limits[0]),
    949                     min(bounds[1], self.projection.x_limits[1]))

AttributeError: 'GeoAxesSubplot' object has no attribute '_autoscaleXon'
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/v0.12.0/lib/python3.10/site-packages/IPython/core/formatters.py:339, in BaseFormatter.__call__(self, obj)
    337     pass
    338 else:
--> 339     return printer(obj)
    340 # Finally look for special method names
    341 method = get_real_method(obj, self.print_method)

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/v0.12.0/lib/python3.10/site-packages/IPython/core/pylabtools.py:151, in print_figure(fig, fmt, bbox_inches, base64, **kwargs)
    148     from matplotlib.backend_bases import FigureCanvasBase
    149     FigureCanvasBase(fig)
--> 151 fig.canvas.print_figure(bytes_io, **kw)
    152 data = bytes_io.getvalue()
    153 if fmt == 'svg':

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/v0.12.0/lib/python3.10/site-packages/matplotlib/backend_bases.py:2314, in FigureCanvasBase.print_figure(self, filename, dpi, facecolor, edgecolor, orientation, format, bbox_inches, pad_inches, bbox_extra_artists, backend, **kwargs)
   2308     renderer = _get_renderer(
   2309         self.figure,
   2310         functools.partial(
   2311             print_method, orientation=orientation)
   2312     )
   2313     with getattr(renderer, "_draw_disabled", nullcontext)():
-> 2314         self.figure.draw(renderer)
   2316 if bbox_inches:
   2317     if bbox_inches == "tight":

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/v0.12.0/lib/python3.10/site-packages/matplotlib/artist.py:74, in _finalize_rasterization.<locals>.draw_wrapper(artist, renderer, *args, **kwargs)
     72 @wraps(draw)
     73 def draw_wrapper(artist, renderer, *args, **kwargs):
---> 74     result = draw(artist, renderer, *args, **kwargs)
     75     if renderer._rasterizing:
     76         renderer.stop_rasterizing()

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/v0.12.0/lib/python3.10/site-packages/matplotlib/artist.py:51, in allow_rasterization.<locals>.draw_wrapper(artist, renderer)
     48     if artist.get_agg_filter() is not None:
     49         renderer.start_filter()
---> 51     return draw(artist, renderer)
     52 finally:
     53     if artist.get_agg_filter() is not None:

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/v0.12.0/lib/python3.10/site-packages/matplotlib/figure.py:3074, in Figure.draw(self, renderer)
   3071         # ValueError can occur when resizing a window.
   3073 self.patch.draw(renderer)
-> 3074 mimage._draw_list_compositing_images(
   3075     renderer, self, artists, self.suppressComposite)
   3077 for sfig in self.subfigs:
   3078     sfig.draw(renderer)

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/v0.12.0/lib/python3.10/site-packages/matplotlib/image.py:131, in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
    129 if not_composite or not has_images:
    130     for a in artists:
--> 131         a.draw(renderer)
    132 else:
    133     # Composite any adjacent images together
    134     image_group = []

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/v0.12.0/lib/python3.10/site-packages/matplotlib/artist.py:51, in allow_rasterization.<locals>.draw_wrapper(artist, renderer)
     48     if artist.get_agg_filter() is not None:
     49         renderer.start_filter()
---> 51     return draw(artist, renderer)
     52 finally:
     53     if artist.get_agg_filter() is not None:

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/v0.12.0/lib/python3.10/site-packages/cartopy/mpl/geoaxes.py:543, in GeoAxes.draw(self, renderer, **kwargs)
    535 """
    536 Extend the standard behaviour of :func:`matplotlib.axes.Axes.draw`.
    537
   (...)
    540 been set.
    541 """
    542 # Shared processing steps
--> 543 self._draw_preprocess(renderer)
    545 # XXX This interface needs a tidy up:
    546 #       image drawing on pan/zoom;
    547 #       caching the resulting image;
    548 #       buffering the result by 10%...;
    549 if not self._done_img_factory:

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/v0.12.0/lib/python3.10/site-packages/cartopy/mpl/geoaxes.py:509, in GeoAxes._draw_preprocess(self, renderer)
    506 # If data has been added (i.e. autoscale hasn't been turned off)
    507 # then we should autoscale the view.
    508 if self.get_autoscale_on() and self.ignore_existing_data_limits:
--> 509     self.autoscale_view()
    511 # Adjust location of background patch so that new gridlines below are
    512 # clipped correctly.
    513 self.patch._adjust_location()

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/v0.12.0/lib/python3.10/site-packages/cartopy/mpl/geoaxes.py:946, in GeoAxes.autoscale_view(self, tight, scalex, scaley)
    943 matplotlib.axes.Axes.autoscale_view(self, tight=tight,
    944                                     scalex=scalex, scaley=scaley)
    945 # Limit the resulting bounds to valid area.
--> 946 if scalex and self._autoscaleXon:
    947     bounds = self.get_xbound()
    948     self.set_xbound(max(bounds[0], self.projection.x_limits[0]),
    949                     min(bounds[1], self.projection.x_limits[1]))

AttributeError: 'GeoAxesSubplot' object has no attribute '_autoscaleXon'
<Figure size 800x400 with 1 Axes>

polyplot is trivial and can only plot the geometries you pass to it. If you want to use color as a visual variable, specify a choropleth. Here we sort GDP per person by country into five buckets by color, using “quantiles” binning from the Mapclassify library.

[4]:
import mapclassify
gpd_per_person = world['gdp_md_est'] / world['pop_est']
scheme = mapclassify.Quantiles(gpd_per_person, k=5)

# Note: this code sample requires geoplot>=0.4.0.
geoplot.choropleth(
    world, hue=gpd_per_person, scheme=scheme,
    cmap='Greens', figsize=(8, 4)
)
/home/docs/checkouts/readthedocs.org/user_builds/geopandas/conda/v0.12.0/lib/python3.10/site-packages/geoplot/geoplot.py:982: ShapelyDeprecationWarning: Iteration over multi-part geometries is deprecated and will be removed in Shapely 2.0. Use the `geoms` property to access the constituent parts of a multi-part geometry.
  for subgeom in geom:
[4]:
<AxesSubplot: >
../_images/gallery_plotting_with_geoplot_7_2.png

If you want to use size as a visual variable, use a cartogram. Here are population estimates for countries in Africa.

[5]:
africa = world.query('continent == "Africa"')
ax = geoplot.cartogram(
    africa, scale='pop_est', limits=(0.2, 1),
    edgecolor='None', figsize=(7, 8)
)
geoplot.polyplot(africa, edgecolor='gray', ax=ax)
/home/docs/checkouts/readthedocs.org/user_builds/geopandas/conda/v0.12.0/lib/python3.10/site-packages/geoplot/geoplot.py:1233: ShapelyDeprecationWarning: Iteration over multi-part geometries is deprecated and will be removed in Shapely 2.0. Use the `geoms` property to access the constituent parts of a multi-part geometry.
  for subgeom in scaled_polygon:
/home/docs/checkouts/readthedocs.org/user_builds/geopandas/conda/v0.12.0/lib/python3.10/site-packages/geoplot/geoplot.py:885: ShapelyDeprecationWarning: Iteration over multi-part geometries is deprecated and will be removed in Shapely 2.0. Use the `geoms` property to access the constituent parts of a multi-part geometry.
  for subgeom in geom:
[5]:
<AxesSubplot: >
../_images/gallery_plotting_with_geoplot_9_2.png

If we have data in the shape of points in space, we may generate a three-dimensional heatmap on it using kdeplot.

[6]:
ax = geoplot.kdeplot(
    collisions.head(1000), clip=boroughs.geometry,
    shade=True, cmap='Reds',
    projection=geoplot.crs.AlbersEqualArea())
geoplot.polyplot(boroughs, ax=ax, zorder=1)
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In [6], line 1
----> 1 ax = geoplot.kdeplot(
      2     collisions.head(1000), clip=boroughs.geometry,
      3     shade=True, cmap='Reds',
      4     projection=geoplot.crs.AlbersEqualArea())
      5 geoplot.polyplot(boroughs, ax=ax, zorder=1)

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/v0.12.0/lib/python3.10/site-packages/geoplot/geoplot.py:1317, in kdeplot(df, projection, extent, figsize, ax, clip, **kwargs)
   1310             sns.kdeplot(
   1311                 x=pd.Series([p.x for p in self.df.geometry]),
   1312                 y=pd.Series([p.y for p in self.df.geometry]),
   1313                 ax=ax, **self.kwargs
   1314             )
   1315         return ax
-> 1317 plot = KDEPlot(
   1318     df, projection=projection, extent=extent, figsize=figsize, ax=ax, clip=clip, **kwargs
   1319 )
   1320 return plot.draw()

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/v0.12.0/lib/python3.10/site-packages/geoplot/geoplot.py:1296, in kdeplot.<locals>.KDEPlot.__init__(self, df, **kwargs)
   1294 def __init__(self, df, **kwargs):
   1295     super().__init__(df, **kwargs)
-> 1296     self.paint_clip()

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/v0.12.0/lib/python3.10/site-packages/geoplot/geoplot.py:508, in ClipMixin.paint_clip(self)
    506 if clip is not None:
    507     if self.projection is not None:
--> 508         xmin, xmax, ymin, ymax = self.ax.get_extent(crs=ccrs.PlateCarree())
    509         extent = (xmin, ymin, xmax, ymax)
    510         clip_geom = self._get_clip(extent, clip)

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/v0.12.0/lib/python3.10/site-packages/cartopy/mpl/geoaxes.py:814, in GeoAxes.get_extent(self, crs)
    805 def get_extent(self, crs=None):
    806     """
    807     Get the extent (x0, x1, y0, y1) of the map in the given coordinate
    808     system.
   (...)
    812
    813     """
--> 814     p = self._get_extent_geom(crs)
    815     r = p.bounds
    816     x1, y1, x2, y2 = r

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/v0.12.0/lib/python3.10/site-packages/cartopy/mpl/geoaxes.py:821, in GeoAxes._get_extent_geom(self, crs)
    819 def _get_extent_geom(self, crs=None):
    820     # Perform the calculations for get_extent(), which just repackages it.
--> 821     with self.hold_limits():
    822         if self.get_autoscale_on():
    823             self.autoscale_view()

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/v0.12.0/lib/python3.10/contextlib.py:135, in _GeneratorContextManager.__enter__(self)
    133 del self.args, self.kwds, self.func
    134 try:
--> 135     return next(self.gen)
    136 except StopIteration:
    137     raise RuntimeError("generator didn't yield") from None

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/v0.12.0/lib/python3.10/site-packages/cartopy/mpl/geoaxes.py:491, in GeoAxes.hold_limits(self, hold)
    488 data_lim = self.dataLim.frozen().get_points()
    489 view_lim = self.viewLim.frozen().get_points()
    490 other = (self.ignore_existing_data_limits,
--> 491          self._autoscaleXon, self._autoscaleYon)
    492 try:
    493     yield

AttributeError: 'GeoAxesSubplot' object has no attribute '_autoscaleXon'
../_images/gallery_plotting_with_geoplot_11_1.png

These are just some of the plots you can make with Geoplot. There are many other possibilities not covered in this brief introduction. For more examples, refer to the Gallery in the Geoplot documentation.