Rotate polar stereographic subplot - python

I am making a figure with subplots which are north polar stereographic projections. I also created a custom boundary shape to display subplot as a circle. However once reprojected, I want to be able to rotate the map, since my data is focusing on the US and thus I was hoping that each subplot would have the US facing "up," thus I would need to rotate it 270 degrees / -90 degrees.
Minimalistic code example pulled from cartopy example
import cartopy.crs as ccrs
import cartopy.feature as cfeature
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline # for notebook
map_projection = ccrs.NorthPolarStereo(central_longitude=0, )
data_projection = ccrs.PlateCarree()
theta = np.linspace(0, 2*np.pi, 100)
center, radius = [0.5, 0.5], 0.5 # by changing radius we can zoom in/out
verts = np.vstack([np.sin(theta), np.cos(theta)]).T
circle = mpl.path.Path(verts * radius + center)
plot_extent=[-179.9,180, 30, 90]
fig, ax1 = plt.subplots(1,1, figsize=(6,6), dpi=100, subplot_kw=dict(projection=map_projection))
ax1.set_boundary(circle, transform=ax1.transAxes)
ax1.coastlines(linewidths=1.0, color='grey')
ax1.add_feature(cfeature.BORDERS, linestyles='--', color='dimgrey', linewidths=0.8 )
ax1.set_extent(plot_extent, crs=ccrs.PlateCarree(),)
gl = ax1.gridlines(crs=ccrs.PlateCarree(), draw_labels=True,
linewidth=1, color='gray', alpha=0.5, linestyle='--', zorder=10)
I haven't yet found any good examples or documentation for what I am trying to do, however I am new to using matplotlib/cartopy.

You need to set central_longitude=-90.
So:
map_projection = ccrs.NorthPolarStereo(central_longitude=-90)

Related

How to zoom into a specific latitude in cartopy.crs.Orthographic?

I'm unsure if this is possible, but I'm essentially trying to isolate the Arctic circle latitude (60N) in an orthographic map AND maintain the ellipsoid, not have the zoomed in image be a rectangle/square.
Here is what I have:
fig = plt.figure(figsize=[20, 10])
ax1 = plt.subplot(1, 1, 1, projection=ccrs.Orthographic(0, 90))
for ax in [ax1]:
ax.coastlines(zorder=2)
ax.stock_img()
ax.gridlines()
This gives the north polar view I want, but I would like for it to stop at 60N.
Current yield
To get a zoom-in and square extent of an orthographic map, You need to plot some control points (with .scatter, for example) or specify correct extent in projection coordinates (more difficult). Here is the code to try.
import cartopy
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
fig = plt.figure(figsize=[8, 8])
lonlatproj = ccrs.PlateCarree()
my_projn = ccrs.Orthographic(central_longitude=0,central_latitude=90)
ax1 = plt.subplot(1, 1, 1, projection=my_projn)
# set `lowlat` as lower limits of latitude to plot some points
# these points will determine the plot extents of the map
lowlat = 60 + 2.8 # and get 60
lons, lats = [-180,-90,0,90], [lowlat,lowlat,lowlat,lowlat]
# plot invisible points to get map extents
ax1.scatter(lons, lats, s=0, color='r', transform=lonlatproj)
#ax1.stock_img() #uncomment to get it plotted
ax1.coastlines(lw=0.5, zorder=2)
ax1.gridlines(lw=2, ec='black', draw_labels=True)
Method 2: By specifying correct extent in projection coordinates
import cartopy
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
fig = plt.figure(figsize=[8, 8])
lonlatproj = ccrs.PlateCarree()
my_projn = ccrs.Orthographic(central_longitude=0,central_latitude=90)
ax1 = plt.subplot(1, 1, 1, projection=my_projn)
# These 2 lines of code grab extents in projection coordinates
_, y_min = my_projn.transform_point(0, 60, lonlatproj) #(0.0, -3189068.5)
x_max, _ = my_projn.transform_point(90, 60, lonlatproj) #(3189068.5, 0)
# prep extents of the axis to plot map
pad = 25000
xmin,xmax,ymin,ymax = y_min-pad, x_max+pad, y_min-pad, x_max+pad
# set extents with prepped values
ax1.set_extent([xmin,xmax,ymin,ymax], crs=my_projn) # data/projection coordinates
ax1.stock_img()
ax1.coastlines(lw=0.5, zorder=2)
# plot other layers of data here using proper values of zorder
# finally, plot gridlines
ax1.gridlines(draw_labels=True, x_inline=False, y_inline=True,
color='k', linestyle='dashed', linewidth=0.5)
plt.show()
Method 3 Plot the map with circular boundary
The runnable code:
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
import matplotlib.path as mpath
import numpy as np
r_limit = 3214068.5 #from: ax.get_ylim() of above plot
# some settings
lonlatproj = ccrs.PlateCarree()
my_projn = ccrs.Orthographic(central_longitude=0, central_latitude=90)
fig = plt.figure(figsize=[8, 8])
ax = plt.subplot(1, 1, 1, projection=my_projn)
# add bluemarble image
ax.stock_img()
# add coastlines
ax.coastlines(lw=0.5, color="black", zorder=20)
# draw graticule (of meridian and parallel lines)
gls = ax.gridlines(draw_labels=True, crs=ccrs.PlateCarree(), lw=3, color="gold",
y_inline=True, xlocs=range(-180,180,30), ylocs=range(-80,91,10))
# add extra padding to the plot extents
r_extent = r_limit*1.0001
ax.set_xlim(-r_extent, r_extent)
ax.set_ylim(-r_extent, r_extent)
# Prep circular boundary
circle_path = mpath.Path.unit_circle()
circle_path = mpath.Path(circle_path.vertices.copy() * r_limit,
circle_path.codes.copy())
#set circle boundary
ax.set_boundary(circle_path)
#hide frame
ax.set_frame_on(False) #hide the rectangle frame
plt.show()

Reorient Matplotlib polar plot

I would like to produce a polar scatterplot in matplotlib. The plot I have from using ax1 = plt.subplot(111, polar=True) looks fine, but I need to deviate from the usual polar graph orientation.
I need 0 degrees to point straight up (rotation).
I need 90 degrees to point right (mirror image).
(How) Can I do this?
You need ax.set_theta_zero_location and ax.set_theta_direction.
For details, see the doc
import matplotlib.pyplot as plt
import numpy as np
r = range(360)
angles = [i * np.pi / 180 for i in r]
f = plt.figure()
ax = plt.subplot(polar=True)
plt.polar(angles, r)
ax.set_xticks(angles[::10])
ax.set_theta_zero_location("N")
ax.set_theta_direction(-1)
plt.show()

Issue w/ image crossing dateline in imshow & cartopy

I'm trying to plot a square grid of equally-spaced (in lat/lon) data using cartopy, matplotlib, and imshow. The data crosses the dateline, and I've had issues getting a map to work properly.
Here's an example of my issue:
import numpy as np
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
lat = np.arange(6000)*0.02 + (-59.99)
lon = np.arange(6000)*0.02 + (85.01)
dat = np.reshape(np.arange(6000*6000),[6000,6000])
tran = ccrs.PlateCarree()
proj = tran
plt.figure(figsize=(8,8))
ax = plt.axes(projection=proj)
print([lon[0],lon[-1],lat[0],lat[-1]])
ax.imshow(dat, extent=[lon[0],lon[-1],lat[0],lat[-1]],transform=tran,interpolation='nearest')
ax.coastlines(resolution='50m', color='black', linewidth=2)
ax.gridlines(crs=proj,draw_labels=True)
plt.show()
tran = ccrs.PlateCarree(central_longitude=180)
proj = tran
plt.figure(figsize=(8,8))
ax = plt.axes(projection=proj)
print([lon[0]-180,lon[-1]-180,lat[0],lat[-1]])
ax.imshow(dat, extent=[lon[0]-180,lon[-1]-180,lat[0],lat[-1]],transform=tran,interpolation='nearest')
ax.coastlines(resolution='50m', color='black', linewidth=2)
ax.gridlines(crs=tran,draw_labels=True)
plt.show()
The first plot yields this image, chopping off at 180E:
The second fixes the map issue, but the grid ticks are now wrong:
I've tried reprojecting, I think (where tran != proj), but it seemingly either hung or was taking too long.
I basically want the bottom image, but with the proper labels. I'm going to have more geolocated data to overplot, so I'd like to do it correctly instead of what seems like a hack right now.
With Cartopy, drawing a map crossing dateline is always challenging. Here is the code that plots the map you want.
import numpy as np
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
# demo raster data
n1 = 300
m1 = 0.4
lat = np.arange(n1)*m1 + (-59.99)
lon = np.arange(n1)*m1 + (85.01)
dat = np.reshape(np.arange(n1*n1), [n1,n1])
cm_lon=180 # for central meridian
tran = ccrs.PlateCarree(central_longitude = cm_lon)
proj = tran
plt.figure(figsize=(8,8))
ax = plt.axes(projection=proj)
ext = [lon[0]-cm_lon, lon[-1]-cm_lon, lat[0], lat[-1]]
#print(ext)
ax.imshow(dat, extent=ext, \
transform=tran, interpolation='nearest')
ax.coastlines(resolution='110m', color='black', linewidth=0.5, zorder=10)
# this draws grid lines only, must go beyond E/W extents
ax.gridlines(draw_labels=False, xlocs=[80,100,120,140,160,180,-180,-160,-140])
# this draw lables only, exclude those outside E/W extents
ax.gridlines(draw_labels=True, xlocs=[100,120,140,160,180,-160])
plt.show()
The resulting map:

Python/Matplotlib Inverse Fill of Polar Plot Polygon

Currently I have the following script that generates a polar plot of azimuth/radius data. "R1" is simple a list of values of [azimuth, inclination] derived from a table in ArcGIS.
import matplotlib.pyplot as plt
import numpy as np
for(a,r) in R1:
angles.append(a)
radius.append(90-r)
theta = np.radians(angles)
r = radius
ax = plt.subplot(111,polar=True)
ax.plot(theta, r, color='black', ls='-', linewidth=1)
ax.fill(theta,r,'0.75') ## should I use ax.fill_betweenx() ?
ax.set_theta_zero_location('N')
ax.set_theta_direction(-1)
ax.set_rmax(90)
ax.set_rmin(0)
ax.set_yticks(range(0,90,10))
yLabel=['90','','','60','','','30','','','']
ax.set_yticklabels(yLabel)
ax.grid(True)
plt.show()
At the moment this creates the following plot:
How can I "invert" the fill so that what is filled with gray will be white, and what is white will be gray?
I have tried ax.fill_betweenx(theta,90,r,color='0.75') and that didn't work. I have been battling with this for some time now to no avail.
ANY help or suggestions are greatly appreciated!
If there is any way I can make this clearer, please let me know in the comments.
Depending on what you want to do with this later, the quickest way is to simply make the background gray and the fill white:
import matplotlib.pyplot as plt
import numpy as np
ax = plt.subplot(111, polar=True)
theta = np.linspace(0, 2*np.pi, 100)
r = 2 + np.sin(theta * 2)
ax.patch.set_facecolor('0.5')
ax.plot(theta, r, color='black', ls='-', linewidth=1)
ax.fill(theta,r,'w')
plt.show()
plt.draw() # just to be safe!

How do I draw a rectangle on the legend in matplotlib?

I am trying to plot a rectangle onto the legend in matplotlib.
To illustrate how far I have gotten I show my best attempt, which does NOT work:
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
import numpy as np
Fig = plt.figure()
ax = plt.subplot(111)
t = np.arange(0.01, 10.0, 0.01)
s1 = np.exp(t)
ax.plot(t, s1, 'b-', label = 'dots')
leg = ax.legend()
rectangle = Rectangle((leg.get_frame().get_x(),
leg.get_frame().get_y()),
leg.get_frame().get_width(),
leg.get_frame().get_height(),
fc = 'red'
)
ax.add_patch(rectangle)
plt.show()
The rectangle just isn't draw anywhere in the figure.
If I look at the values of leg.get_frame().get_x(), leg.get_frame().get_y()), leg.get_frame().get_width() and leg.get_frame().get_height(), I see that they are
0.0, 0.0, 1.0 and 1.0 respectively.
My problem thus sees to be, to find the co-ordinates of the frame of the legend.
It would be really great if you could help me out.
Thanks for reading this far.
This link may have the exact thing you are looking for.
http://matplotlib.org/users/legend_guide.html#creating-artists-specifically-for-adding-to-the-legend-aka-proxy-artists
import matplotlib.patches as mpatches
import matplotlib.pyplot as plt
red_patch = mpatches.Patch(color='red', label='The red data')
plt.legend(handles=[red_patch])
plt.show()
The trouble is that the position of the legend is not known in advance. Only by the time you render the figure (calling plot()), is the position decided.
A solution I came across is to draw the figure twice. In addition, I've used axes coordinates (default is data coordinates) and scaled the rectangle so you still see a bit of the legend behind it. Note that I had to set the legend and rectangle zorder as well; the legend gets drawn later than the rectangle and thus the rectangle otherwise disappears behind the legend.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
Fig = plt.figure()
ax = plt.subplot(111)
t = np.arange(0.01, 10.0, 0.01)
s1 = np.exp(t)
ax.plot(t, s1, 'b-', label = 'dots')
leg = ax.legend()
leg.set_zorder(1)
plt.draw() # legend position is now known
bbox = leg.legendPatch.get_bbox().inverse_transformed(ax.transAxes)
rectangle = Rectangle((bbox.x0, bbox.y0),
bbox.width*0.8, bbox.height*0.8,
fc='red', transform=ax.transAxes, zorder=2)
ax.add_patch(rectangle)
plt.show()

Categories

Resources