how to overlay a shapefile in matplotlib - matplotlib

In matplotlib how to overlay the shapefile (available in folder) as attached below at the top right position outside the plot.

The code referenced by banderkat:
import matplotlib.pyplot as plt
import Image
import numpy as np
im = Image.open('Jbc4j.jpg')
width = im.size[0]
height = im.size[1]
# We need a float array between 0-1, rather than
# a uint8 array between 0-255
im = np.array(im).astype(np.float) / 255
a = np.random.randint(0,100,100)
b = range(100)
fig = plt.figure(1,figsize=(5, 7), dpi=80, facecolor='w')
ax = fig.add_subplot(111)
ax.scatter(a,b)
fig.canvas.draw()
# With newer (1.0) versions of matplotlib, you can
# use the "zorder" kwarg to make the image overlay
# the plot, rather than hide behind it... (e.g. zorder=10)
fig.figimage(im, fig.bbox.xmax - width, fig.bbox.ymax - height, zorder=0)
# (Saving with the same dpi as the screen default to
# avoid displacing the logo image)
fig.savefig('temp.png', dpi=80)
plt.show()
Produces the following result (imaged cropped to save space).
Changing the zorder=1 will place the image on top.
Other helpful references:
How to change background color for scatter plot in matplotlib
How do you change the size of figures drawn with matplotlib?
Python/Matplotlib - Change the relative size of a subplot
In Matplotlib, what does the argument mean in fig.add_subplot(111)?
Customizing Location of Subplot Using GridSpec

You can use basemap toolkit to load and plot shapefile. Here I've plotted shapeFile in a separate axes and aligned it to top-right of other axes plot using 'subplot2grid'.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
import matplotlib.gridspec as gridspec
def plotShapeFile():
# Lambert Conformal Conic map.
m = Basemap(llcrnrlon=-100.,llcrnrlat=0.,urcrnrlon=-20.,urcrnrlat=57.,
projection='lcc',lat_1=20.,lat_2=40.,lon_0=-60.,
resolution ='l',area_thresh=1000.)
# read shapefile.
shp_info = m.readshapefile('C:/basemap-1.0.6/basemap-1.0.6/examples/huralll020','hurrtracks',drawbounds=False)
# find names of storms that reached Cat 4.
names = []
for shapedict in m.hurrtracks_info:
cat = shapedict['CATEGORY']
name = shapedict['NAME']
if cat in ['H4','H5'] and name not in names:
# only use named storms.
if name != 'NOT NAMED': names.append(name)
# plot tracks of those storms.
for shapedict,shape in zip(m.hurrtracks_info,m.hurrtracks):
name = shapedict['NAME']
cat = shapedict['CATEGORY']
if name in names:
xx,yy = zip(*shape)
# show part of track where storm > Cat 4 as thick red.
if cat in ['H4','H5']:
m.plot(xx,yy,linewidth=1.5,color='r')
elif cat in ['H1','H2','H3']:
m.plot(xx,yy,color='k')
# draw coastlines, meridians and parallels.
m.drawcoastlines()
m.drawcountries()
m.drawmapboundary(fill_color='#99ffff')
m.fillcontinents(color='#cc9966',lake_color='#99ffff')
m.drawparallels(np.arange(10,70,20),labels=[1,1,0,0])
m.drawmeridians(np.arange(-100,0,20),labels=[0,0,0,1])
if __name__ == '__main__':
fig=plt.figure()
plt.subplots_adjust(wspace=0.001, hspace=0.001)
ax1=plt.subplot2grid((5,5), (0,0), colspan=4, rowspan=4)
labels = 'Frogs', 'Hogs', 'Dogs', 'Logs'
fracs = [15,30,45, 10]
explode=(0, 0.05, 0, 0)
p1,t1,at1 = plt.pie(fracs, explode=explode, labels=labels, autopct='%1.1f%%', shadow=True)
plt.title('Raining Hogs and Dogs', bbox={'facecolor':'0.8', 'pad':5})
ax2=plt.subplot2grid((5,5), (0,4), colspan=1, rowspan=1)
#draw shapeFile on the current active axes, i.e. ax2
plotShapeFile()
plt.tight_layout()
plt.show()
Below are links to references I've used:
http://sourceforge.net/projects/matplotlib/files/matplotlib-toolkits/basemap-1.0.6/
http://matplotlib.org/basemap/users/examples.html
Output:

Related

Align multi-line ticks in Seaborn plot

I have the following heatmap:
I've broken up the category names by each capital letter and then capitalised them. This achieves a centering effect across the labels on my x-axis by default which I'd like to replicate across my y-axis.
yticks = [re.sub("(?<=.{1})(.?)(?=[A-Z]+)", "\\1\n", label, 0, re.DOTALL).upper() for label in corr.index]
xticks = [re.sub("(?<=.{1})(.?)(?=[A-Z]+)", "\\1\n", label, 0, re.DOTALL).upper() for label in corr.columns]
fig, ax = plt.subplots(figsize=(20,15))
sns.heatmap(corr, ax=ax, annot=True, fmt="d",
cmap="Blues", annot_kws=annot_kws,
mask=mask, vmin=0, vmax=5000,
cbar_kws={"shrink": .8}, square=True,
linewidths=5)
for p in ax.texts:
myTrans = p.get_transform()
offset = mpl.transforms.ScaledTranslation(-12, 5, mpl.transforms.IdentityTransform())
p.set_transform(myTrans + offset)
plt.yticks(plt.yticks()[0], labels=yticks, rotation=0, linespacing=0.4)
plt.xticks(plt.xticks()[0], labels=xticks, rotation=0, linespacing=0.4)
where corr represents a pre-defined pandas dataframe.
I couldn't seem to find an align parameter for setting the ticks and was wondering if and how this centering could be achieved in seaborn/matplotlib?
I've adapted the seaborn correlation plot example below.
from string import ascii_letters
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
sns.set_theme(style="white")
# Generate a large random dataset
rs = np.random.RandomState(33)
d = pd.DataFrame(data=rs.normal(size=(100, 7)),
columns=['Donald\nDuck','Mickey\nMouse','Han\nSolo',
'Luke\nSkywalker','Yoda','Santa\nClause','Ronald\nMcDonald'])
# Compute the correlation matrix
corr = d.corr()
# Generate a mask for the upper triangle
mask = np.triu(np.ones_like(corr, dtype=bool))
# Set up the matplotlib figure
f, ax = plt.subplots(figsize=(11, 9))
# Generate a custom diverging colormap
cmap = sns.diverging_palette(230, 20, as_cmap=True)
# Draw the heatmap with the mask and correct aspect ratio
sns.heatmap(corr, mask=mask, cmap=cmap, vmax=.3, center=0,
square=True, linewidths=.5, cbar_kws={"shrink": .5})
for i in ax.get_yticklabels():
i.set_ha('right')
i.set_rotation(0)
for i in ax.get_xticklabels():
i.set_ha('center')
Note the two for sequences above. These get the label and then set the horizontal alignment (You can also change the vertical alignment (set_va()).
The code above produces this:

PyPlot ConnectionPatch between CartoPy GeoAxes

The ConnectionPatch is a useful way to draw a line between two points on two different axes (demo). Is it possible to use this class when one (or both) of the axes is of Cartopy GeoAxes type? A related answer suggests a work-around but I would prefer to avoid this.
I can not answer your question about the use of that class thing. But, if you are interested in plotting the lines between 2 different Cartopy geoaxes, or between matplotlib axes and a geoaxe, that can be achieved with some coordinate transformation. Here is a runnable code and the output plot. I have written some comments within the code to help explain the important steps.
For further information about coordinate system and tranformation:
Cartopy https://scitools.org.uk/cartopy/docs/latest/tutorials/understanding_transform.html
Since Cartopy is built on top of Matplotlib, you need to look into the related subject in Matplotlib.
Matplotlib https://matplotlib.org/3.2.1/tutorials/advanced/transforms_tutorial.html
import cartopy
import cartopy.mpl.geoaxes
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
fig, ax = plt.subplots()
fig.set_size_inches([8,8]) # 9,6; 8,9; 8,3 all OK
# Plot simple line on main axes
ax.plot([4,5,3,1,2])
p1 = [0.5,3.0] # Bangkok text location
p2 = [0.5,2.75] # Himalaya text location
# Plot texts (Bangkok, Himalaya) on the main axes
ax.text(*p1, "Bangkok", ha='right')
ax.text(*p2, "Himalaya", ha='right')
# Ploting on UR inset map (cartopy) on the main axes (ax)
bkk_lon, bkk_lat = 100, 13 # Bangkok
hml_lon, hml_lat = 83.32, 29.22 # Everest peak
# Create cartopy geoaxes inset axes as part of the main axes 'ax'
axins = inset_axes(ax, width="40%", height="30%", loc="upper right",
axes_class = cartopy.mpl.geoaxes.GeoAxes,
axes_kwargs = dict(map_projection = cartopy.crs.PlateCarree()))
# Set map limits on that axes (for Thailand)
llx, lly = 95, 0
urx, ury = 110, 25
axins.set_xlim((llx, urx))
axins.set_ylim((lly, ury))
# Plot coastlines
axins.add_feature(cartopy.feature.COASTLINE)
# Plot line across the inset mao, LL to UR; OK
#ll_p, ur_p = [llx,urx], [lly,ury]
#axins.plot(ll_p, ur_p, "r--")
axins.plot(bkk_lon, bkk_lat, 'ro', transform=cartopy.crs.PlateCarree()) # OK!
# Create another inset map on the main axes (ax)
axins2 = inset_axes(ax, width="40%", height="30%", loc="lower left",
axes_class = cartopy.mpl.geoaxes.GeoAxes,
axes_kwargs = dict(map_projection = cartopy.crs.PlateCarree()))
# Set map limits on that axes (second inset map)
llx2, lly2 = -60, -20
urx2, ury2 = 120, 90
axins2.set_xlim((llx2, urx2))
axins2.set_ylim((lly2, ury2))
axins2.add_feature(cartopy.feature.COASTLINE)
# Plot line from UK to BKK, OK
#p21, p22 = [0, 100], [40, 13]
#axins2.plot(p21, p22, "r--")
# Plot blue dot at Himalaya
axins2.plot(hml_lon, hml_lat, "bo")
plt.draw() # Do this to get updated position
# Do coordinate transformation to get BKK, HML locations in display coordinates
# from axins_data_xy to dp_xy
dpxy_bkk_axins = axins.transData.transform((bkk_lon, bkk_lat)) # get display coordinates
# from axins2_data_xy to dp_xy
dpxy_bkk_axins2 = axins2.transData.transform((hml_lon, hml_lat)) # get display coordinates
# Do coordinate transformation to get BKK, HML locations in data coordinates of the main axes 'ax'
# from both dp_xy to main_ax_data
ur_bkk = ax.transData.inverted().transform( dpxy_bkk_axins )
ll_hml = ax.transData.inverted().transform( dpxy_bkk_axins2 )
# Prep coordinates for line connecting BKK to HML
xs = ur_bkk[0], ll_hml[0]
ys = ur_bkk[1], ll_hml[1]
xs = ur_bkk[0], ll_hml[0]
ys = ur_bkk[1], ll_hml[1]
ax.plot(xs, ys, 'g--') # from Bkk to Himalaya of different inset maps
# Plot lines from texts (on main axes) to locations on maps
ax.plot([p1[0], ur_bkk[0]], [p1[1], ur_bkk[1]], 'y--')
ax.plot([p2[0], ll_hml[0]], [p2[1], ll_hml[1]], 'y--')
# Set cartopy inset background invisible
axins.background_patch.set_visible(False)
axins2.background_patch.set_visible(False)
plt.show()
The output plot:-

how to add one more plot in matplotlib script

My matplotlib script plots a file "band.hdf5", which is in hdf5 format, with
f = h5py.File('band.hdf5', 'r')
I want to add one more hdf5 file "band-new.hdf5" here in such a way that the output plot will have one more plot on right side for new file. Y-axis label should be avoided for "band-new.hdf5" and X-axis label should be common for both file.
The header of the script is this
import h5py
import matplotlib.pyplot as plt
import warnings
import matplotlib
This script is taken from the accepted answer
https://stackoverflow.com/questions/62099211/how-to-plot-two-case1-hdf5-and-case2-hdf5-files-in-matplotlib-seeking-help-to-c?rq=1
Is this the solution you needed?
I take the code from
and adapted it to draw two plots side-to-side from the data you shared.
import h5py
import matplotlib.pyplot as plt
import warnings
import matplotlib
warnings.filterwarnings("ignore") # Ignore all warnings
cmap = matplotlib.cm.get_cmap('jet', 4)
ticklabels=['A','B','C','D','E']
params = {
'mathtext.default': 'regular',
'axes.linewidth': 1.2,
'axes.edgecolor': 'Black',
'font.family' : 'serif'
}
#get the viridis cmap with a resolution of 3
#apply a scale to the y axis. I'm just picking an arbritrary number here
scale = 10
offset = 0 #set this to a non-zero value if you want to have your lines offset in a waterfall style effect
f_left = h5py.File('band-222.hdf5', 'r')
f_right = h5py.File('band-332.hdf5', 'r')
print ('datasets from left are:')
print(list(f_left.keys()))
print ('datasets from right are:')
print(list(f_right.keys()))
# PLOTTING
plt.rcParams.update(params)
fig = plt.figure(figsize=(16,8))
ax1 = fig.add_subplot(121)
# LEFT ONE
dist=f_left[u'distance']
freq=f_left[u'frequency']
kpt=f_left[u'path']
lbl = {0:'AB', 1:'BC', 2:'CD', 3:'fourth'}
for i, section in enumerate(dist):
for nbnd, _ in enumerate(freq[i][0]):
x = section # to_list() you may need to convert sample to list.
y = (freq[i, :, nbnd] + offset*nbnd) * scale
if (nbnd<3):
color=f'C{nbnd}'
else:
color='black'
ax1.plot(x, y, c=color, lw=2.0, alpha=0.8, label = lbl[nbnd] if nbnd < 3 and i == 0 else None)
ax1.legend()
# Labels and axis limit and ticks
ax1.set_ylabel(r'Frequency (THz)', fontsize=12)
ax1.set_xlabel(r'Wave Vector (q)', fontsize=12)
ax1.set_xlim([dist[0][0],dist[len(dist)-1][-1]])
xticks=[dist[i][0] for i in range(len(dist))]
xticks.append(dist[len(dist)-1][-1])
ax1.set_xticks(xticks)
ax1.set_xticklabels(ticklabels)
# Plot grid
ax1.grid(which='major', axis='x', c='green', lw=2.5, linestyle='--', alpha=0.8)
# RIGHT ONE
ax2 = fig.add_subplot(122)
dist=f_right[u'distance']
freq=f_right[u'frequency']
kpt=f_right[u'path']
lbl = {0:'AB', 1:'BC', 2:'CD', 3:'fourth'}
for i, section in enumerate(dist):
for nbnd, _ in enumerate(freq[i][0]):
x = section # to_list() you may need to convert sample to list.
y = (freq[i, :, nbnd] + offset*nbnd) * scale
if (nbnd<3):
color=f'C{nbnd}'
else:
color='black'
ax2.plot(x, y, c=color, lw=2.0, alpha=0.8, label = lbl[nbnd] if nbnd < 3 and i == 0 else None)
ax2.legend()
# remove y axis
ax2.axes.get_yaxis().set_visible(False)
ax2.set_xlabel(r'Wave Vector (q)', fontsize=12)
ax2.set_xlim([dist[0][0],dist[len(dist)-1][-1]])
xticks=[dist[i][0] for i in range(len(dist))]
xticks.append(dist[len(dist)-1][-1])
ax2.set_xticks(xticks)
ax2.set_xticklabels(ticklabels)
# Plot grid
ax2.grid(which='major', axis='x', c='green', lw=2.5, linestyle='--', alpha=0.8)
fig.tight_layout() # Or equivalently, "plt.tight_layout()"
# Save to pdf
plt.savefig('plots.pdf', bbox_inches='tight')
The final figure is like this.

Draw colorbar with twin scales

I'd like to draw a (vertical) colorbar, which has two different scales (corresponding to two different units for the same quantity) on each side. Think Fahrenheit on one side and Celsius on the other side. Obviously, I'd need to specify the ticks for each side individually.
Any idea how I can do this?
That should get you started:
import matplotlib.pyplot as plt
import numpy as np
# generate random data
x = np.random.randint(0,200,(10,10))
plt.pcolormesh(x)
# create the colorbar
# the aspect of the colorbar is set to 'equal', we have to set it to 'auto',
# otherwise twinx() will do weird stuff.
cbar = plt.colorbar()
pos = cbar.ax.get_position()
cbar.ax.set_aspect('auto')
# create a second axes instance and set the limits you need
ax2 = cbar.ax.twinx()
ax2.set_ylim([-2,1])
# resize the colorbar (otherwise it overlays the plot)
pos.x0 +=0.05
cbar.ax.set_position(pos)
ax2.set_position(pos)
plt.show()
If you create a subplot for the colorbar, you can create a twin axes for that subplot and manipulate it like a normal axes.
import matplotlib.colors as mcolors
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(-1,2.7)
X,Y = np.meshgrid(x,x)
Z = np.exp(-X**2-Y**2)*.9+0.1
fig, (ax, cax) = plt.subplots(ncols=2, gridspec_kw={"width_ratios":[15,1]})
im =ax.imshow(Z, vmin=0.1, vmax=1)
cbar = plt.colorbar(im, cax=cax)
cax2 = cax.twinx()
ticks=np.arange(0.1,1.1,0.1)
iticks=1./np.array([10,3,2,1.5,1])
cbar.set_ticks(ticks)
cbar.set_label("z")
cbar.ax.yaxis.set_label_position("left")
cax2.set_ylim(0.1,1)
cax2.set_yticks(iticks)
cax2.set_yticklabels(1./iticks)
cax2.set_ylabel("1/z")
plt.show()
Note that in newer version of matplotlib, the above answers no long work (as #Ryan Skene pointed out). I'm using v3.3.2. The secondary_yaxis function works for the colorbars in the same way as for regular plot axes and gives one colorbar with two scales: https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.secondary_yaxis.html#matplotlib.axes.Axes.secondary_yaxis
import matplotlib.pyplot as plt
import numpy as np
# generate random data
x = np.random.randint(0,200,(10,10)) #let's assume these are temperatures in Fahrenheit
im = plt.imshow(x)
# create the colorbar
cbar = plt.colorbar(im,pad=0.1) #you may need to adjust this padding for the secondary colorbar label[enter image description here][1]
cbar.set_label('Temperature ($^\circ$F)')
# define functions that relate the two colorbar scales
# e.g., Celcius to Fahrenheit and vice versa
def F_to_C(x):
return (x-32)*5/9
def C_to_F(x):
return (x*9/5)+32
# create a second axes
cbar2 = cbar.ax.secondary_yaxis('left',functions=(F_to_C,C_to_F))
cbar2.set_ylabel('Temperatrue ($\circ$C)')
plt.show()
I am using an inset axis for my colorbar and, for some reason, I found the above to answers no longer worked as of v3.4.2. The twinx took up the entire original subplot.
So I just replicated the inset axis (instead of using twinx) and increased the zorder on the original inset.
axkws = dict(zorder=2)
cax = inset_axes(
ax, width="100%", height="100%", bbox_to_anchor=bbox,
bbox_transform=ax.transAxes, axes_kwargs=axkws
)
cbar = self.fig.colorbar(mpl.cm.ScalarMappable(cmap=cmap), cax=cax)
cbar.ax.yaxis.set_ticks_position('left')
caxx = inset_axes(
ax, width="100%", height="100%",
bbox_to_anchor=bbox, bbox_transform=ax.transAxes
)
caxx.yaxis.set_ticks_position('right')

matplotlib: Stretch image to cover the whole figure

I am quite used to working with matlab and now trying to make the shift matplotlib and numpy. Is there a way in matplotlib that an image you are plotting occupies the whole figure window.
import numpy as np
import matplotlib.pyplot as plt
# get image im as nparray
# ........
plt.figure()
plt.imshow(im)
plt.set_cmap('hot')
plt.savefig("frame.png")
I want the image to maintain its aspect ratio and scale to the size of the figure ... so when I do savefig it exactly the same size as the input figure, and it is completely covered by the image.
Thanks.
I did this using the following snippet.
#!/usr/bin/env python
import numpy as np
import matplotlib.cm as cm
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt
from pylab import *
delta = 0.025
x = y = np.arange(-3.0, 3.0, delta)
X, Y = np.meshgrid(x, y)
Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
Z = Z2-Z1 # difference of Gaussians
ax = Axes(plt.gcf(),[0,0,1,1],yticks=[],xticks=[],frame_on=False)
plt.gcf().delaxes(plt.gca())
plt.gcf().add_axes(ax)
im = plt.imshow(Z, cmap=cm.gray)
plt.show()
Note the grey border on the sides is related to the aspect rario of the Axes which is altered by setting aspect='equal', or aspect='auto' or your ratio.
Also as mentioned by Zhenya in the comments Similar StackOverflow Question
mentions the parameters to savefig of bbox_inches='tight' and pad_inches=-1 or pad_inches=0
You can use a function like the one below.
It calculates the needed size for the figure (in inches) according to the resolution in dpi you want.
import numpy as np
import matplotlib.pyplot as plt
def plot_im(image, dpi=80):
px,py = im.shape # depending of your matplotlib.rc you may
have to use py,px instead
#px,py = im[:,:,0].shape # if image has a (x,y,z) shape
size = (py/np.float(dpi), px/np.float(dpi)) # note the np.float()
fig = plt.figure(figsize=size, dpi=dpi)
ax = fig.add_axes([0, 0, 1, 1])
# Customize the axis
# remove top and right spines
ax.spines['right'].set_color('none')
ax.spines['left'].set_color('none')
ax.spines['top'].set_color('none')
ax.spines['bottom'].set_color('none')
# turn off ticks
ax.xaxis.set_ticks_position('none')
ax.yaxis.set_ticks_position('none')
ax.xaxis.set_ticklabels([])
ax.yaxis.set_ticklabels([])
ax.imshow(im)
plt.show()
Here's a minimal object-oriented solution:
fig = plt.figure(figsize=(8, 8))
ax = fig.add_axes([0, 0, 1, 1], frameon=False, xticks=[], yticks=[])
Testing it out with
ax.imshow([[0]])
fig.savefig('test.png')
saves out a uniform purple block.
edit: As #duhaime points out below, this requires the figure to have the same aspect as the axes.
If you'd like the axes to resize to the figure, add aspect='auto' to imshow.
If you'd like the figure to resize to be resized to the axes, add
from matplotlib import tight_bbox
bbox = fig.get_tightbbox(fig.canvas.get_renderer())
tight_bbox.adjust_bbox(fig, bbox, fig.canvas.fixed_dpi)
after the imshow call. This is the important bit of matplotlib's tight_layout functionality which is implicitly called by things like Jupyter's renderer.