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()
Related
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)
I am trying to create a plot with matplotlib that includes several different lines, offset artificially, that are coloured according to the RedBlue colourmap mpl.cm.RdBu. Now I want an arrow next to the plot that acts as an effective colourscale, meaning that it should have a colour gradient.
So far, I have managed to create the arrow itself using annotate with the help of this answer and drew a "Rainbow arrow" inside the plot using this brilliant answer (Note: You will need matplotlib 2.2.4 or older to run this part of the code, see comments.).
This is the MWE I can produce so far:
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.transforms
import matplotlib.path
from matplotlib.collections import LineCollection
# from https://stackoverflow.com/questions/47163796/using-colormap-with-annotate-arrow-in-matplotlib
def rainbowarrow(ax, start, end, cmap="viridis", n=50,lw=3):
cmap = plt.get_cmap(cmap,n)
# Arrow shaft: LineCollection
x = np.linspace(start[0],end[0],n)
y = np.linspace(start[1],end[1],n)
points = np.array([x,y]).T.reshape(-1,1,2)
segments = np.concatenate([points[:-1],points[1:]], axis=1)
lc = LineCollection(segments, cmap=cmap, linewidth=lw)
lc.set_array(np.linspace(0,1,n))
ax.add_collection(lc)
# Arrow head: Triangle
tricoords = [(0,-0.4),(0.5,0),(0,0.4),(0,-0.4)]
angle = np.arctan2(end[1]-start[1],end[0]-start[0])
rot = matplotlib.transforms.Affine2D().rotate(angle)
tricoords2 = rot.transform(tricoords)
tri = matplotlib.path.Path(tricoords2, closed=True)
ax.scatter(end[0],end[1], c=1, s=(2*lw)**2, marker=tri, cmap=cmap,vmin=0)
ax.autoscale_view()
def plot_arrow(data,n):
fig, subfig = plt.subplots(1,1,figsize=(6.28,10)) # plotsize, etc
colorP=mpl.cm.RdBu(0.2)
i = 0
while i<=n-1:
subfig.plot(data[i,0], (data[i,1])+i, lw=2, color=mpl.cm.RdBu(1-i/20)) # plot of data
i=i+1
subfig.annotate('', xy=(1.1,0), xycoords='axes fraction', xytext=(1.1,1),
arrowprops=dict(arrowstyle="<-", lw = 3))
subfig.annotate('A', xy=(1.1,0), xycoords='axes fraction', xytext=(1.1,1))
subfig.annotate('B', xy=(1.1,0), xycoords='axes fraction', xytext=(1.1,0))
rainbowarrow(subfig, (1.1,3), (1.1,5), cmap='RdBu_r', n=100,lw=3)
plt.show(fig)
plt.close(fig)
# things to plot
np.random.seed(19680802)
n = 20
i = 0
data = np.empty([n,2,10])
while i<=n-1:
data[i]=np.sin(np.random.rand(10))
i = i+1
# actual plot
plot_arrow(data,n)
Here's the graph generated:
In a nutshell: I want the annotation arrow outside the plot to have the colour of the colourmap, as the small rainbow arrow inside the plot.
import matplotlib
import numpy as np
from matplotlib.patches import Circle
import matplotlib.pyplot as plt
matplotlib.rcParams["figure.figsize"]=(6.4, 4.8)
fig, ax = plt.subplots()
circle1 = Circle((0.1, 0.1), 0.2, facecolor = "k", edgecolor = 'red', linewidth = 30)
circle2 = Circle((0.5, 0.5), 0.2, facecolor = "k")
ax.axis("equal")
ax.add_artist(circle1);
ax.add_artist(circle2);
plt.show()
When I run the above code, which tries to draw 2 circles, the patches overflow outside the visible area. How can I fit both circles into view ?
First of all, to add a patch to an axes, use ax.add_patch().
Then to make sure the axes is scaled according to its content, use ax.autoscale()
ax.add_artist(circle1)
ax.add_artist(circle2)
ax.autoscale()
For visual effect purpose, I wish I could remove the grids outside the circle and only keep those are within the circle.
Btw, how to fulfill the cell ([8,9],[9,10]) with red color, I mean, the cell on the right of x=8 and down y=9.
My code is below and current image is also attached.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.transforms import BlendedGenericTransform
fig, ax = plt.subplots()
ax.text(0, -0.02, 'y', transform=BlendedGenericTransform(ax.transData, ax.transAxes), ha='center')
ax.text(1.01, 0, 'x', transform=BlendedGenericTransform(ax.transAxes, ax.transData), va='center')
ax.set_xticks(np.arange(0,side+1,1))
ax.set_yticks(np.arange(0,side+1,1))
plt.grid()
ax.xaxis.tick_top()
plt.gca().invert_yaxis()
circle = plt.Circle((15, 15), radius=15, fc='w')
plt.gca().add_patch(circle)
fig.set_size_inches(18.5, 10.5)
The trick is to set the clip_path property on the gridline artists
Here's a simplified (minimal) example:
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
# draw the circle
circle = plt.Circle((15, 15), radius=15, fc='w')
ax.add_patch(circle)
# settings for the axes
ax.grid()
ax.set_xlim(0,30)
ax.set_ylim(0,30)
ax.set_aspect(1)
# clip the gridlines
plt.setp(ax.xaxis.get_gridlines(), clip_path=circle)
plt.setp(ax.yaxis.get_gridlines(), clip_path=circle)
plt.show()
Result:
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!