Adding gridlines using Cartopy - matplotlib

I'm trying to add gridlines to a map I made using Cartopy, however, when I use the example code from the cartopy documentation, it doesn't display what I want and I can't figure out how to manipulate it to do so.
def plotMap():
proj = ccrs.Mercator(central_longitude=180, min_latitude=15,
max_latitude=55)
fig, ax = plt.subplots(subplot_kw=dict(projection=proj), figsize=(12,12))
ax.set_extent([255 ,115, 0, 60], crs=ccrs.PlateCarree())
ax.add_feature(cfeature.LAND, facecolor='0.3')
ax.add_feature(cfeature.LAKES, alpha=0.9)
ax.add_feature(cfeature.BORDERS, zorder=10)
ax.add_feature(cfeature.COASTLINE, zorder=10)
#(http://www.naturalearthdata.com/features/)
states_provinces = cfeature.NaturalEarthFeature(
category='cultural', name='admin_1_states_provinces_lines',
scale='50m', facecolor='none')
ax.add_feature(states_provinces, edgecolor='black', zorder=10)
#ax.gridlines(xlocs=grids_ma, ylocs=np.arange(-80,90,20), zorder=21,
draw_labels=True )
ax.gridlines(crs=ccrs.PlateCarree(), linewidth=2, color='black',
draw_labels=True, alpha=0.5, linestyle='--')
ax.xlabels_top = False
ax.ylabels_left = False
ax.ylabels_right=True
ax.xlines = True
ax.xlocator = mticker.FixedLocator([-160, -140, -120, 120, 140, 160, 180,])
ax.xformatter = LONGITUDE_FORMATTER
ax.yformatter = LATITUDE_FORMATTER
ax.xlabel_style = {'size': 15, 'color': 'gray'}
ax.xlabel_style = {'color': 'red', 'weight': 'bold'}
return fig, ax
I've attached a picture of the output. For reference, I only want the longitude gridlines to start at the left of my domain and end at the right side, preferably being spaced every 20 degrees. Ideally the same for latitude lines as well.
Bad gridline plot

Is the example you are following the one at the bottom of this page? If so, you are attempting to set attributes on the GeoAxes (ax) instance which should be set on the GridLiner (gl) instance:
import cartopy
import cartopy.crs as ccrs
import cartopy.feature as cfeature
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
def plotMap():
proj = ccrs.Mercator(central_longitude=180, min_latitude=15,
max_latitude=55)
fig, ax = plt.subplots(subplot_kw=dict(projection=proj), figsize=(12,12))
ax.set_extent([255 ,115, 0, 60], crs=ccrs.PlateCarree())
ax.add_feature(cfeature.LAND, facecolor='0.3')
ax.add_feature(cfeature.LAKES, alpha=0.9)
ax.add_feature(cfeature.BORDERS, zorder=10)
ax.add_feature(cfeature.COASTLINE, zorder=10)
states_provinces = cfeature.NaturalEarthFeature(
category='cultural', name='admin_1_states_provinces_lines',
scale='50m', facecolor='none')
ax.add_feature(states_provinces, edgecolor='black', zorder=10)
gl = ax.gridlines(crs=ccrs.PlateCarree(), linewidth=2, color='black', alpha=0.5, linestyle='--', draw_labels=True)
gl.xlabels_top = False
gl.ylabels_left = False
gl.ylabels_right=True
gl.xlines = True
gl.xlocator = mticker.FixedLocator([120, 140, 160, 180, -160, -140, -120])
gl.ylocator = mticker.FixedLocator([0, 20, 40, 60])
gl.xformatter = LONGITUDE_FORMATTER
gl.yformatter = LATITUDE_FORMATTER
gl.xlabel_style = {'color': 'red', 'weight': 'bold'}
This produces the following map. The gridliner doesn't seem to be coping with the dateline. I do not know if there is a way around this, but there is a note at the top of the above linked documentation to say that there are currently known limitations with this class, so maybe not.
An alternative is to set the various labels and their styles directly with matplotlib. Note that you have to set the ticklabels separately from the ticks, otherwise you get labels corresponding to the Mercator coordinate reference system:
import cartopy.mpl.ticker as cticker
def plotMap2():
proj = ccrs.Mercator(central_longitude=180, min_latitude=15,
max_latitude=55)
fig, ax = plt.subplots(subplot_kw=dict(projection=proj), figsize=(12,12))
ax.set_extent([255 ,115, 0, 60], crs=ccrs.PlateCarree())
ax.add_feature(cfeature.LAND, facecolor='0.3')
ax.add_feature(cfeature.LAKES, alpha=0.9)
ax.add_feature(cfeature.BORDERS, zorder=10)
ax.add_feature(cfeature.COASTLINE, zorder=10)
states_provinces = cfeature.NaturalEarthFeature(
category='cultural', name='admin_1_states_provinces_lines',
scale='50m', facecolor='none')
ax.add_feature(states_provinces, edgecolor='black', zorder=10)
ax.set_xticks([120., 140., 160., 180., -160., -140., -120.], crs=ccrs.PlateCarree())
ax.set_xticklabels([120., 140., 160., 180., -160., -140., -120.], color='red', weight='bold')
ax.set_yticks([20, 40], crs=ccrs.PlateCarree())
ax.set_yticklabels([20, 40])
ax.yaxis.tick_right()
lon_formatter = cticker.LongitudeFormatter()
lat_formatter = cticker.LatitudeFormatter()
ax.xaxis.set_major_formatter(lon_formatter)
ax.yaxis.set_major_formatter(lat_formatter)
ax.grid(linewidth=2, color='black', alpha=0.5, linestyle='--')

Related

Plot circle at the title in matplotlib python

I have a 2 line title and first line has a number at the end of the line.
Can we plot a circle around the number?
Here is the code to generate the figure.
from matplotlib import rcParams
from matplotlib import pyplot as plt
import numpy as np
import os
rcParams.update({'figure.autolayout': True})
some_text = 'XXX'
any_number=15
title = '%s: %d\n YYY ZZZZ WWWWW' % (some_text,any_number)
fig = plt.figure(figsize=(8, 8), dpi=100)
plt.tick_params(axis='y', which='major', labelsize=60, width=3, length=10, pad=40)
plt.tick_params(axis='y', which='minor', labelsize=60, width=3, length=10, pad=40)
ax = plt.gca()
plt.title(title, fontsize=60, pad=40, loc='center', fontweight='semibold')
plt.style.use('ggplot')
ax.set_facecolor('white')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.spines['left'].set_visible(True)
for edge_i in ['left']:
ax.spines[edge_i].set_edgecolor("black")
ax.spines[edge_i].set_linewidth(3)
ax.spines[edge_i].set_bounds(0, 1)
x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
plt.yticks(np.arange(0, 1.01, step=0.2))
data_list= np.array([1,1,1,1,1,0.9, 0.8, 0.7, 0.8,0.85])
plt.bar(x, data_list, 0.9, color='indianred',edgecolor="black", linewidth=3,zorder=1)
plt.tick_params(
axis='x', # changes apply to the x-axis
which='both', # both major and minor ticks are affected
bottom=False, # ticks along the bottom edge are off
top=False, # ticks along the top edge are off
labelbottom=False) # labels along the bottom edge are off
figure_name = 'figure_with_circle.png'
figure_file = os.path.join('/Users/burcakotlu/Desktop',figure_name)
fig.savefig(figure_file, dpi=100, bbox_inches="tight")
plt.close(fig)
Here is the current figure and the wanted circle.
One could use the following without ax.bar():
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.set_title('title')
circle1 = plt.Circle((2,4.15), 0.2, color='k', clip_on=False, zorder=100, fill=False)
ax.add_patch(circle1)
ax.set_xlim(0,4)
ax.set_ylim(0,4)
plt.show()
I have found a way to plot circle together with bar plots without distorting bars. Here is the code below:
from matplotlib import rcParams
from matplotlib import pyplot as plt
import numpy as np
import os
import matplotlib.patches as patches
from matplotlib.offsetbox import AnchoredText
rcParams.update({'figure.autolayout': True})
some_text = 'XXX'
any_number=15
title = '%s: %d\n YYY ZZZZ WWWWW' % (some_text,any_number)
fig = plt.figure(figsize=(12,12), dpi=100)
plt.tick_params(axis='y', which='major', labelsize=60, width=3, length=10, pad=40)
plt.tick_params(axis='y', which='minor', labelsize=60, width=3, length=10, pad=40)
ax = plt.gca()
number_of_xxx = '12'
anchored_text_number_of_xxx = AnchoredText(number_of_xxx,
frameon=False, borderpad=0, pad=0.1,
loc='upper right',
bbox_to_anchor=[0.95, 1.3],
bbox_transform=plt.gca().transAxes,
prop={'fontsize': 60,
'fontweight': 'semibold'})
ax.add_artist(anchored_text_number_of_xxx)
circle1 = patches.Circle((0.88, 1.25), radius=0.1, transform=ax.transAxes, zorder=100, fill=False, color='gold', lw=8, clip_on=False)
ax.add_patch(circle1)
ax.set_title(title, fontsize=60, pad=40, loc='center', fontweight='semibold', zorder=50)
ax.set_facecolor('white')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.spines['left'].set_visible(True)
for edge_i in ['left']:
ax.spines[edge_i].set_edgecolor("black")
ax.spines[edge_i].set_linewidth(3)
ax.spines[edge_i].set_bounds(0, 1)
x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
ax.set_yticks(np.arange(0, 1.01, step=0.2))
data_list= np.array([1,1,1,1,1,0.9, 0.8, 0.7, 0.8,0.85])
ax.bar(x, data_list, 0.9, color='indianred',edgecolor="black", linewidth=3,zorder=1)
ax.tick_params(
axis='x', # changes apply to the x-axis
which='both', # both major and minor ticks are affected
bottom=False, # ticks along the bottom edge are off
top=False, # ticks along the top edge are off
labelbottom=False) # labels along the bottom edge are off
figure_name = 'figure_with_circle.png'
figure_file = os.path.join('/Users/burcakotlu/Desktop',figure_name)
fig.savefig(figure_file, dpi=100, bbox_inches="tight")
plt.close(fig)

Python 3 matplotlit figure saved broken

I use the python 3 to make a figure, it displays very well, but when I save it, it is broken for the right half, as figures appended below. I am not sure what is wrong with it. The code I have is the following:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.ticker import rcParams, AutoMinorLocator
params = {'backend': 'ps',
'font.size': 30,
'font.style': 'normal',
'axes.labelsize': 30,
#'text.fontsize': 30,
'axes.linewidth': 2,
'legend.fontsize': 12,
'xtick.labelsize': 30,
'ytick.labelsize': 30,
'xtick.top': True,
'ytick.right': True,
#'xtick.major.top': True,
#'xtick.minor.top': True,
#'ytick.major.right': True,
#'ytick.minor.right': True,
'text.usetex': True,
'ps.usedistiller': 'xpdf'}
rcParams.update(params)
ion = [-2.0, -2.5, -3.0, -3.25, -3.5, -4.0, -4.5, -5.0, -5.5, -6.0]
cooling_mass = [0.036409, 0.035329, 0.042622, 0.069795, 0.090708, 0.107212, 0.109671, 0.110513, 0.107213, 0.109558]
fig, ax = plt.subplots(1, figsize=(8,6))
minor_locator1 = AutoMinorLocator(5)
ax.xaxis.set_minor_locator(minor_locator1)
minor_locator2 = AutoMinorLocator(5)
ax.yaxis.set_minor_locator(minor_locator2)
ax.tick_params('both', length=10, width=2, direction='in',which='major',pad=8)
ax.tick_params('both', length=5, width=2, direction='in',which='minor',pad=8)
ax.axhspan(0.036, 0.107, facecolor='lightskyblue', alpha=0.5)
ax.scatter(ion, cooling_mass, color='b', marker = 'o', s=50, zorder=2)
ax.set_ylim([0.008,0.14])
ax.set_yticks([0.03,0.06,0.09,0.12])
ax.set_xlim([-6.5,-1.5])
ax.set_xticks([-6.0,-4.0,-2.0])
ax.set_ylabel(r'$\rm Cooling ~Mass ~Rate ~[\rm M_\odot/yr]$', size=20, labelpad=5)
ax.set_xlabel(r'$\log {\rm U}$')
plt.gca().invert_xaxis()
plt.savefig('ion_cooling.eps',bbox_inches='tight')
plt.show()

Matplotlib - Setting a tick label's background colour

I have a subplot and its tick labels overlap with the data. I would like to set the x-tick labels to have a background colour (e.g. white). Currently I have only been able to find how to change the label's colour, but not the background. I know how to get the effect using a text object as shown below. (NB - I don't want the whole subplot's margin to be coloured, but just the tick label).
MWE
import matplotlib as mpl
rc_fonts = {
"text.usetex": True,
'text.latex.preview': True,
"font.size": 50,
'mathtext.default': 'regular',
'axes.titlesize': 55,
"axes.labelsize": 55,
"legend.fontsize": 50,
"xtick.labelsize": 50,
"ytick.labelsize": 50,
'figure.titlesize': 55,
'figure.figsize': (10, 6.5), # 15, 9.3
'text.latex.preamble': [
r"""\usepackage{lmodern,amsmath,amssymb,bm,physics,mathtools,nicefrac,letltxmacro,fixcmex}
"""],
"font.family": "serif",
"font.serif": "computer modern roman",
}
mpl.rcParams.update(rc_fonts)
import matplotlib.pylab as plt
from mpl_toolkits.axes_grid1.inset_locator import inset_axes, InsetPosition, mark_inset
from numpy import linspace, sin
x = linspace(0, 1, 100)
plt.clf()
ax1 = plt.gca()
ax2 = plt.axes([0, 0, 1, 1], label=str(2))
ip = InsetPosition(ax1, [0.08, 0.63, 0.45, 0.3])
ax2.set_axes_locator(ip)
ax1.plot(x, x)
ax1.plot(x, x + 0.3)
ax1.set_xlim(0, 1)
ax1.set_ylim(0, 1)
ax2.xaxis.set_tick_params(labelcolor='r')
ax1.text(0.3, 0.3, '$1$', transform=ax1.transAxes, horizontalalignment='center', verticalalignment='center', color='black', backgroundcolor='white')
To set a label's background color you may use the same property as for a text, essentially because a label is a text.
plt.setp(ax2.get_xticklabels(), backgroundcolor="limegreen")
For more sophisticated backgrounds, you could also use the bbox property.
bbox = dict(boxstyle="round", ec="limegreen", fc="limegreen", alpha=0.5)
plt.setp(ax2.get_xticklabels(), bbox=bbox)
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots()
ax.plot(np.linspace(0, 1, 5), np.random.rand(5))
# set xticklabels
xtl = []
for x in ax.get_xticks():
xtl += ['lbl: {:.1f}'.format(x)]
ax.set_xticklabels(xtl)
# modify labels
for tl in ax.get_xticklabels():
txt = tl.get_text()
if txt == 'lbl: 1.0':
txt += ' (!)'
tl.set_backgroundcolor('C3')
tl.set_text(txt)

How to set the updated value of Slider always in the centre?

Following the Slider Demo of Matplotlib https://matplotlib.org/gallery/widgets/slider_demo.html, I would like to update the Slider ranges, so that every time I change the slider values, those are re-centred in the Slider.
I have tried to define the Sliders as
sfreq = Slider(axfreq, 'Freq', freq-10, freq+10, valinit=freq)
samp = Slider(axamp, 'Amp', amp-5, amp+5, valinit=amp)
but since the update() function does not return anything, that does not work. I also tried making these variables global inside the function, which also did not work. I finally tried defining the Sliders inside the update function,
def update(val):
amp = samp.val
freq = sfreq.val
l.set_ydata(amp*np.sin(2*np.pi*freq*t))
fig.canvas.draw_idle()
Slider(axfreq, 'Freq', freq-10, freq+10, valinit=freq)
Slider(axamp, 'Amp', amp-5, amp+5, valinit=amp)
but that overlays more and more Sliders as I change the values. Any suggestions?
So I just decided to make the range of the slider cover several orders of magnitude of the parameter, and display the values in a logarithmic scale. In case anyone wonders, and following the matplotlib demo:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider, Button, RadioButtons
fig, ax = plt.subplots()
plt.subplots_adjust(left=0.25, bottom=0.25)
t = np.arange(0.0, 1.0, 0.001)
a0 = 5
f0 = 10
delta_f = 5.0
s = a0*np.sin(2*np.pi*f0*t)
l, = plt.plot(t, s, lw=2, color='red')
plt.axis([0, 1, -10, 10])
axcolor = 'lightgoldenrodyellow'
axfreq = plt.axes([0.25, 0.1, 0.65, 0.03], facecolor=axcolor)
axamp = plt.axes([0.25, 0.15, 0.65, 0.03], facecolor=axcolor)
sfreq = Slider(axfreq, 'Freq', np.log(1), np.log10(1000), valinit=np.log10(f0), valfmt='%4.2E')
samp = Slider(axamp, 'Amp', a0-5, a0+5, valinit=a0)
def update(val):
amp = samp.val
freq = sfreq.val
sfreq.valtext.set_text('{:4.2E}'.format(10**freq))
l.set_ydata(amp*np.sin(2*np.pi*10**freq*t))
fig.canvas.draw_idle()
sfreq.on_changed(update)
samp.on_changed(update)
resetax = plt.axes([0.8, 0.025, 0.1, 0.04] )
button = Button(resetax, 'Reset', color=axcolor, hovercolor='0.975')
def reset(event):
sfreq.reset()
samp.reset()
button.on_clicked(reset)
rax = plt.axes([0.025, 0.5, 0.15, 0.15], facecolor=axcolor)
radio = RadioButtons(rax, ('red', 'blue', 'green'), active=0)
def colorfunc(label):
l.set_color(label)
fig.canvas.draw_idle()
radio.on_clicked(colorfunc)
plt.show()

How to use same labels for shared x axes in Matplotlib?

I'm trying to translate two separate figure into one figure with sharing x axis. But there are something that I'm missing. For styling and ticks I normally use the codes
plt.xlabel(r'$\mathrm{2\theta\ (deg)}$')
plt.ylabel(r'$\mathrm{Intensity\ (a.u)}$')
plt.xlim(15,80)
plt.legend(loc=4,prop={'size':10})
params = {'legend.fontsize': 18,
'axes.labelsize': 18,
'axes.titlesize': 18,
'xtick.labelsize' :12,
'mathtext.fontset': 'cm',
'mathtext.rm': 'serif', }
matplotlib.rcParams.update(params)
plt.tick_params(
axis='both', which='both',
right='off', left='off',
top = 'off', bottom = 'off',
labelleft='off')
Now, I need to apply it for this shared figure. These includes following:
No ticks will be on figure.
Axes labels will be shared.
It would be better to load text files inside a loop.
What do I need to know for these improvements?
import matplotlib.pyplot as plt
from numpy import loadtxt
import matplotlib
f = plt.figure()
plt.subplots_adjust(hspace=0.001)
data = loadtxt("ES1.txt",float)
POS = data[:,0]
ESD = data[:,1]
ax1 = plt.subplot(311)
ax1.plot(POS, ESD, color="blue")
data = loadtxt("ES2.txt",float)
POS = data[:,0]
ESD = data[:,1]
ax2 = plt.subplot(312, sharex=ax1)
ax2.plot(POS, ESD, color="red")
yticklabels = ax1.get_yticklabels() + ax2.get_yticklabels()
plt.setp(yticklabels, visible=False)
plt.savefig('shared_xrd' + '.png', dpi=600, bbox_inches='tight')
It could be that the following code is more like you want it.
import matplotlib.pyplot as plt
import numpy as np
import matplotlib
params = {'legend.fontsize': 18,
'axes.labelsize': 18,
'axes.titlesize': 18,
'xtick.labelsize' :12,
'mathtext.fontset': 'cm',
'mathtext.rm': 'serif',
"xtick.bottom" : False,
"ytick.left" : False,
}
matplotlib.rcParams.update(params)
f, axes = plt.subplots(nrows=2, sharex=True)
plt.subplots_adjust(hspace=0.001, bottom=0.2)
colors=["blue", "red"]
for i in [0,1]:
data = np.loadtxt("ES{}.txt".format(i+1))
POS = data[:,0]
ESD = data[:,1]
axes[i].plot(POS, ESD, color=colors[i], label="data{}".format(i))
axes[i].legend(loc=4,prop={'size':10})
# make ticks invisble
axes[0].set_yticks([])
axes[1].set_yticks([])
plt.xlabel(r'$\mathrm{2\theta\ (deg)}$')
plt.xlim(15,80)
#create subplot just for placing the ylabel centered on all plots
shadowaxes = f.add_subplot(111, xticks=[], yticks=[], frame_on=False)
shadowaxes.set_ylabel(r'$\mathrm{Intensity\ (a.u)}$')
plt.savefig(__file__ + '.png', dpi=600, bbox_inches='tight')
plt.show()