Reputation: 23
I have a rotated pole projection (taken from the Rapid Refresh model parameters) that I am able to plot correctly in matplotlib-basemap, but can't figure out how to reproduce with cartopy. Here is the Python code using basemap:
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
bm = Basemap(projection = "rotpole",
o_lat_p = 36.0,
o_lon_p = 180.0,
llcrnrlat = -10.590603,
urcrnrlat = 46.591976,
llcrnrlon = -139.08585,
urcrnrlon = 22.661009,
lon_0 = -106.0,
rsphere = 6370000,
resolution = 'l')
fig = plt.figure(figsize=(8,8))
ax = fig.add_axes([0.1,0.1,0.8,0.8])
bm.drawcoastlines(linewidth=.5)
print bm.proj4string
plt.savefig("basemap_map.png")
plt.close(fig)
The proj4 string that prints is:
+o_proj=longlat +lon_0=-106.0 +o_lat_p=36.0 +R=6370000.0 +proj=ob_tran +units=m +o_lon_p=180.0
If I use the RotatedPole projection in cartopy and supply the projection parameters from above, I get an image in the south pole. Here is a snippet (manually typed in from a real example, be warned):
from cartopy import crs
import matplotlib.pyplot as plt
cart = crs.RotatedPole(pole_longitude=180.0,
pole_latitude=36.0,
central_rotated_longitude=-106.0,
globe = crs.Globe(semimajor_axis=6370000,
semiminor_axis=6370000))
fig = plt.figure(figsize=(8,8))
ax = plt.axes([0.1,0.1,0.8,0.8], projection=cart)
ax.set_extent([-139.08585, 22.661009, -10.590603, 46.591976], crs.Geodetic())
plt.savefig("cartopy_map.png")
plt.close(fig)
I've also tried modifying arguments to the RotatedPole class to produce the proj4 parameters from above, and even tried making my own subclass of _CylindricalProjection and setting the proj4 parameters directly in the constructor, but still no luck.
What is the right way in cartopy to produce the same result as basemap?
Here is the basemap image:
Here is what cartopy produces for the above example:
Thanks for your help!
Bill
Upvotes: 2
Views: 4321
Reputation: 21839
There is an attribute available on a cartopy CRS which gives you the proj4 parameters.
from cartopy import crs
rp = crs.RotatedPole(pole_longitude=180.0,
pole_latitude=36.0,
central_rotated_longitude=-106.0,
globe=crs.Globe(semimajor_axis=6370000,
semiminor_axis=6370000))
print(rp.proj4_params)
Gives:
{'a': 6370000, 'o_proj': 'latlon',
'b': 6370000, 'to_meter': 0.017453292519943295,
'ellps': 'WGS84', 'lon_0': 360.0,
'proj': 'ob_tran', 'o_lat_p': 36.0,
'o_lon_p': -106.0}
So it looks like you only need to set the pole longitude and latitude in order to match your desired projection. The important point being that pole longitude is the position of the dateline of the new projection, not its central longitude - from memory, I seem to remember that this is consistent with bodies such as the WMO, but inconsistent with proj.4:
>>> rp = ccrs.RotatedPole(pole_longitude=-106.0 - 180,
pole_latitude=36,
globe=ccrs.Globe(semimajor_axis=6370000,
semiminor_axis=6370000))
>>> print(rp.proj4_params)
{'a': 6370000, 'o_proj': 'latlon', 'b': 6370000, 'to_meter': 0.017453292519943295,
'ellps': 'WGS84', 'lon_0': -106.0, 'proj': 'ob_tran',
'o_lat_p': 36, 'o_lon_p': 0.0}
With all of that in place, the final code might look something like:
import cartopy.crs as ccrs
import cartopy.feature
import matplotlib.pyplot as plt
import numpy as np
rp = ccrs.RotatedPole(pole_longitude=-106.0 - 180,
pole_latitude=36,
globe=ccrs.Globe(semimajor_axis=6370000,
semiminor_axis=6370000))
pc = ccrs.PlateCarree()
ax = plt.axes(projection=rp)
ax.coastlines('50m', linewidth=0.8)
ax.add_feature(cartopy.feature.LAKES,
edgecolor='black', facecolor='none',
linewidth=0.8)
# In order to reproduce the extent, we can't use cartopy's smarter
# "set_extent" method, as the bounding box is computed based on a transformed
# rectangle of given size. Instead, we want to emulate the "lower left corner"
# and "upper right corner" behaviour of basemap.
xs, ys, zs = rp.transform_points(pc,
np.array([-139.08, 22.66]),
np.array([-10.59, 46.59])).T
ax.set_xlim(xs)
ax.set_ylim(ys)
plt.show()
Upvotes: 5