Geographic and polar plots

ProPlot includes seamless integration with matplotlib polar axes and the cartopy and basemap packages. Note that this feature is optional, and does not require that cartopy or basemap are installed.

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 XYAxes.

Polar projections

To draw polar axes, pass proj='polar' or e.g. proj={1:'polar'} to subplots. This generates a PolarAxes instance. Its format command permits polar-specific modifications like changing the central radius, the zero azimuth location, the radial and azimuthal limits, and the positive azimuthal direction. A demonstration is below.

[1]:
import proplot as plot
import numpy as np
f, axs = plot.subplots([[1,1,2,2],[0,3,3,0]], proj='polar')
state = np.random.RandomState(51423)
N = 200
x = np.linspace(0, 2*np.pi, N)
y = 100*(state.rand(N,5)-0.3).cumsum(axis=0)/N
for i in range(5):
    axs.plot(x + i*2*np.pi/5, y[:,i], cycle='contrast', zorder=0, lw=3)
axs.format(suptitle='Polar axes demo', linewidth=1, ticklabelsize=9, rlines=0.5, rlim=(0,19))
axs[0].format(title='Normal plot', thetaformatter='pi', rlines=5, gridalpha=1, gridlinestyle=':',
              rlabelpos=180, color='gray8', ticklabelweight='bold')
axs[1].format(title='Sector plot', thetadir=-1, thetalines=90, thetalim=(0,270), theta0='N',
              rlim=(0,22), rlines=5)
axs[2].format(title='Annular plot', thetadir=-1, thetalines=10,
              r0=0, rlim=(10,22), rformatter='null', rlocator=2)
axs.format(titlepad='1.5em') # matplotlib default title offset is incorrect
_images/projection_4_0.svg

Geographic projections

To specify a geographic projection, pass proj='name' or e.g. proj={1:'name'} to subplots where 'name' is any valid PROJ.4 projection name listed in the Proj table. This generates a GeoAxes or BasemapAxes, depending on whether you passed basemap=True to subplots. These axes are designed as follows.

  • GeoAxes joins the cartopy GeoAxes class with the ProPlot Axes class, and adds a format command. It includes all the methods you normally get with cartopy, like set_extent.

  • BasemapAxes redirects the plot, scatter, contour, contourf, pcolor, pcolormesh, quiver, streamplot, and barb methods to identically named methods on the Basemap instance, and provides access to Basemap geographic plotting commands like fillcontinents via the format command.

So, with ProPlot, you no longer have to invoke verbose cartopy CRS classes like LambertAzimuthalEqualArea, or mess about with a separate Basemap instance.

To pass keyword args to Basemap and cartopy.crs.Projection classes, use the proj_kw dictionary. ProPlot lets you supply native PROJ.4 keyword names to the cartopy.crs.Projection classes, e.g. lon_0 instead of central_longitude. This makes things a bit less verbose. ProPlot also lets you draw Basemap projections without having to always specify the projection settings by applying sensible defaults.

[2]:
import proplot as plot
plot.rc.coastlinewidth = plot.rc.linewidth = 0.8
f, axs = plot.subplots(ncols=2, axwidth=2.5, proj='robin', proj_kw={'lon_0':180})
axs.format(suptitle='Simple projection axes demo', coast=True, latlines=30, lonlines=60)
/home/docs/checkouts/readthedocs.org/user_builds/proplot/conda/v0.2.1/lib/python3.7/site-packages/cartopy/io/__init__.py:260: ProPlotWarning: Downloading: http://naciscdn.org/naturalearth/110m/physical/ne_110m_coastline.zip
_images/projection_7_1.svg
[3]:
import proplot as plot
f, axs = plot.subplots(hratios=(1.5,1,1,1,1),
    basemap={(1,3,5,7,9):False, (2,4,6,8,10):True},
    proj={(1,2):'mill', (3,4):'cyl', (5,6):'moll', (7,8):'sinu', (9,10):'npstere'},
    ncols=2, nrows=5) #, proj_kw={'lon_0':0})
axs.format(suptitle='Complex projection axes demo')
axs.format(coast=True, latlines=30, lonlines=60)
axs[:,1].format(labels=True, lonlines=plot.arange(-180,179,60))
axs.format(collabels=['Cartopy examples', 'Basemap examples'])
Warning: Cannot label meridians on Mollweide basemapWarning: Cannot label meridians on Sinusoidal basemap
_images/projection_8_1.svg

Included projections

The available cartopy projections are plotted below. See the Proj documentation for details. ProPlot adds to cartopy the previously unavailable Aitoff, Hammer, Winkel Tripel, and Kavrisky VII projections (i.e. 'aitoff', 'hammer', 'wintri', and 'kav7'), as well as North Polar and South Polar versions of the Azimuthal Equidistant, Lambert Azimuthal Equal-Area, and Gnomic projections (i.e. 'npaeqd', 'spaeqd', 'nplaea', 'splaea', 'npgnom', and 'spgnom'), adding to the existing SouthPolarStereo and NorthPolarStereo projections.

The available basemap projections are plotted below. See the Proj documentation for details. Basemap projection bounds are usually rectangles, while cartopy bounds are more flexible. Basemap used to have many more projections than cartopy, but the ProPlot additions to cartopy have made the matchup more even.

[4]:
import proplot as plot
import numpy as np
projs = ['cyl', 'merc', 'mill', 'lcyl', 'tmerc',
         'robin', 'hammer', 'moll', 'kav7', 'aitoff', 'wintri', 'sinu',
         'geos', 'ortho', 'nsper', 'aea', 'eqdc', 'lcc', 'gnom',
         'npstere', 'nplaea', 'npaeqd', 'npgnom', 'igh',
         'eck1', 'eck2', 'eck3', 'eck4', 'eck5', 'eck6']
f, axs = plot.subplots(ncols=3, nrows=10, proj=projs)
axs.format(land=True, reso='lo', labels=False, suptitle='Table of cartopy projections')
for proj,ax in zip(projs,axs):
    ax.format(title=proj, titleweight='bold', labels=False)
/home/docs/checkouts/readthedocs.org/user_builds/proplot/conda/v0.2.1/lib/python3.7/site-packages/cartopy/io/__init__.py:260: ProPlotWarning: Downloading: http://naciscdn.org/naturalearth/110m/physical/ne_110m_land.zip
/home/docs/checkouts/readthedocs.org/user_builds/proplot/conda/v0.2.1/lib/python3.7/site-packages/cartopy/mpl/feature_artist.py:163: ProPlotWarning: Unable to determine extent. Defaulting to global.
_images/projection_12_1.svg
[5]:
import proplot as plot
import numpy as np
projs = ['cyl', 'merc', 'mill', 'cea', 'gall', 'sinu',
         'eck4', 'robin', 'moll', 'kav7', 'hammer', 'mbtfpq',
         'geos', 'ortho', 'nsper',
         'vandg', 'aea', 'eqdc', 'gnom', 'cass', 'lcc',
         'npstere', 'npaeqd', 'nplaea']
f, axs = plot.subplots(ncols=3, nrows=8, basemap=True, proj=projs)
axs.format(land=True, labels=False, suptitle='Table of basemap projections')
for proj,ax in zip(projs,axs):
    ax.format(title=proj, titleweight='bold', labels=False)
_images/projection_13_0.svg

Zooming into projections

To zoom into cartopy projections, you can use set_extent, or alternatively pass lonlim, latlim, or boundinglat to format. Note that ProPlot always draws a circular boundary around North Polar and South Polar Stereographic, Azimuthal Equidistant, Lambert Azimuthal Equal-Area, and Gnomic projections, no matter the “zoom” setting (implemented as with this example).

For basemap projections, you must set the limits when declaring the projection by passing proj_kw to subplots with any of the boundinglat, llcrnrlon, llcrnrlat, urcrnrlon, urcrnrlat, llcrnrx, llcrnry, urcrnrx, urcrnry, width, and/or height keyword args.

[6]:
import proplot as plot
f, axs = plot.subplots(nrows=2, axwidth=4.5,
           proj='pcarree', basemap={1:False, 2:True},
           proj_kw={2:{'llcrnrlon':-20, 'llcrnrlat':-10, 'urcrnrlon':180, 'urcrnrlat':50}})
# Ordinary projection
axs.format(land=True, labels=True, lonlines=20, latlines=20, suptitle='Zooming into projections')
axs[0].format(lonlim=(-140,60), latlim=(-10,50), labels=True, title='Cartopy example')
axs[1].format(title='Basemap example')
# Polar projection
f, axs = plot.subplots(ncols=2, axwidth=2.2,
           proj={1:'splaea', 2:'npaeqd'}, basemap={1:False,2:True},
           proj_kw={2:{'boundinglat':60}})
axs.format(land=True, latlines=10, latmax=80, suptitle='Zooming into polar projections')
axs[0].format(boundinglat=-60, title='Cartopy example')
axs[1].format(title='Basemap example')
# Example from basemap website
f, axs = plot.subplots(ncols=2, axwidth=2, proj='lcc', basemap={1:False,2:True},
             proj_kw={1:{'lon_0':0}, 2:{'lon_0':-100, 'lat_0':45, 'width':8e6, 'height':8e6}})
axs.format(suptitle='Zooming into specific regions', land=True)
axs[0].format(title='Cartopy example', land=True, lonlim=(-20,50), latlim=(30,70))
axs[1].format(title='Basemap example', land=True)
_images/projection_16_0.svg
_images/projection_16_1.svg
_images/projection_16_2.svg

Plotting geophysical data

The below demonstrates how to plot geophysical data with ProPlot. For GeoAxes plotting methods, transform=crs.PlateCarree() is now the default behavior. For BasemapAxes plotting methods, latlon=True is now the default behavior, and methods are called on the axes instead of the Basemap instance. For both basemap and cartopy projections, you can also pass globe=True to 2D plotting commands to ensure global data coverage.

These features are powered by the standardize_2d, default_transform, and default_latlon wrappers.

[7]:
import proplot as plot
import numpy as np
offset = -40
x = plot.arange(offset, 360 + offset-1, 60)
y = plot.arange(-60,60+1,30)
state = np.random.RandomState(51423)
data = np.random.rand(len(y), len(x))
titles = ('Geophysical data demo', 'Global coverage demo')
for globe in (False,True,):
    f, axs = plot.subplots(ncols=2, nrows=2, axwidth=2.5,
        proj='kav7', basemap={(1,3):False, (2,4):True})
    for i,ax in enumerate(axs):
        cmap = ('sunset', 'sunrise')[i % 2]
        if i < 2:
            m = ax.contourf(x, y, data, cmap=cmap, globe=globe, extend='both')
            f.colorbar(m, loc='b', span=i+1, label='values', tickminor=False, extendsize='1.7em')
        else:
            ax.pcolor(x, y, data, cmap=cmap, globe=globe, extend='both')
        if globe:
            continue
        ix = offset + np.linspace(0, 360, 20)
        for cmd in (np.sin,np.cos):
            iy = cmd(ix*np.pi/180)*60
            ax.plot(ix, iy, color='k', lw=0, marker='o')
    axs.format(suptitle=titles[globe],
               collabels=['Cartopy example', 'Basemap example'],
               rowlabels=['Contourf', 'Pcolor'], latlabels='r', lonlabels='b', lonlines=90,
               abc=True, abcstyle='a)', abcloc='ul', abcborder=False)
/home/docs/checkouts/readthedocs.org/user_builds/proplot/conda/v0.2.1/lib/python3.7/site-packages/proplot/axes.py:3336: UserWarning: Cannot add gridline labels on cartopy <proplot.projs.KavrayskiyVII object at 0x7fc7289de990> projection.
                    f'Cannot add gridline labels on cartopy {self.projection} '

/home/docs/checkouts/readthedocs.org/user_builds/proplot/conda/v0.2.1/lib/python3.7/site-packages/proplot/axes.py:3341: UserWarning: Cannot add gridline labels on cartopy <proplot.projs.KavrayskiyVII object at 0x7fc7289de990> projection.
                    f'Cannot add gridline labels on cartopy {self.projection} '

/home/docs/checkouts/readthedocs.org/user_builds/proplot/conda/v0.2.1/lib/python3.7/site-packages/proplot/axes.py:3336: UserWarning: Cannot add gridline labels on cartopy <proplot.projs.KavrayskiyVII object at 0x7fc72a9b1728> projection.
                    f'Cannot add gridline labels on cartopy {self.projection} '

/home/docs/checkouts/readthedocs.org/user_builds/proplot/conda/v0.2.1/lib/python3.7/site-packages/proplot/axes.py:3341: UserWarning: Cannot add gridline labels on cartopy <proplot.projs.KavrayskiyVII object at 0x7fc72a9b1728> projection.
                    f'Cannot add gridline labels on cartopy {self.projection} '

/home/docs/checkouts/readthedocs.org/user_builds/proplot/conda/v0.2.1/lib/python3.7/site-packages/proplot/axes.py:3336: UserWarning: Cannot add gridline labels on cartopy <proplot.projs.KavrayskiyVII object at 0x7fc72a9b1d00> projection.
                    f'Cannot add gridline labels on cartopy {self.projection} '

/home/docs/checkouts/readthedocs.org/user_builds/proplot/conda/v0.2.1/lib/python3.7/site-packages/proplot/axes.py:3341: UserWarning: Cannot add gridline labels on cartopy <proplot.projs.KavrayskiyVII object at 0x7fc72a9b1d00> projection.
                    f'Cannot add gridline labels on cartopy {self.projection} '

/home/docs/checkouts/readthedocs.org/user_builds/proplot/conda/v0.2.1/lib/python3.7/site-packages/proplot/axes.py:3336: UserWarning: Cannot add gridline labels on cartopy <proplot.projs.KavrayskiyVII object at 0x7fc7290b4ca8> projection.
                    f'Cannot add gridline labels on cartopy {self.projection} '

/home/docs/checkouts/readthedocs.org/user_builds/proplot/conda/v0.2.1/lib/python3.7/site-packages/proplot/axes.py:3341: UserWarning: Cannot add gridline labels on cartopy <proplot.projs.KavrayskiyVII object at 0x7fc7290b4ca8> projection.
                    f'Cannot add gridline labels on cartopy {self.projection} '

_images/projection_19_1.svg
_images/projection_19_2.svg

Customizing projections

GeoAxes and BasemapAxes both derive from ProjAxes, which provides a format method. format can be used to draw gridlines, add gridline labels, set gridline label locations, modify the projection bounding box, and add and stylize geographic features, like land masses, coastlines, and international borders. These methods also call format on Axes, and so can be used for subplot titles, a-b-c labels, and figure titles as before.

[8]:
import proplot as plot
import numpy as np
N = 40
f, axs = plot.subplots([[1,1,2],[3,3,3]], axwidth=4, proj={1:'robin', 2:'ortho', 3:'wintri'})
ax = axs[0]
ax.format(title='Robinson map', land=True, landcolor='navy blue', facecolor='pale blue',
           coastcolor='gray5', borderscolor='gray5', innerborderscolor='gray5',
           geogridlinewidth=1.5, geogridcolor='gray8', geogridalpha=1,
           coast=True, innerborders=True, borders=True)
ax = axs[1]
ax.format(title='Ortho map', reso='med', land=True, coast=True, latlines=10, lonlines=15,
          landcolor='mushroom', suptitle='Projection axes formatting demo',
          facecolor='petrol', coastcolor='charcoal', coastlinewidth=0.8, geogridlinewidth=1)
ax = axs[2]
ax.format(land=True, facecolor='ocean blue', landcolor='almond', title='Winkel tripel map',
         lonlines=60, latlines=15)
axs.format(suptitle='Projection axes formatting demo', collabels=['col 1', 'col 2'],
           abc=True, abcstyle='A.', abcloc='ul', abcborder=False, linewidth=1.5)
/home/docs/checkouts/readthedocs.org/user_builds/proplot/conda/v0.2.1/lib/python3.7/site-packages/cartopy/io/__init__.py:260: ProPlotWarning: Downloading: http://naciscdn.org/naturalearth/110m/cultural/ne_110m_admin_0_boundary_lines_land.zip
/home/docs/checkouts/readthedocs.org/user_builds/proplot/conda/v0.2.1/lib/python3.7/site-packages/cartopy/io/__init__.py:260: ProPlotWarning: Downloading: http://naciscdn.org/naturalearth/110m/cultural/ne_110m_admin_1_states_provinces_lakes.zip
/home/docs/checkouts/readthedocs.org/user_builds/proplot/conda/v0.2.1/lib/python3.7/site-packages/cartopy/io/__init__.py:260: ProPlotWarning: Downloading: http://naciscdn.org/naturalearth/50m/physical/ne_50m_land.zip
/home/docs/checkouts/readthedocs.org/user_builds/proplot/conda/v0.2.1/lib/python3.7/site-packages/cartopy/io/__init__.py:260: ProPlotWarning: Downloading: http://naciscdn.org/naturalearth/50m/physical/ne_50m_coastline.zip
_images/projection_22_1.svg