This example shows how to overlay data and graphics in different projections, demonstrating various features of Iris, Cartopy and matplotlib.
We wish to overlay two datasets, defined on different rotated-pole grids. To display both together, we make a pseudocoloured plot of the first, overlaid with contour lines from the second. We also add some lines and text annotations drawn in various projections.
We plot these over a specified region, in two different map projections.
"""
Plotting in different projections
=================================
This example shows how to overlay data and graphics in different projections,
demonstrating various features of Iris, Cartopy and matplotlib.
We wish to overlay two datasets, defined on different rotated-pole grids.
To display both together, we make a pseudocoloured plot of the first, overlaid
with contour lines from the second.
We also add some lines and text annotations drawn in various projections.
We plot these over a specified region, in two different map projections.
"""
import cartopy.crs as ccrs
import iris
import iris.plot as iplt
import numpy as np
import matplotlib.pyplot as plt
# Define a Cartopy 'ordinary' lat-lon coordinate reference system.
crs_latlon = ccrs.PlateCarree()
def make_plot(projection_name, projection_crs):
# Create a matplotlib Figure.
plt.figure()
# Add a matplotlib Axes, specifying the required display projection.
# NOTE: specifying 'projection' (a "cartopy.crs.Projection") makes the
# resulting Axes a "cartopy.mpl.geoaxes.GeoAxes", which supports plotting
# in different coordinate systems.
ax = plt.axes(projection=projection_crs)
# Set display limits to include a set region of latitude * longitude.
# (Note: Cartopy-specific).
ax.set_extent((-80.0, 20.0, 10.0, 80.0), crs=crs_latlon)
# Add coastlines and meridians/parallels (Cartopy-specific).
ax.coastlines(linewidth=0.75, color='navy')
ax.gridlines(crs=crs_latlon, linestyle='-')
# Plot the first dataset as a pseudocolour filled plot.
maindata_filepath = iris.sample_data_path('rotated_pole.nc')
main_data = iris.load_cube(maindata_filepath)
# NOTE: iplt.pcolormesh calls "pyplot.pcolormesh", passing in a coordinate
# system with the 'transform' keyword: This enables the Axes (a cartopy
# GeoAxes) to reproject the plot into the display projection.
iplt.pcolormesh(main_data, cmap='RdBu_r')
# Overplot the other dataset (which has a different grid), as contours.
overlay_filepath = iris.sample_data_path('space_weather.nc')
overlay_data = iris.load_cube(overlay_filepath, 'total electron content')
# NOTE: as above, "iris.plot.contour" calls "pyplot.contour" with a
# 'transform' keyword, enabling Cartopy reprojection.
iplt.contour(overlay_data, 20,
linewidths=2.0, colors='darkgreen', linestyles='-')
# Draw a margin line, some way in from the border of the 'main' data...
# First calculate rectangle corners, 7% in from each corner of the data.
x_coord, y_coord = main_data.coord(axis='x'), main_data.coord(axis='y')
x_start, x_end = np.min(x_coord.points), np.max(x_coord.points)
y_start, y_end = np.min(y_coord.points), np.max(y_coord.points)
margin = 0.07
margin_fractions = np.array([margin, 1.0 - margin])
x_lower, x_upper = x_start + (x_end - x_start) * margin_fractions
y_lower, y_upper = y_start + (y_end - y_start) * margin_fractions
box_x_points = x_lower + (x_upper - x_lower) * np.array([0, 1, 1, 0, 0])
box_y_points = y_lower + (y_upper - y_lower) * np.array([0, 0, 1, 1, 0])
# Get the Iris coordinate sytem of the X coordinate (Y should be the same).
cs_data1 = x_coord.coord_system
# Construct an equivalent Cartopy coordinate reference system ("crs").
crs_data1 = cs_data1.as_cartopy_crs()
# Draw the rectangle in this crs, with matplotlib "pyplot.plot".
# NOTE: the 'transform' keyword specifies a non-display coordinate system
# for the plot points (as used by the "iris.plot" functions).
plt.plot(box_x_points, box_y_points, transform=crs_data1,
linewidth=2.0, color='white', linestyle='--')
# Mark some particular places with a small circle and a name label...
# Define some test points with latitude and longitude coordinates.
city_data = [('London', 51.5072, 0.1275),
('Halifax, NS', 44.67, -63.61),
('Reykjavik', 64.1333, -21.9333)]
# Place a single marker point and a text annotation at each place.
for name, lat, lon in city_data:
plt.plot(lon, lat, marker='o', markersize=7.0, markeredgewidth=2.5,
markerfacecolor='black', markeredgecolor='white',
transform=crs_latlon)
# NOTE: the "plt.annotate call" does not have a "transform=" keyword,
# so for this one we transform the coordinates with a Cartopy call.
at_x, at_y = ax.projection.transform_point(lon, lat,
src_crs=crs_latlon)
plt.annotate(
name, xy=(at_x, at_y), xytext=(30, 20), textcoords='offset points',
color='black', backgroundcolor='white', size='large',
arrowprops=dict(arrowstyle='->', color='white', linewidth=2.5))
# Add a title, and display.
plt.title('A pseudocolour plot on the {} projection,\n'
'with overlaid contours.'.format(projection_name))
iplt.show()
def main():
# Enable a future option, to ensure that the netcdf load works the same way
# as in future Iris versions.
iris.FUTURE.netcdf_promote = True
# Demonstrate with two different display projections.
make_plot('Equidistant Cylindrical', ccrs.PlateCarree())
make_plot('North Polar Stereographic', ccrs.NorthPolarStereo())
if __name__ == '__main__':
main()
(png)
(png)