Geographic and polar plots

ProPlot includes features for working with polar axes and the cartopy and basemap map projection packages. These features are optional – installation of cartopy and basemap are not required.

To change the axes projection, pass proj='name' to subplots. To use different projections for different subplots, pass a dictionary of projection names with the subplot number as the key – for example, proj={1: 'name'}. The default “projection” is always CartesianAxes.

Polar axes

To draw polar axes, pass proj='polar' or e.g. proj={1:'polar'} to subplots. This generates a PolarAxes instance with its own proplot.axes.PolarAxes.format command. This command permits polar-specific modifications like changing the central radius r0, the zero azimuth location theta0, and the positive azimuthal direction thetadir. It also supports changing the radial and azimuthal limits rlim and thetalim, which can be used to make sector plots and annular plots.

For details, see proplot.axes.PolarAxes.format.

  1. [1]:
  1. import proplot as plot
  2. import numpy as np
  3. N = 200
  4. state = np.random.RandomState(51423)
  5. x = np.linspace(0, 2 * np.pi, N)
  6. y = 100 * (state.rand(N, 5) - 0.3).cumsum(axis=0) / N
  7. fig, axs = plot.subplots([[1, 1, 2, 2], [0, 3, 3, 0]], proj='polar')
  8. axs.format(
  9. suptitle='Polar axes demo', linewidth=1, titlepad='1em',
  10. ticklabelsize=9, rlines=0.5, rlim=(0, 19),
  11. )
  12. for i in range(5):
  13. xi = x + i * 2 * np.pi / 5
  14. axs.plot(xi, y[:, i], cycle='FlatUI', zorder=0, lw=3)
  15. # Standard polar plot
  16. axs[0].format(
  17. title='Normal plot', thetaformatter='tau',
  18. rlabelpos=225, rlines=plot.arange(5, 30, 5),
  19. color='red8', tickpad='1em',
  20. )
  21. # Sector plot
  22. axs[1].format(
  23. title='Sector plot', thetadir=-1, thetalines=90, thetalim=(0, 270), theta0='N',
  24. rlim=(0, 22), rlines=plot.arange(5, 30, 5),
  25. )
  26. # Annular plot
  27. axs[2].format(
  28. title='Annular plot', thetadir=-1, thetalines=20, gridcolor='red',
  29. r0=-20, rlim=(0, 22), rformatter='null', rlocator=2
  30. )

_images/projections_2_0.svg

Geographic axes

ProPlot can turn any subplot into a geographic projection using the cartopy or basemap packages as “backends”. The syntax with cartopy as the backend is exactly the same as when basemap is the backend.

To turn a subplot into a geographic projection, pass proj='name' or e.g. proj={2: 'name'} (see above) to subplots where name is any valid PROJ projection name. You can also generate a cartopy.crs.Projection or mpl_toolkits.basemap.Basemap instance directly using the Proj constructor function and pass the class instance to proj.

subplots returns instances of proplot.axes.CartopyAxes or proplot.axes.BasemapAxes, depending on whether basemap=True was used. Both of these derive from proplot.axes.GeoAxes, which includes a format method that can be used to control various geographic features with the same syntax whether cartopy or basemap is the backend.

These features mean you no longer have to invoke verbose cartopy classes like LambertAzimuthalEqualArea and NaturalEarthFeature, and you no longer have to directly work with the Basemap instance. In the below examples, we create a variety of geographic plots with both cartopy and basemap as the backends.

Note

  • ProPlot ensures that polar cartopy projections like NorthPolarStereo have circular boundaries (see this example from the cartopy website).

  • By default, non-polar cartopy projections are forced to have global extent with set_global and polar cartopy projections are bounded at the equator. This stands in contrast to the default cartopy behavior, where map boundaries are determined automatically based on the coordinates of the plotted content. To revert to cartopy’s default behavior, set [rc[‘cartopy.autoextent’]](https://proplot.readthedocs.io/en/latest/configuration.html?highlight=cartopy.autoextent#rc-proplot) to True or pass autoextent=True to CartopyAxes.

  • To make things more consistent between cartopy and basemap, the Proj constructor function lets you supply native PROJ keyword names for the cartopy Projection classes (e.g. lon_0 instead of central_longitude) and instantiates Basemap projections with sensible default PROJ parameters rather than raising an error when they are omitted (e.g. lon_0=0 as the default for most projections).

Warning

Basemap is no longer a maintained package. However as shown below, gridline labels tend to look much nicer in basemap than in cartopy – especially when “inline” cartopy labels are disabled. This is the main reason ProPlot continues to support both basemap and cartopy. When cartopy catches up, basemap support may be deprecated.

  1. [2]:
  1. # Simple figure with just one projection
  2. # Option 1: Create a projection manually with plot.Proj()
  3. # immport proplot as plot
  4. # proj = plot.Proj('robin', lon_0=180)
  5. # fig, axs = plot.subplots(nrows=2, axwidth=3, proj=proj)
  6. # Option 2: Pass the name to 'proj' and keyword arguments to 'proj_kw'
  7. import proplot as plot
  8. fig, axs = plot.subplots(nrows=2, axwidth=3, proj='robin', proj_kw={'lon_0': 180})
  9. axs.format(
  10. suptitle='Figure with single projection',
  11. coast=True, latlines=30, lonlines=60,
  12. )
  1. /home/docs/checkouts/readthedocs.org/user_builds/proplot/conda/v0.6.4/lib/python3.8/site-packages/cartopy/io/__init__.py:260: DownloadWarning: Downloading: https://naciscdn.org/naturalearth/110m/physical/ne_110m_coastline.zip
  2. warnings.warn('Downloading: {}'.format(url), DownloadWarning)

_images/projections_4_1.svg

  1. [3]:
  1. # Complex figure with different projections
  2. import proplot as plot
  3. fig, axs = plot.subplots(
  4. ncols=2, nrows=3,
  5. hratios=(1, 1, 1.4),
  6. basemap=(False, True, False, True, False, True), # cartopy column 1
  7. proj=('cyl', 'cyl', 'hammer', 'hammer', 'npstere', 'npstere'),
  8. )
  9. axs.format(
  10. suptitle='Figure with several projections',
  11. collabels=['Cartopy projections', 'Basemap projections'],
  12. coast=True, latlines=20, lonlines=30,
  13. lonlabels='b', latlabels='r', # or lonlabels=True, labels=True, etc.
  14. )
  15. axs[0, :].format(latlines=30, lonlines=60, labels=True)
  16. plot.rc.reset()
  1. Warning: Cannot label meridians on Hammer basemap

_images/projections_5_1.svg

Plotting geographic data

In ProPlot, plotting in GeoAxes is not much different from plotting in CartesianAxes. ProPlot makes longitude-latitude (i.e. Plate Carrée) coordinates the default coordinate system for your datasets by passing transform=ccrs.PlateCarree() to cartopy plotting commands and latlon=True to basemap plotting commands. And again, basemap plotting commands are invoked from the proplot.axes.GeoAxes rather than from the Basemap instance.

To ensure 2D plots like contour cover the entire globe, pass globe=True to the plotting command. This interpolates your data to the poles and across the longitude seams before plotting, having the same effect as cartopy’s add_cyclic_point function and basemap’s addcyclic function.

Geographic feature can be drawn underneath data or on top of data by changing the corresponding zorder rc setting. For example, to draw land patches on top of all plotted content as a “land mask,” use ax.format(land=True, landzorder=4). See the next section for details.

  1. [4]:
  1. import proplot as plot
  2. import numpy as np
  3. # Fake data with unusual longitude seam location and without coverage over poles
  4. offset = -40
  5. lon = plot.arange(offset, 360 + offset - 1, 60)
  6. lat = plot.arange(-60, 60 + 1, 30)
  7. state = np.random.RandomState(51423)
  8. data = state.rand(len(lat), len(lon))
  9. # Plot data both without and with globe=True
  10. for globe in (False, True,):
  11. string = 'with' if globe else 'without'
  12. fig, axs = plot.subplots(
  13. ncols=2, nrows=2, axwidth=2.5,
  14. proj='kav7', basemap={(1, 3): False, (2, 4): True}
  15. )
  16. axs.format(
  17. suptitle=f'Geophysical data {string} global coverage',
  18. collabels=['Cartopy example', 'Basemap example'],
  19. rowlabels=['Contourf', 'Pcolormesh'],
  20. abc=True, abcstyle='a)', abcloc='ul', abcborder=False,
  21. coast=True, lonlines=90,
  22. )
  23. for i, ax in enumerate(axs):
  24. cmap = ('sunset', 'sunrise')[i % 2]
  25. if i < 2:
  26. m = ax.contourf(lon, lat, data, cmap=cmap, globe=globe, extend='both')
  27. fig.colorbar(m, loc='b', span=i + 1, label='values', extendsize='1.7em')
  28. else:
  29. ax.pcolor(lon, lat, data, cmap=cmap, globe=globe, extend='both')

_images/projections_7_0.svg

_images/projections_7_1.svg

Formatting projections

CartopyAxes and BasemapAxes both derive from proplot.axes.GeoAxes, which provides a format method. This can be used to draw “major” gridlines “minor” gridlines. Gridline locations and label formats can be configured with the lonlocator, latlocator, lonformatter, latformatter, lonminorlocator, and latminorlocator keywords. Major gridline labels and their positions can be configured with the labels, lonlabels, and latlabels keywords. Cartopy map bounds can be set with the lonlim, latlim, and boundinglat keywords. Geographic features like land masses, coastlines, and administrative borders can be toggled on and off and stylized with a variety of rc settings. Finally, proplot.axes.GeoAxes.format also calls proplot.axes.Axes.format, and so can be used to for subplot titles, a-b-c labels, and figure titles as before.

For details, see the proplot.axes.GeoAxes.format documentation.

  1. [5]:
  1. import proplot as plot
  2. fig, axs = plot.subplots(
  3. [[1, 1, 2], [3, 3, 3]],
  4. axwidth=4, proj={1: 'eqearth', 2: 'ortho', 3: 'wintri'},
  5. wratios=(1, 1, 1.2), hratios=(1, 1.2),
  6. )
  7. axs.format(
  8. suptitle='Projection axes formatting demo',
  9. collabels=['Column 1', 'Column 2'],
  10. abc=True, abcstyle='A.', abcloc='ul', abcborder=False, linewidth=1.5
  11. )
  12. # Styling projections in different ways
  13. ax = axs[0]
  14. ax.format(
  15. title='Equal earth', land=True, landcolor='navy', facecolor='pale blue',
  16. coastcolor='gray5', borderscolor='gray5', innerborderscolor='gray5',
  17. gridlinewidth=1.5, gridcolor='gray5', gridalpha=0.5,
  18. gridminor=True, gridminorlinewidth=0.5,
  19. coast=True, borders=True, borderslinewidth=0.8,
  20. )
  21. ax = axs[1]
  22. ax.format(
  23. title='Orthographic', reso='med', land=True, coast=True, latlines=10, lonlines=15,
  24. landcolor='mushroom', suptitle='Projection axes formatting demo',
  25. facecolor='petrol', coastcolor='charcoal', coastlinewidth=0.8, gridlinewidth=1
  26. )
  27. ax = axs[2]
  28. ax.format(
  29. land=True, facecolor='ocean blue', landcolor='bisque', title='Winkel tripel',
  30. lonlines=60, latlines=15,
  31. gridlinewidth=0.8, gridminor=True, gridminorlinestyle=':',
  32. lonlabels=True, latlabels='r', loninline=True,
  33. gridlabelcolor='gray8', gridlabelsize='med-large',
  34. )
  1. /home/docs/checkouts/readthedocs.org/user_builds/proplot/conda/v0.6.4/lib/python3.8/site-packages/cartopy/io/__init__.py:260: DownloadWarning: Downloading: https://naciscdn.org/naturalearth/110m/physical/ne_110m_land.zip
  2. warnings.warn('Downloading: {}'.format(url), DownloadWarning)
  3. /home/docs/checkouts/readthedocs.org/user_builds/proplot/conda/v0.6.4/lib/python3.8/site-packages/cartopy/io/__init__.py:260: DownloadWarning: Downloading: https://naciscdn.org/naturalearth/110m/cultural/ne_110m_admin_0_boundary_lines_land.zip
  4. warnings.warn('Downloading: {}'.format(url), DownloadWarning)
  5. /home/docs/checkouts/readthedocs.org/user_builds/proplot/conda/v0.6.4/lib/python3.8/site-packages/cartopy/io/__init__.py:260: DownloadWarning: Downloading: https://naciscdn.org/naturalearth/50m/physical/ne_50m_land.zip
  6. warnings.warn('Downloading: {}'.format(url), DownloadWarning)
  7. /home/docs/checkouts/readthedocs.org/user_builds/proplot/conda/v0.6.4/lib/python3.8/site-packages/cartopy/io/__init__.py:260: DownloadWarning: Downloading: https://naciscdn.org/naturalearth/50m/physical/ne_50m_coastline.zip
  8. warnings.warn('Downloading: {}'.format(url), DownloadWarning)

_images/projections_9_1.svg

Zooming into projections

To zoom into cartopy projections, use set_extent or pass lonlim, latlim, or boundinglat to format. The boundinglat keyword controls the circular latitude boundary for North Polar and South Polar Stereographic, Azimuthal Equidistant, Lambert Azimuthal Equal-Area, and Gnomonic projections. By default, ProPlot tries to use the degree-minute-second cartopy locators and formatters made available in cartopy 0.18. You can switch from minute-second subintervals to traditional decimal subintervals by passing dms=False to format.

To zoom into basemap projections, pass any of the boundinglat, llcrnrlon, llcrnrlat, urcrnrlon, urcrnrlat, llcrnrx, llcrnry, urcrnrx, urcrnry, width, or height keyword arguments to the Proj constructor function either directly or via the proj_kw subplots keyword argument. You can also pass lonlim and latlim to Proj and these arguments will be used for llcrnrlon, llcrnrlat, etc. You can not zoom into basemap projections with format after they have already been created.

  1. [6]:
  1. import proplot as plot
  2. # Plate Carrée map projection
  3. plot.rc.reso = 'med' # use higher res for zoomed in geographic features
  4. proj = plot.Proj('cyl', lonlim=(-20, 180), latlim=(-10, 50), basemap=True)
  5. fig, axs = plot.subplots(nrows=2, axwidth=5, proj=('cyl', proj))
  6. axs.format(
  7. land=True, labels=True, lonlines=20, latlines=20,
  8. gridminor=True, suptitle='Zooming into projections'
  9. )
  10. axs[0].format(
  11. lonlim=(-140, 60), latlim=(-10, 50),
  12. labels=True, title='Cartopy example'
  13. )
  14. axs[1].format(title='Basemap example')

_images/projections_11_0.svg

  1. [7]:
  1. import proplot as plot
  2. # Pole-centered map projections
  3. proj = plot.Proj('npaeqd', boundinglat=60, basemap=True)
  4. fig, axs = plot.subplots(ncols=2, axwidth=2.7, proj=('splaea', proj))
  5. axs.format(
  6. land=True, latmax=80, # no gridlines poleward of 80 degrees
  7. suptitle='Zooming into polar projections'
  8. )
  9. axs[0].format(boundinglat=-60, title='Cartopy example')
  10. axs[1].format(title='Basemap example')

_images/projections_12_0.svg

  1. [8]:
  1. import proplot as plot
  2. # Zooming in on continents
  3. proj1 = plot.Proj('lcc', lon_0=0) # cartopy projection
  4. proj2 = plot.Proj('lcc', lon_0=-100, lat_0=45, width=8e6, height=8e6, basemap=True)
  5. fig, axs = plot.subplots(ncols=2, axwidth=3, proj=(proj1, proj2))
  6. axs.format(suptitle='Zooming into specific regions', land=True)
  7. axs[0].format(lonlim=(-20, 50), latlim=(30, 70), title='Cartopy example')
  8. axs[1].format(lonlines=20, title='Basemap example')
  9. # Zooming to very small scale with degree-minute-second labels
  10. plot.rc.reso = 'hi'
  11. fig, axs = plot.subplots(ncols=2, axwidth=2.5, proj='cyl')
  12. axs.format(
  13. land=True, labels=True,
  14. borders=True, borderscolor='white',
  15. suptitle='Degree-minute-second labels',
  16. )
  17. axs[0].format(lonlim=(-7.5, 2), latlim=(49.5, 59))
  18. axs[1].format(lonlim=(-6, -2), latlim=(54.5, 58.5))
  19. plot.rc.reset()

_images/projections_13_0.svg

  1. /home/docs/checkouts/readthedocs.org/user_builds/proplot/conda/v0.6.4/lib/python3.8/site-packages/cartopy/io/__init__.py:260: DownloadWarning: Downloading: https://naciscdn.org/naturalearth/10m/physical/ne_10m_land.zip
  2. warnings.warn('Downloading: {}'.format(url), DownloadWarning)
  3. /home/docs/checkouts/readthedocs.org/user_builds/proplot/conda/v0.6.4/lib/python3.8/site-packages/cartopy/io/__init__.py:260: DownloadWarning: Downloading: https://naciscdn.org/naturalearth/10m/cultural/ne_10m_admin_0_boundary_lines_land.zip
  4. warnings.warn('Downloading: {}'.format(url), DownloadWarning)

_images/projections_13_2.svg

Included projections

The available cartopy and basemap projections are plotted below. See Proj for a table of projection names with links to the relevant PROJ documentation.

ProPlot uses the cartopy API to add the Aitoff, Hammer, Winkel Tripel, and Kavrisky VII projections (i.e. 'aitoff', 'hammer', 'wintri', and 'kav7'), as well as North and South polar versions of the Azimuthal Equidistant, Lambert Azimuthal Equal-Area, and Gnomic projections (i.e. 'npaeqd', 'spaeqd', 'nplaea', 'splaea', 'npgnom', and 'spgnom'), modeled after the existing NorthPolarStereo and SouthPolarStereo projections.

  1. [9]:
  1. import proplot as plot
  2. # Table of cartopy projections
  3. projs = [
  4. 'cyl', 'merc', 'mill', 'lcyl', 'tmerc',
  5. 'robin', 'hammer', 'moll', 'kav7', 'aitoff', 'wintri', 'sinu',
  6. 'geos', 'ortho', 'nsper', 'aea', 'eqdc', 'lcc', 'gnom',
  7. 'npstere', 'nplaea', 'npaeqd', 'npgnom', 'igh',
  8. 'eck1', 'eck2', 'eck3', 'eck4', 'eck5', 'eck6'
  9. ]
  10. fig, axs = plot.subplots(ncols=3, nrows=10, width=7, proj=projs)
  11. axs.format(
  12. land=True, reso='lo', labels=False,
  13. suptitle='Table of cartopy projections'
  14. )
  15. for proj, ax in zip(projs, axs):
  16. ax.format(title=proj, titleweight='bold', labels=False)
  1. /home/docs/checkouts/readthedocs.org/user_builds/proplot/conda/v0.6.4/lib/python3.8/site-packages/proplot/constructor.py:1433: UserWarning: The default value for the *approx* keyword argument to TransverseMercator will change from True to False after 0.18.
  2. proj = crs(**kwproj)
  3. /home/docs/checkouts/readthedocs.org/user_builds/proplot/conda/v0.6.4/lib/python3.8/site-packages/cartopy/mpl/feature_artist.py:154: UserWarning: Unable to determine extent. Defaulting to global.
  4. warnings.warn('Unable to determine extent. Defaulting to global.')

_images/projections_15_1.svg

  1. [10]:
  1. import proplot as plot
  2. # Table of basemap projections
  3. projs = [
  4. 'cyl', 'merc', 'mill', 'cea', 'gall', 'sinu',
  5. 'eck4', 'robin', 'moll', 'kav7', 'hammer', 'mbtfpq',
  6. 'geos', 'ortho', 'nsper',
  7. 'vandg', 'aea', 'eqdc', 'gnom', 'cass', 'lcc',
  8. 'npstere', 'npaeqd', 'nplaea'
  9. ]
  10. fig, axs = plot.subplots(ncols=3, nrows=8, basemap=True, width=7, proj=projs)
  11. axs.format(
  12. land=True, labels=False,
  13. suptitle='Table of basemap projections'
  14. )
  15. for proj, ax in zip(projs, axs):
  16. ax.format(title=proj, titleweight='bold', labels=False)

_images/projections_16_0.svg