How to change x,y in plt to lon, lat in cartopy? - python-3.x

I want to change x, y to lon, lat (Picture below yellow marker) when I click some point in map and get x, y.
How can I change it? Help me please...
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
import numpy as np
import cartopy
def mouse_click(event):
x, y = event.xdata, event.ydata
print(x, y)
proj = ccrs.LambertConformal(central_longitude=125, central_latitude=35, false_easting=400000,false_northing=400000,
standard_parallels=(46, 49))
fig = plt.figure(figsize=(16.535433, 11.692913))
ax = fig.add_subplot(1,1,1, projection=proj)
ax.set_extent((79, 156, 10, 66))
gl = ax.gridlines(crs=ccrs.PlateCarree(), draw_labels=False, linewidth=0.2,color='black', alpha=0.5,
linestyle=(3, (20, 5)), xlocs=np.arange(-180, 200, 10), x_inline=False, y_inline=False)
#xlabel_style = {'rotation': 0},ylabel_style = {'rotation': 0})
ax.coastlines(resolution='50m', alpha=1, linewidth=0.5)
ax.add_feature(cartopy.feature.BORDERS, alpha=1,linewidth=0.5)
ax.add_feature(cartopy.feature.LAKES, alpha=0.4)
plt.connect('button_press_event', mouse_click)
plt.show()
I tried to find about it but It's hard to understanding.

I found some way.
Change the projection.
fig = plt.figure()
ax = fig.add_axes([0, 0, 1, 1], projection=ccrs.PlateCarree())
ax.add_feature(cartopy.feature.LAND, facecolor='black')
ax.set_global()
In LambertConformal,x,y is different with lon,lat. But in PlateCarree, they are same.

Related

Matplotlib: how to get color bars that are one on top of each other as opposed to side by side?

I have the following code:
import matplotlib.pyplot as plt
import numpy as np
img1 = np.zeros([512,512])
img2 = np.zeros([512,512])
plt.figure(figsize=(10,10))
plt.imshow(img1, cmap='inferno')
plt.axis('off')
cba = plt.colorbar(shrink=0.25)
cba.ax.set_ylabel('Events / counts', fontsize=14)
cba.ax.tick_params(labelsize=12)
plt.imshow(img2, cmap='turbo', alpha=0.5)
plt.axis('off')
cba = plt.colorbar(shrink=0.25)
cba.ax.set_ylabel('Lifetime / ns)', fontsize=14)
cba.ax.tick_params(labelsize=12)
plt.tight_layout()
plt.show()
which produces the following output:
My question is, how can I get color bars that are on top of one another as opposed to next to each other? Ideally, I would like to get something like this:
You can grab the position of the ax and use it to create new axes for the colorbars. Here is an example:
import numpy as np
import matplotlib.pyplot as plt
from scipy import ndimage
data = ndimage.gaussian_filter(np.random.randn(512, 512), sigma=15, mode='nearest') * 20
fig, ax = plt.subplots()
im1 = ax.imshow(data, vmin=-1, vmax=0, cmap='viridis')
data[data < 0] = np.nan
im2 = ax.imshow(data, vmin=0.001, vmax=1, cmap='Reds_r')
ax.axis('off')
pos = ax.get_position()
bar_h = (pos.y1 - pos.y0) * 0.5 # 0.5 joins the two bars, e.g. 0.48 separates them a bit
ax_cbar1 = fig.add_axes([pos.x1 + 0.02, pos.y0, 0.03, bar_h])
cbar1 = fig.colorbar(im1, cax=ax_cbar1, orientation='vertical')
ax_cbar2 = fig.add_axes([pos.x1 + 0.02, pos.y1 - bar_h, 0.03, bar_h])
cbar2 = fig.colorbar(im2, cax=ax_cbar2, orientation='vertical')
plt.show()

How to add a hovering annotation on a bar plot with mplcursors

How can I modify this plot to show me the value of each bar upon hovering mouse?
sns.barplot(x = "date", y = "no_of_dogs", data = dogs_adopted_per_day, palette="husl")
plt.show()
You could employ mplcursors as follows:
import matplotlib.pyplot as plt
import mplcursors
import numpy as np
import pandas as pd
import seaborn as sns
df = pd.DataFrame({"date": pd.date_range('20210101', periods=10),
"no_of_dogs": np.random.randint(10, 30, 10)})
fig, ax = plt.subplots(figsize=(15, 5))
sns.barplot(x="date", y="no_of_dogs", data=df, palette="husl", ax=ax)
x_dates = df['date'].dt.strftime('%Y-%m-%d')
ax.set_xticklabels(labels=x_dates)
cursor = mplcursors.cursor(hover=True)
#cursor.connect("add")
def on_add(sel):
x, y, width, height = sel.artist[sel.target.index].get_bbox().bounds
sel.annotation.set(text=f"{x_dates[round(x)]}\n{height:.0f}",
position=(0, 20), anncoords="offset points")
sel.annotation.xy = (x + width / 2, y + height)
plt.show()

Matplotlib- Add a color bar below a multi-colored line subplot as shown in the image

I am having a multicolored line plot and I want to add a color bar under it in the same figure like as shown in the image below, Is it possible?
I have attached a color bar image as a reference which I took from another code.
My intention here is to use the color bar like a legend for each segment of the line in the plot.
Edit-1: I want to have the color bar using a mappable object such as an image, So don't want to create a new subplot for the sole purpose of the color bar.
Any suggestion is welcome. Thanks in Advance.
This is the code for multicolored line plot
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
Segments=[[[3,1],[6,1]],[[6,2],[9,2]],[[9,3],[12,3]],[[12,4],[15,4]], [[12,4],[15,4]]]
Points_1 = np.concatenate([Segments[:-1], Segments[1:]], axis=1)
lc = LineCollection(Points_1, colors=['r','g','b','y'], linewidths=2)
fig, ax = plt.subplots()
ax.add_collection(lc)
ax.autoscale()
plt.show()
This is a workaround I'am using:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
import matplotlib.colorbar as mcolorbar
import matplotlib.colors as mcolors
Segments=[[[3,1],[6,1]],[[6,2],[9,2]],[[9,3],[12,3]],[[12,4],[15,4]], [[12,4],[15,4]]]
Points_1 = np.concatenate([Segments[:-1], Segments[1:]], axis=1)
lc = LineCollection(Points_1, colors=['r','g','b','y'], linewidths=2)
fig, ax = plt.subplots(2, 1, gridspec_kw={'height_ratios' : [5,1]})
ax[0].add_collection(lc)
bounds = np.linspace(0, 1, 5)[:-1]
labels = ['Action1', 'Action2', 'Action3', 'Action4']
ax[0].set_xlim([0, 15])
ax[0].set_ylim([0, 10])
cb2 = mcolorbar.ColorbarBase(ax = ax[1], cmap = cmap, orientation = 'horizontal', extendfrac='auto')
cb2.set_ticks(bounds)
cb2.set_ticklabels(labels)
plt.tight_layout()
plt.show()
If you specifically want to avoid subplots, you can use a scalar mappable:
fig, ax = plt.subplots()
ax.add_collection(lc)
ax.autoscale()
cmap = mcolors.ListedColormap(['r','g','b','y'])
sm = plt.cm.ScalarMappable(cmap=cmap)
sm.set_array([]) # this line may be ommitted for matplotlib >= 3.1
cbar = fig.colorbar(sm, ax=ax, orientation='horizontal',aspect=90)
bounds = np.linspace(0, 1, 5)[:-1]
labels = ['Action1', 'Action2', 'Action3', 'Action4']
ax.set_xlim([0, 15])
ax.set_ylim([0, 10])
cbar.set_ticks(bounds)
cbar.set_ticklabels(labels)
plt.tight_layout()
plt.show()
This helped me to get what I asked.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
from matplotlib.collections import LineCollection
Segments=[[[3,1],[6,1]],[[6,2],[9,2]],[[9,3],[12,3]],[[12,4],[15,4]], [[12,4],[15,4]]]
Points_1 = np.concatenate([Segments[:-1], Segments[1:]], axis=1)
lc = LineCollection(Points_1, colors=['r','g','b','y'], linewidths=2)
fig, ax = plt.subplots()
ax.add_collection(lc)
ax.autoscale()
c=[1,2,3,4,5]
labels = ['Action1', 'Action2', 'Action3', 'Action4']
cmap = mcolors.ListedColormap(['r','g','b','y'])
norm = mcolors.BoundaryNorm([1,2,3,4,5],4)
sm = plt.cm.ScalarMappable(norm=norm, cmap=cmap)
sm.set_array([]) # this line may be ommitted for matplotlib >= 3.1
cbar=fig.colorbar(sm, ticks=c, orientation='horizontal')
cbar.set_ticklabels(['Action1', 'Action2', 'Action3', 'Action4'])
plt.show()

How to have a fast crosshair mouse cursor for subplots in matplotlib?

In this video of backtrader's matplotlib implementation https://youtu.be/m6b4Ti4P2HA?t=2008 I can see that a default and very fast and CPU saving crosshair mouse cursor seems to exist in matplotlib.
I would like to have the same kind of mouse cursor for a simple multi subplot plot in matplotlib like this:
import numpy as np
import matplotlib
matplotlib.use('QT5Agg')
matplotlib.rcParams['figure.figsize'] = (20.0, 22.0)
import matplotlib.pyplot as plt
fig = plt.figure()
ax1 = plt.subplot(2, 1, 1)
ax2 = plt.subplot(2, 1, 2, sharex=ax1)
ax1.plot(np.array(np.random.rand(100)))
ax2.plot(np.array(np.random.rand(100)))
plt.show()
So, if I am with my mouse in the lower subplot, I want to see directly and very precisely, which value of x/y in the lower plot corresponds to which value pair in the upper plot.
I have found other solutions to do this but they seem to be very slow compared to the implementation in the video.
You can create a crosshair cursor via mplcursors. sel.extras.append() takes care that the old cursor is removed when a new is drawn. With sel.annotation.set_text you can adapt the popup annotation shown. To leave out the annotation, use sel.annotation.set_visible(False). To find the corresponding y-value in the other subplot, np.interp with the data extracted from the curve can be used.
import numpy as np
import matplotlib.pyplot as plt
import mplcursors
def crosshair(sel):
x, y2 = sel.target
y1 = np.interp( sel.target[0], plot1.get_xdata(), plot1.get_ydata() )
sel.annotation.set_text(f'x: {x:.2f}\ny1: {y1:.2f}\ny2: {y2:.2f}')
# sel.annotation.set_visible(False)
hline1 = ax1.axhline(y1, color='k', ls=':')
vline1 = ax1.axvline(x, color='k', ls=':')
vline2 = ax2.axvline(x, color='k', ls=':')
hline2 = ax2.axhline(y2, color='k', ls=':')
sel.extras.append(hline1)
sel.extras.append(vline1)
sel.extras.append(hline2)
sel.extras.append(vline2)
fig = plt.figure(figsize=(15, 10))
ax1 = plt.subplot(2, 1, 1)
ax2 = plt.subplot(2, 1, 2, sharex=ax1)
plot1, = ax1.plot(np.array(np.random.uniform(-1, 1, 100).cumsum()))
plot2, = ax2.plot(np.array(np.random.uniform(-1, 1, 100).cumsum()))
cursor = mplcursors.cursor(plot2, hover=True)
cursor.connect('add', crosshair)
plt.show()
Here is an alternative implementation that stores the data in global variables and moves the lines (instead of deleting and recreating them):
import numpy as np
import matplotlib.pyplot as plt
import mplcursors
def crosshair(sel):
x = sel.target[0]
y1 = np.interp(x, plot1x, plot1y)
y2 = np.interp(x, plot2x, plot2y)
sel.annotation.set_visible(False)
hline1.set_ydata([y1])
vline1.set_xdata([x])
hline2.set_ydata([y2])
vline2.set_xdata([x])
hline1.set_visible(True)
vline1.set_visible(True)
hline2.set_visible(True)
vline2.set_visible(True)
fig = plt.figure(figsize=(15, 10))
ax1 = plt.subplot(2, 1, 1)
ax2 = plt.subplot(2, 1, 2, sharex=ax1)
plot1, = ax1.plot(np.array(np.random.uniform(-1, 1, 100).cumsum()))
plot2, = ax2.plot(np.array(np.random.uniform(-1, 1, 100).cumsum()))
plot1x = plot1.get_xdata()
plot1y = plot1.get_ydata()
plot2x = plot2.get_xdata()
plot2y = plot2.get_ydata()
hline1 = ax1.axhline(plot1y[0], color='k', ls=':', visible=False)
vline1 = ax1.axvline(plot1x[0], color='k', ls=':', visible=False)
hline2 = ax2.axhline(plot2y[0], color='k', ls=':', visible=False)
vline2 = ax2.axvline(plot2x[0], color='k', ls=':', visible=False)
cursor = mplcursors.cursor([plot1, plot2], hover=True)
cursor.connect('add', crosshair)
plt.show()
Sorry for the late answer, but I was horrified by how much code was suggested above, when there is this one-liner on matplotlib to do a simple crosshair accross different axes. It won't show your labels but it's CPU-light.
from matplotlib.widgets import MultiCursor
cursor = MultiCursor(fig.canvas, (ax[0], ax[1]), color='r',lw=0.5, horizOn=True, vertOn=True)

matplotlib: put two x-y plots in one

I am using the following codes to plot several data points (xi, yi)
import numpy as np
import matplotlib.pyplot as plt
xi = np.array(data_df[['col_A']])
yi = np.array(data_df[['col_B']])
plt.figure()
plt.plot(xi, yi)
x = np.linspace(0, 30, 30)
y= np.exp(x*0.16)
plt.plot(x, y)
plt.show()
I want the plot to look like this:
Thanks!
User subplots to plot more than 1 plots in 1 figure.You need to call plt.show() only once.
import numpy as np
import matplotlib.pyplot as plt
xi = np.array(data_df[['col_A']])
yi = np.array(data_df[['col_B']])
plt.figure()
plt.subplot(2,1,1)
plt.plot(xi, yi)
plt.subplot(2,1,2)
x = np.linspace(0, 30, 30)
y= np.exp(x*0.16)
plt.plot(x, y)
plt.show()

Resources