Fit circle patch overflowing outside view in Matplotlib? - python

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()

Related

Smooth color change animation of patch in Matplotlib

import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.patches import Circle
from matplotlib import animation
matplotlib.rcParams["figure.figsize"]=(10, 5)
fig, ax = plt.subplots()
ax.axis("equal")
ax.set_xlim(-2, 2)
ax.set_ylim(-1, 1)
def animate(i):
circle1 = Circle((0.1, 0.1), 0.2, color = '#' + str(i)*6)
circle2 = Circle((0.5, 0.5), 0.2, color = '#ffff00')
ax.add_artist(circle1);
ax.add_artist(circle2);
anim = animation.FuncAnimation(fig, animate, frames=9, interval=1000, blit=True)
plt.show()
Inside my animate function, i am trying a smooth transition from black to grey for my first circle patch. However, it doesn't work. Can someone please help? Also, I need a smooth border animation too. Is it possible to have seperate face color and border color animations for same patch? If yes, then how ?

Avoid overlapping colorbar in xarray facet grid plot

import xarray as xr
import cartopy.crs as ccrs
USA_PROJ = ccrs.AlbersEqualArea(central_longitude=-97., central_latitude=38.)
g_simple = ds_by_month.t2m.plot(x='longitude',
y='latitude',
col='month',
col_wrap=6,
aspect=ds.dims['longitude'] / ds.dims['latitude'],
subplot_kws=dict(projection=USA_PROJ),
add_colorbar=False,
transform=ccrs.PlateCarree())
g_simple.add_colorbar(orientation='horizontal')
for ax in g_simple.axes.ravel():
ax.coastlines()
ax.set_extent([-121, -72, 22.5, 50])
plt.tight_layout()
plt.show()
On running the code above, I get the foll. figure:
How do I ensure that the colorbar is not overlapping the plots? the overlap happens even if I use the xarray default colorbar.
You could give the color bar its own set of axes and set the "bottom" value to negative so that it exceeds the bounding box, or otherwise set the subplots_adjust function using a keyword argument (i.e. hspace = 2 etc).
Here's an example with random data below (modified from matplotlib subplots example):
import numpy as np
import matplotlib.pyplot as plt
fig, axes = plt.subplots(nrows=2, ncols=6, figsize=(15,5))
for ax in axes.flat:
im = ax.imshow(np.random.random((10,10)), vmin=0, vmax=1)
# color bar
fig.subplots_adjust(right=0.875) #also try using kwargs bottom, top, or hspace
cbar_ax = fig.add_axes([0.1, -0.1, .8, .05]) #left, bottom, width, height
fig.colorbar(im, cax=cbar_ax, orientation="horizontal")
plt.show()

Python Plot: How to remove grid lines not within the circle?

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:

Scale image in matplotlib without changing the axis

I have a GUI that displays a plot. I want to fit that plot to an existing image. I displayed the image under the plot using:
myaxe.plot(...)
myaxeimage = myaxe.imshow(myimage, axpect='auto', extent=myaxe.axis(), zorder=-1)
I'm already able to play with the opacity of the image, using
myaxeimage.set_alpha()
Now I'd like to be able to zoom in and out and to move around the image, using the GUI, without touching to the existing plot and axes, in order to align it with my plot. In other words, I want to scale to given sx and sy factors, and to put origin of the image at a given (x,y) point, clipping parts of the image going outside the axes. How can I do that?
There is a watermark example distributed with matplotlib that is sort of similar. Starting from that code, we can modify as follows:
Use ax.imshow to plot the image first. I do this because the extent parameter affects the final extent of ax. Since we want the final extent to be governed by the plt.plot(...), let's put it last.
myaximage = ax.imshow(im, aspect='auto', extent=(1,15,0.3,0.7), alpha=0.5, origin='upper', zorder=-1)
Instead of extent=myaxe.axis(), use extent to control the position and size of the image. extent=(1,15,0.3,0.7) places the image in the rectangle with (1, 0.3) as the bottom left corner and (15, 0.7) as the top right corner.
With origin='upper', the [0,0] index of the array im is placed at the upper left corner of the extent. With origin='lower' it would have been placed at the lower left corner.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cbook as cbook
import matplotlib.image as image
np.random.seed(1)
datafile = cbook.get_sample_data('logo2.png', asfileobj=False)
im = image.imread(datafile)
fig, ax= plt.subplots()
myaximage = ax.imshow(im, aspect='auto', extent=(1,15,0.3,0.7), alpha=0.5, zorder=-1)
ax.plot(np.random.rand(20), '-o', ms=20, lw=2, alpha=1.0, mfc='orange')
ax.grid()
plt.show()
If you want to expand the image and clip it to the extent of the plot, you might need to use ax.set_xlim and ax.set_ylim as well:
myaximage = ax.imshow(im, aspect='auto', extent=(-1,25,0.3,0.7), alpha=0.5, zorder=-1,
origin='upper')
ax.plot(np.random.rand(20), '-o', ms=20, lw=2, alpha=1.0, mfc='orange')
ax.set_xlim(0,20)
ax.set_ylim(0,1)
Or, for more control, you can clip the image to an arbitrary path by using myaximage.set_clip_path:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cbook as cbook
import matplotlib.image as image
import matplotlib.patches as patches
np.random.seed(1)
datafile = cbook.get_sample_data('logo2.png', asfileobj=False)
im = image.imread(datafile)
fig, ax= plt.subplots()
myaximage = ax.imshow(im, aspect='auto', extent=(-5,25,0.3,0.7),
alpha=0.5, origin='upper',
zorder=-2)
# patch = patches.Circle((300,300), radius=100)
patch = patches.Polygon([[5, 0.4], [15, 0.4], [15, 0.6], [5, 0.6]], closed=True,
transform=ax.transData)
myaximage.set_clip_path(patch)
ax.plot(np.random.rand(20), '-o', ms=20, lw=2, alpha=1.0, mfc='orange',
zorder=-1)
ax.set_xlim(0, 20)
ax.set_ylim(0, 1)
plt.show()
Finally, I followed tcaswell suggestion and used 2 different axes. This way, I simply have to play with set_xlim() and set_ylim() of my image axes to change the origin and/or the zooming factor of my image. I order to get the image below my plot, without hiding it with the frame of the plot, I removed the frame of the plot and used the frame of the image axes instead. I also hidden the ticks from the image axes.
from matplotlib import pyplot
f = pyplot.figure()
a = f.add_subplot(111, frameon=False) # Remove frame
a.plot(...)
myimg = pyplot.imread(...)
imgaxes = f.add_axes(a.get_position(), # new axes with same position
label='image', # label to ensure imgaxes is different from a
zorder=-1, # put image below the plot
xticks=[], yticks=[]) # remove the ticks
img = imgaxes.imshow(myimg, aspect='auto') # ensure image takes all the place
# now, to modify things
img.set_alpha(...)
imgaxes.set_xlim((x1, x2)) # x1 and x2 must be calculated from
# image size, origin, and zoom factor

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