When setting the map extent for a cartopy map that has a projection other than Plate Carree, there are areas visible on the map that are outside of the specified extent. An example of this can be seen in the last image of this jupyter notebook: https://gist.github.com/ajdawson/e75715203148c9b22e436c6017d3c513
With the Lambert Conformal projection, there are areas inside the figure window (not shaded) that fall outside of the specified extent. While map extent in this example was set to [-140, -60, 20, 70], the minimum longitude visible is close to -180 in the top left corner. Essentially, the extent set with set_extent()
is not the true lat/lon extent seen in the image.
I'd like a way to find what this true extent is for every image. This would be the max and min lat and lon visible anywhere in the image. For most projections, this could be done by finding the lat/lon coordinates of the corners of the map, but that method would fail if the poles are visible in the figure (and for any polar projection).
Update 9/23/21
I found that that the cartopy axes has a get_extent()
method that does exactly this!
Example:
import matplotlib.pyplot as plt
import cartopy.crs as ccrs, cartopy.feature as cfeature
fig = plt.figure()
ax = fig.add_subplot(111, projection=ccrs.LambertConformal())
ax.set_extent([-130, -60, 20, 70], ccrs.PlateCarree())
ax.add_feature(cfeature.COASTLINE)
print(ax.get_extent(crs=ccrs.PlateCarree())) # this is the visible map extent, not the same as what was set using ax.set_extent()
Previous answer - a solution that also works
I ended up coming up with a solution that works better for my application than the original question. If I'm plotting gridded data on the map, I convert from data coordinates to map coordinates, then from map coordinates to figure coordinates, and finally from figure coordinates to axes coordinates. If the grid point is within (0:1, 0:1), it will fall within the visible map extent.
import numpy as np
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
lat = np.arange(0,91,1.0)
lon = np.arange(-180,1,1.0)
lons,lats = np.meshgrid(lon,lat)
mapproj = ccrs.LambertConformal() # the map projection
dataproj = ccrs.PlateCarree() # the data projection, PlateCarree for lat/lon data
fig = plt.figure()
ax = fig.add_subplot(111, projection=mapproj)
points = mapproj.transform_points(dataproj, lons, lats)
xpts = points[:,:,0].flatten().tolist()
ypts = points[:,:,1].flatten().tolist()
crs_pts = list(zip(xpts,ypts))
fig_pts = ax.transData.transform(crs_pts)
ax_pts = ax.transAxes.inverted().transform(fig_pts)
x = ax_pts[:,0].reshape(lats.shape)
y = ax_pts[:,1].reshape(lats.shape)
mask = (x>=-0.02) & (x<=1.02) & (y>=-0.02) & (y<=1.02) # add padding for large grid spacings