Getting display coordinated from cartopy generated image plot - python

I have need to plot point on the map, produce PNG image on that map and, output display coordinated of the plotted point.
Using cartopy I could get the map I wanted and plot a point in given lon/lat coordinates.
I cannot figure out how to get the pixel coordinates out. I tried to follow simple matplotlib tutorial https://matplotlib.org/users/transforms_tutorial.html But it does not work as expected in this situation
import cartopy.crs as ccrs
import cartopy.feature as cfeature
import matplotlib.pyplot as plt
# Create Mercator projection with dateline in the middle:
from matplotlib import lines
fig = plt.figure(figsize=(10, 10))
ax = plt.axes(projection=ccrs.Mercator(central_longitude=26,))
ax.set_extent([19, 33, 59.5, 70.5], crs=ccrs.PlateCarree())
LAND = cfeature.NaturalEarthFeature('physical', 'land', '50m',
edgecolor='face',
facecolor=cfeature.COLORS['land'], zorder=-1)
ax.add_feature(LAND)
ax.coastlines(resolution='50m')
ax.add_feature(cfeature.NaturalEarthFeature('cultural', 'admin_0_boundary_lines_land',
'50m', edgecolor='black', facecolor='none'))
plt.plot([26.7042], [60.8679], color='blue', linewidth=2, marker='o',
transform=ccrs.PlateCarree(),
)
fig.canvas.draw()
# print image x y coordinates of point 60.8679° N, 26.7042° E here
plt.show()

Related

Colorbar for sns.jointplot "kde"-style on the side

I'm trying to plot a colorbar next to my density plot with marginal axes.
It does plot the colorbar, but unfortunately not on the side.
That's what a tried so far:
sns.jointplot(x,y, data=df3, kind="kde", color="skyblue", legend=True, cbar=True,
xlim=[-10,40], ylim=[900,1040])
It looks like this:
I also tried this:
from matplotlib import pyplot as plt
import seaborn as sns
import numpy as np
kdeplot = sns.jointplot(x=tumg, y=pumg, kind="kde")
plt.subplots_adjust(left=0.2, right=0.8, top=0.8, bottom=0.2)
cbar_ax = kdeplot.fig.add_axes([.85, .25, .05, .4])
plt.colorbar(cax=cbar_ax)
plt.show()
But with the second option I'm getting a runtime error:
No mappable was found to use for colorbar creation.
First define a mappable such as an image (with imshow) or a contour set (with contourf).
Does anyone have an idea how to solve the problem?
There only seems to be information for a colorbar when effectively creating the colorbar.
So, an idea is to combine both approaches: add a colorbar via kdeplot, and then move it to the desired location. This will leave the main joint plot with insufficient width, so its width also should be adapted:
from matplotlib import pyplot as plt
import seaborn as sns
import numpy as np
# create some dummy data: gaussian multivariate with 10 centers with each 1000 points
tumg = np.random.normal(np.tile(np.random.uniform(10, 20, 10), 1000), 2)
pumg = np.random.normal(np.tile(np.random.uniform(10, 20, 10), 1000), 2)
kdeplot = sns.jointplot(x=tumg, y=pumg, kind="kde", cbar=True)
plt.subplots_adjust(left=0.1, right=0.8, top=0.9, bottom=0.1)
# get the current positions of the joint ax and the ax for the marginal x
pos_joint_ax = kdeplot.ax_joint.get_position()
pos_marg_x_ax = kdeplot.ax_marg_x.get_position()
# reposition the joint ax so it has the same width as the marginal x ax
kdeplot.ax_joint.set_position([pos_joint_ax.x0, pos_joint_ax.y0, pos_marg_x_ax.width, pos_joint_ax.height])
# reposition the colorbar using new x positions and y positions of the joint ax
kdeplot.fig.axes[-1].set_position([.83, pos_joint_ax.y0, .07, pos_joint_ax.height])
plt.show()

How to zoom in cartopy

I am attempting to edit the boundaries of a map using the EuroPP() projection in cartopy. I have written the following code, but hope to zoom in on the Scandinavia region. Any suggestions? Thanks!
import matplotlib.pyplot as plt
%matplotlib inline
import cartopy
import cartopy.crs as ccrs
import cartopy.io.img_tiles as cimgt
plt.figure(figsize=(5, 6))
ax = plt.axes(projection=ccrs.EuroPP())
ax.coastlines(resolution='50m', linewidth = 0.5)
ax.gridlines()
ax.add_feature(cartopy.feature.BORDERS, linestyle='-', alpha=.5)
ax.add_feature(cartopy.feature.OCEAN)
tile = cimgt.StamenTerrain()
ax.add_image(tile,5)
plt.show()
The code produces the following image:
You can use set_extent to "zoom" into the desired region, in your case, just create a bounding box around Scandinavia. Add the following code right before plt.show():
ax.set_extent([0, 43, 54, 75])
Output:

Adapting coordinates in cartopy depending on the projection of a plot

In the following example, I am losing my point (i.e., I don't understand the change in coordinates) if I am using the ccrs.Mercator() projection instead of the ccrs.PlateCarree():
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
mypt = (6, 56)
ax0 = plt.subplot(221, projection=ccrs.PlateCarree()) # OK
ax1 = plt.subplot(222, projection=ccrs.Mercator()) # NOT OK
ax2 = plt.subplot(224, projection=ccrs.Mercator()) # NOT OK
def plotpt(ax, extent=(-15,15,46,62)):
ax.plot(mypt[0], mypt[1], 'r*', ms=20)
ax.set_extent(extent)
ax.coastlines(resolution='50m')
ax.gridlines(draw_labels=True)
plotpt(ax0)
plotpt(ax1)
plotpt(ax2, extent=(-89,89,-89,89))
plt.show()
It looks like the coordinates of my point go from (6,56) to (0,0)
What am I missing?
Why is the behaviour correct with ccrs.PlateCarree() and not with ccrs.Mercator()? Should I add any transform somewhere?
[EDIT with the solution]
My initial confusion came from the fact that projection applies to the plot, while transform applies to the data, meaning they should be set different when they do not share the same system - my first attempts with transform where wrong as in ax1 below, ax1bis is the solution.
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
mypt = (6, 56)
ax0 = plt.subplot(221, projection=ccrs.PlateCarree())
ax1 = plt.subplot(222, projection=ccrs.Mercator())
ax1bis = plt.subplot(223, projection=ccrs.Mercator())
ax2 = plt.subplot(224, projection=ccrs.Mercator())
def plotpt(ax, extent=(-15,15,46,62), **kwargs):
ax.plot(mypt[0], mypt[1], 'r*', ms=20, **kwargs)
ax.set_extent(extent)
ax.coastlines(resolution='50m')
ax.gridlines(draw_labels=True)
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('left')
plotpt(ax0) # correct because projection and data share the same system
plotpt(ax1, transform=ccrs.Mercator()) # WRONG
plotpt(ax1bis, transform=ccrs.PlateCarree()) # Correct, projection and transform are different!
plotpt(ax2, extent=(-89,89,-89,89), transform=ccrs.Mercator()) # WRONG
plt.show()
Yes, you should add the transform keyword to the plot call. You should also specify the coordinate system you want to set the extents in:
def plotpt(ax, extent=(-15,15,46,62)):
ax.plot(mypt[0], mypt[1], 'r*', ms=20, transform=ccrs.PlateCarree())
ax.set_extent(extent, crs=ccrs.PlateCarree())
ax.coastlines(resolution='50m')
ax.gridlines(draw_labels=True)
A basic guide on transforms and projections is now available in the cartopy documentation http://scitools.org.uk/cartopy/docs/latest/tutorials/understanding_transform.html. To avoid surprises, you should always specify a transform when plotting data on a map.

mplot3D fill_between extends over axis limits

I have questions related to creating a simple lineplot in Python with mplot3D where the area under the plot is filled. I am using Python 2.7.5 on RedHatEnterprise 7.2, matplotlib 1.2.0 and numpy 1.7.2.
Using the code below, I am able to generate a line plot. This is displayed as expected with the beginning / end of the plot set by the limits of the imported data set.
I am then trying to fill the area between the line plot and -0.1 using the answer given by Bart from Plotting a series of 2D plots projected in 3D in a perspectival way. This works, however, the filled area is continued beyond the limits of the data set. This is also the case when running the example from the link.
This screen shot shows the plot generated with filled area extending beyond the set axis limits.
How do I achieve that the filled area is only the range of the data set or the axis limits whichever is smaller?
How do I add a legend for those plots onto the figure?
Code as follows:
from numpy import *
import matplotlib.pylab as plt
from mpl_toolkits.mplot3d import Axes3D
x,y = genfromtxt("data.dat",unpack=True)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.add_collection3d(plt.fill_between(x,y,-0.1, color='orange', alpha=0.3,label="filled plot"),1, zdir='y')
ax.plot(x,y,1,zdir="y",label="line plot")
ax.legend()
ax.set_xlim3d(852.353,852.359)
ax.set_zlim3d(-0.1,5)
ax.set_ylim3d(0,2)
ax.get_xaxis().get_major_formatter().set_useOffset(False)
plt.show()
I don't know how to put fill_between working the way you want it to, but I can provide an alternative using a 3D polygon:
from numpy import *
import matplotlib.pylab as plt
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Poly3DCollection # New import
#x,y = genfromtxt("data.dat",unpack=True)
# Generated some random data
w = 3
x,y = np.arange(100), np.random.randint(0,100+w,100)
y = np.array([y[i-w:i+w].mean() for i in range(3,100+w)])
z = np.zeros(x.shape)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
#ax.add_collection3d(plt.fill_between(x,y,-0.1, color='orange', alpha=0.3,label="filled plot"),1, zdir='y')
verts = [(x[i],z[i],y[i]) for i in range(len(x))] + [(x.max(),0,0),(x.min(),0,0)]
ax.add_collection3d(Poly3DCollection([verts],color='orange')) # Add a polygon instead of fill_between
ax.plot(x,z,y,label="line plot")
ax.legend()
ax.set_ylim(-1,1)
plt.show()
The code above generates some random data. Builds vertices from it and plots a polygon with those vertices. This will give you the plot you wish (but does not use fill_between). The result is:

How to draw a line outside of an axis in matplotlib (in figure coordinates)

Matplotlib has a function that writes text in figure coordinates (.figtext())
Is there a way to do the same but for drawing lines?
In particular my goal is to draw lines to group some ticks on the y-axis together.
Tested in python 3.8.12, matplotlib 3.4.3
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
x = np.linspace(0,10,100)
y = np.sin(x)*(1+x)
fig, ax = plt.subplots()
ax.plot(x,y,label='a')
# new clear axis overlay with 0-1 limits
ax2 = plt.axes([0,0,1,1], facecolor=(1,1,1,0))
x,y = np.array([[0.05, 0.1, 0.9], [0.05, 0.5, 0.9]])
line = Line2D(x, y, lw=5., color='r', alpha=0.4)
ax2.add_line(line)
plt.show()
But if you want to align with ticks, then why not use plot coordinates?

Categories