I want to plot a vector field with vectors representing a displacement between one point to another on the map with cartopy.
My code works as expected when using the PlateCarree() transformation, but arrow length is several orders of magnitude off for all the other projections I tested.
Here is a MWE that should illustrate quite clearly the issue:
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import numpy
# Want to test for several different projections
projections = [
ccrs.PlateCarree(),
ccrs.EqualEarth(),
ccrs.Mollweide(),
ccrs.AzimuthalEquidistant(),
]
# ALl the coordinates will be given in the PlateCarree coordinate system.
coordinate_ccrs = ccrs.PlateCarree()
# We want N**2 points over the latitude/longitude values.
N = 5
lat, lon = numpy.meshgrid(numpy.linspace(-80, 80, N), numpy.linspace(-170, 170, N))
lat, lon = lat.flatten(), lon.flatten()
# We want arrows to appear, let make a small perturbation and try
# to do an arrow from (lon, lat) to (lon + perturbation, lat + perturbation).
rng = numpy.random.default_rng()
perturbation_amplitude = 10
lat_perturbation = perturbation_amplitude * rng.random(N * N)
lon_perturbation = perturbation_amplitude * rng.random(N * N)
# Create the matplotlib figure and axes, no projection for the moment as this
# will be changed later.
fig, axes = plt.subplots(2, 2)
axes = axes.flatten()
for i, projection in enumerate(projections):
# Replace the existing ax with an ax with the desired projection.
ax = axes[i]
fig.delaxes(ax)
ax = axes[i] = fig.add_subplot(2, 2, i + 1, projection=projection)
# Make the plot readable.
ax.set_global()
ax.gridlines(draw_labels="x")
# Non pertubed points are plotted in black.
ax.plot(lon, lat, "k.", ms=5, transform=coordinate_ccrs)
# Perturbed points are plotted in red.
ax.plot(
lon + lon_perturbation,
lat + lat_perturbation,
"r.",
ms=5,
transform=coordinate_ccrs,
)
# We try to draw arrows from a given black dot to its corresponding
# red dot.
ax.quiver(
lon,
lat,
lon_perturbation,
lat_perturbation,
transform=coordinate_ccrs,
# From https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.quiver.html?highlight=quiver#matplotlib.axes.Axes.quiver
# look at the documentation of the "scale_unit" parameter.
# The next 3 parameters are what matplotlib tell us to do. From
# matplotlib documentation:
# To plot vectors in the x-y plane, with u and v having the same units
# as x and y, use angles='xy', scale_units='xy', scale=1.
angles="xy",
scale_units="xy",
scale=1,
# Simply make the arrows nicer, removing these last 3 parameters do not
# solve the issue.
minshaft=2,
minlength=0.5,
width=0.002,
)
# Show everything
plt.show()
which display on the screen the following image:
The PlateCarree transformation is the only one displaying arrows. In fact, there are arrows in the other 3 projections, but I order to see them I need to scale them by 10000 with scale=0.00001 in the call to quiver, which gives:
Did I make a mistake when using cartopy API, is this expected behaviour and I missed something in the documentation, or is this a bug?
while there's quite some debate on github about cartopy's implementation of quiver-plot transformations GitHub-issues there is in fact a way on how to get your plot look as you want it to look...
However, while thinking about this... I noticed that there's a thing that you might want to consider when using projected quiver-plots...
As I see it, the re-projected arrows would would most probably need to be curved to really visualize the same direction as provided in the original data!
(in the input-crs the arrow points as a straight line from point A to B, but if you re-project the points, the "straight line" that connected A and B is now in general a curved line, and so if the original direction was correct, I think the new direction should be indicated as a curved arrow...)
That being said, you could achieve what you want by transforming the points manually instead of letting cartopy do the job:
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import numpy
# Want to test for several different projections
projections = [
ccrs.PlateCarree(),
ccrs.EqualEarth(),
ccrs.Mollweide(),
ccrs.AzimuthalEquidistant(),
]
# ALl the coordinates will be given in the PlateCarree coordinate system.
coordinate_ccrs = ccrs.PlateCarree()
# We want N**2 points over the latitude/longitude values.
N = 5
lat, lon = numpy.meshgrid(numpy.linspace(-80, 80, N), numpy.linspace(-170, 170, N))
lat, lon = lat.flatten(), lon.flatten()
# We want arrows to appear, let make a small perturbation and try
# to do an arrow from (lon, lat) to (lon + perturbation, lat + perturbation).
rng = numpy.random.default_rng()
perturbation_amplitude = 10
lat_perturbation = perturbation_amplitude * rng.random(N * N)
lon_perturbation = perturbation_amplitude * rng.random(N * N)
# Create the matplotlib figure and axes, no projection for the moment as this
# will be changed later.
fig, axes = plt.subplots(2, 2)
axes = axes.flatten()
for i, projection in enumerate(projections):
# Replace the existing ax with an ax with the desired projection.
ax = axes[i]
fig.delaxes(ax)
ax = axes[i] = fig.add_subplot(2, 2, i + 1, projection=projection)
# Make the plot readable.
ax.set_global()
ax.gridlines(draw_labels="x")
# Non pertubed points are plotted in black.
ax.plot(lon, lat, "k.", ms=5, transform=coordinate_ccrs)
# Perturbed points are plotted in red.
ax.plot(
lon + lon_perturbation,
lat + lat_perturbation,
"r.",
ms=5,
transform=coordinate_ccrs,
)
xy_start = projection.transform_points(coordinate_ccrs, lon, lat)[:,:-1].T
xy_end = projection.transform_points(coordinate_ccrs, lon + lon_perturbation,
lat + lat_perturbation)[:,:-1].T
# We try to draw arrows from a given black dot to its corresponding
# red dot.
ax.quiver(
*xy_start,
*(xy_end - xy_start),
# From https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.quiver.html?highlight=quiver#matplotlib.axes.Axes.quiver
# look at the documentation of the "scale_unit" parameter.
# The next 3 parameters are what matplotlib tell us to do. From
# matplotlib documentation:
# To plot vectors in the x-y plane, with u and v having the same units
# as x and y, use angles='xy', scale_units='xy', scale=1.
angles="xy",
scale_units="xy",
scale=1,
# Simply make the arrows nicer, removing these last 3 parameters do not
# solve the issue.
minshaft=2,
minlength=0.5,
width=0.002,
)
# Show everything
plt.show()
Related
I am trying to plot some data in polar coordinates (I am currently using the polar projection):
The code I am using is the following:
import matplotlib.pyplot as plt
import numpy as np
# Create radial and angular array
r = np.linspace(1.0,10,11)
t = np.linspace(0.0,0.5*np.pi,101)
# Define the quantity that I want to plot
z = np.zeros((len(t),len(r)))
for yval in range(len(r)):
z[:,yval] = np.cos(16.0*t)/r[yval]
#Create the figure
f = plt.figure(figsize=(13,8))
ax = plt.subplot(111, projection='polar')
ax.set_rorigin(-1)
#Plot the data
pcm = ax.pcolormesh(t,r,z.T,cmap = 'hot',shading='gouraud')
ax.set_xlim([0.0,0.5*np.pi])
ax.set_ylim([1.0,10.0])
#Add colorbar and show
bar = f.colorbar(pcm)
plt.show()
So far I have no problem, but I would like to zoom on a particular region of this plot.
However, when I set the axes range the axes is still polar, therefore I cannot zoom on a "cartesian" region of the domain (i.e. a square box).
A possible option would be to transform the data into cartesian coordinates, but when I do it I lose a lot of resolution in the inner part of the domain, which is something that I should absolutely avoid.
How can I select a rectangular zone of a plot in polar coordinates without transforming by hand the data? And in case I have to switch to cartesian coordinates, is there any matplotlib or python function that does it while taking care of the resolution in the inner regions of the domain?
Thanks in advance
You can create an X, Y mesh yourself that is has a higher resolution on the inner part of the domain and use that with ax.pcolormesh()
# Create radial and angular array
r = np.linspace(1.0,10,11)
t = np.linspace(0.0,0.5*np.pi,101)
# Define the quantity that I want to plot
z = np.zeros((len(t),len(r)))
for yval in range(len(r)):
z[:,yval] = np.cos(16.0*t)/r[yval]
#Create the figure, bigger figsize to make the resulting plot square
f = plt.figure(figsize=(13,10))
ax = plt.subplot(111) # Drop back to XY coordinates
# Generate the XY corners of the colormesh
X = np.array([[ri*np.cos(j) for j in t] for ri in r])
Y = np.array([[ri*np.sin(j) for j in t] for ri in r])
#Plot the data
pcm = ax.pcolormesh(X,Y,z.T,cmap = 'hot',shading='gouraud')
#Add colorbar and show
bar = f.colorbar(pcm)
plt.show()
The figure from the question
The figure generated by code above
A way to do this is to create an expanded polar plot and then clip a rectangle of it. A picture is worth a thousand words:
Here is a function that allows you to do so. The arguments are the original axes, the xlims and ylims of the region to be zoomed and the inset axes bounds (x0, y0, width, height) in the original axes coordinates. The function outputs a cartesian ax with the specified limits, a polar axes where you can plot and the rmax value you need to set AFTER plotting (if you do it before, it will change after plotting).
def create_polar_zoom_inset(ax, xlims, ylims, inset_bounds):
# Create cartesian axes for inset
ax_inset_cart = ax.inset_axes(inset_bounds)
ax_inset_cart.set_xlim(xlims)
ax_inset_cart.set_ylim(ylims)
# Calculate location of expanded polar inset
# Scale factor from data to axes coordinates
xscalefactor = inset_bounds[2]/(xlims[1] - xlims[0])
yscalefactor = inset_bounds[3]/(ylims[1] - ylims[0])
# Center of expanded polar inset
center_inset_polar = [
inset_bounds[0] - xlims[0]*xscalefactor,
inset_bounds[1] - ylims[0]*yscalefactor
]
# Max value of r in the inset
rmax_inset = 2*np.sqrt(np.power(xlims, 2).max() + np.power(ylims, 2).max())
# Size of the expanded polar inset
size_inset_polar = [2*rmax_inset*xscalefactor, 2*rmax_inset*yscalefactor]
# Create expanded polar inset
polar_inset_bounds = [
center_inset_polar[0] - 0.5*size_inset_polar[0],
center_inset_polar[1] - 0.5*size_inset_polar[1],
size_inset_polar[0],
size_inset_polar[1]
]
ax_inset_polar = ax.inset_axes(polar_inset_bounds, projection="polar")
ax_inset_polar.set_facecolor("None")
# Remove tick labels from expanded polar inset
ax_inset_polar.xaxis.set_ticklabels([])
ax_inset_polar.yaxis.set_ticklabels([])
# Clip elements of the expanded inset outside the cartesian inset
ax_inset_polar.patch = ax_inset_cart.patch
for axis in [ax_inset_polar.xaxis, ax_inset_polar.yaxis]:
axis.set_clip_path(ax_inset_cart.patch)
ax_inset_polar.spines['polar'].set_clip_path(ax_inset_cart.patch)
return ax_inset_cart, ax_inset_polar, rmax_inset
The code in your example is especially hard since the origin of the axes is not (0,0) but (-1,-1). That would need additional tinkering. But if we set rorigin to 0 (as it will be usually the case), the code would look as follows
# Create radial and angular array
r = np.linspace(1.0,10,11)
t = np.linspace(0.0,0.5*np.pi,101)
# Define the quantity that I want to plot
z = np.zeros((len(t),len(r)))
for yval in range(len(r)):
z[:,yval] = np.cos(16.0*t)/r[yval]
#Create the figure
f = plt.figure(figsize=(13,8))
ax = plt.subplot(111, projection='polar')
ax.set_rorigin(0)
#Plot the data
pcm = ax.pcolormesh(t,r,z.T,cmap = 'hot',shading='gouraud')
ax.set_xlim([0.0,0.5*np.pi])
ax.set_ylim([1.0,10.0])
#Add colorbar and show
bar = f.colorbar(pcm)
#Create inset
ax_c, ax_p, rmax_inset = create_polar_zoom_inset(
ax, xlims=[0., 2.], ylims=[1, 2], inset_bounds=[0.4, 0.3, 0.6, 0.3])
#Plot on inset
ax_p.pcolormesh(t,r,z.T,cmap = 'hot',shading='gouraud')
#Make rorigin and rmin coincide with the original plot
ax_p.set_rorigin(0)
ax_p.set_rmin(1)
#Set rmax
ax_p.set_rmax(rmax_inset)
plt.show()
I want to have the markers of a scatter plot match a radius given in the data coordinates.
I've read in pyplot scatter plot marker size, that the marker size is given as the area of the marker in points^2.
I tried transforming the given radius into points via axes.transData and then calculating the area via pi * r^2, but I did not succeed.
Maybe I did the transformation wrong.
It could also be that running matplotlib from WSL via VcXsrv is causing this problem.
Here is an example code of what I want to accomplish, with the results as an image below the code:
import matplotlib.pyplot as plt
from numpy import pi
n = 16
# create a n x n square with a marker at each point
x_data = []
y_data = []
for x in range(n):
for y in range(n):
x_data.append(x)
y_data.append(y)
fig,ax = plt.subplots(figsize=[7,7])
# important part:
# calculate the marker size so that the markers touch
# radius in data coordinates:
r = 0.5
# radius in display coordinates:
r_ = ax.transData.transform([r,0])[0] - ax.transData.transform([0,0])[0]
# marker size as the area of a circle
marker_size = pi * r_**2
ax.scatter(x_data, y_data, s=marker_size, edgecolors='black')
plt.show()
When I run it with s=r_ I get the result on the left and with s=marker_size I get the result on the right of the following image:
The code looks perfectly fine. You can see this if you plot just 4 points (n=2):
The radius is (almost) exactly the r=0.5 coordinate-units that you wanted to have. wait, almost?!
Yes, the problem is that you determine the coordinate-units-to-figure-points size before plotting, so before setting the limits, which influence the coordinate-units but not the overall figure size...
Sounded strange? Perhaps. The bottom line is that you determine the coordinate transformation with the default axis-limits ((0,1) x (0,1)) and enlarges them afterwards to (-0.75, 15.75)x(-0.75, 15.75)... but you are not reducing the marker-size.
So either set the limits to the known size before plotting:
ax.set_xlim((0,n-1))
ax.set_ylim((0,n-1))
The complete code is:
import matplotlib.pyplot as plt
from numpy import pi
n = 16
# create a n x n square with a marker at each point as dummy data
x_data = []
y_data = []
for x in range(n):
for y in range(n):
x_data.append(x)
y_data.append(y)
# open figure
fig,ax = plt.subplots(figsize=[7,7])
# set limits BEFORE plotting
ax.set_xlim((0,n-1))
ax.set_ylim((0,n-1))
# radius in data coordinates:
r = 0.5 # units
# radius in display coordinates:
r_ = ax.transData.transform([r,0])[0] - ax.transData.transform([0,0])[0] # points
# marker size as the area of a circle
marker_size = pi * r_**2
# plot
ax.scatter(x_data, y_data, s=marker_size, edgecolors='black')
plt.show()
... or scale the markers's size according to the new limits (you will need to know them or do the plotting again)
# plot with invisible color
ax.scatter(x_data, y_data, s=marker_size, color=(0,0,0,0))
# calculate scaling
scl = ax.get_xlim()[1] - ax.get_xlim()[0]
# plot correctly (with color)
ax.scatter(x_data, y_data, s=marker_size/scl**2, edgecolors='blue',color='red')
This is a rather tedious idea, because you need to plot the data twice but you keep the autosizing of the axes...
There obviously remains some spacing. This is due a misunderstanding of the area of the markers. We are not talking about the area of the symbol (in this case a circle) but of a bounding box of the marker (imagine, you want to control the size of a star or an asterix as marker... one would never calculate the actual area of the symbol).
So calculating the area is not pi * r_**2 but rather a square: (2*r_)**2
# open figure
fig,ax = plt.subplots(figsize=[7,7])
# setting the limits
ax.set_xlim((0,n-1))
ax.set_ylim((0,n-1))
# radius in data coordinates:
r = 0.5 # units
# radius in display coordinates:
r_ = ax.transData.transform([r,0])[0] - ax.transData.transform([0,0])[0] # points
# marker size as the area of a circle
marker_size = (2*r_)**2
# plot
ax.scatter(x_data, y_data, s=marker_size,linewidths=1)
#ax.plot(x_data, y_data, "o",markersize=2*r_)
plt.show()
As soon as you add an edge (so a non-zero border around the markers), they will overlap:
If even gets more confusing if you use plot (which is faster if all markers should have the same size as the docs state as "Notes"). The markersize is only the width (not the area) of the marker:
ax.plot(x_data, y_data, "o",markersize=2*r_,color='magenta')
I am trying to reproduce a plot like this:
So the requirements are actually that the grid (that is to be present just on the left side) behaves just like a grid, that is, if we zoom in and out, it is always there present and not dependent on specific x-y limits for the actual data.
Unfortunately there is no diagonal version of axhline/axvline (open issue here) so I was thinking about using the grid from polar plots.
So for that I have two problems:
This answer shows how to overlay a polar axis on top of a rectangular one, but it does not match the origins and x-y values. How can I do that?
I also tried the suggestion from this answer for having polar plots using ax.set_thetamin/max but I get an AttributeError: 'AxesSubplot' object has no attribute 'set_thetamin' How can I use these functions?
This is the code I used to try to add a polar grid to an already existing rectangular plot on ax axis:
ax_polar = fig.add_axes(ax, polar=True, frameon=False)
ax_polar.set_thetamin(90)
ax_polar.set_thetamax(270)
ax_polar.grid(True)
I was hoping I could get some help from you guys. Thanks!
The mpl_toolkits.axisartist has the option to plot a plot similar to the desired one. The following is a slightly modified version of the example from the mpl_toolkits.axisartist tutorial:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cbook as cbook
from mpl_toolkits.axisartist import SubplotHost, ParasiteAxesAuxTrans
from mpl_toolkits.axisartist.grid_helper_curvelinear import GridHelperCurveLinear
import mpl_toolkits.axisartist.angle_helper as angle_helper
from matplotlib.projections import PolarAxes
from matplotlib.transforms import Affine2D
# PolarAxes.PolarTransform takes radian. However, we want our coordinate
# system in degree
tr = Affine2D().scale(np.pi/180., 1.) + PolarAxes.PolarTransform()
# polar projection, which involves cycle, and also has limits in
# its coordinates, needs a special method to find the extremes
# (min, max of the coordinate within the view).
# 20, 20 : number of sampling points along x, y direction
extreme_finder = angle_helper.ExtremeFinderCycle(20, 20,
lon_cycle=360,
lat_cycle=None,
lon_minmax=None,
lat_minmax=(0, np.inf),)
grid_locator1 = angle_helper.LocatorDMS(36)
tick_formatter1 = angle_helper.FormatterDMS()
grid_helper = GridHelperCurveLinear(tr,
extreme_finder=extreme_finder,
grid_locator1=grid_locator1,
tick_formatter1=tick_formatter1
)
fig = plt.figure(1, figsize=(7, 4))
fig.clf()
ax = SubplotHost(fig, 1, 1, 1, grid_helper=grid_helper)
# make ticklabels of right invisible, and top axis visible.
ax.axis["right"].major_ticklabels.set_visible(False)
ax.axis["right"].major_ticks.set_visible(False)
ax.axis["top"].major_ticklabels.set_visible(True)
# let left axis shows ticklabels for 1st coordinate (angle)
ax.axis["left"].get_helper().nth_coord_ticks = 0
# let bottom axis shows ticklabels for 2nd coordinate (radius)
ax.axis["bottom"].get_helper().nth_coord_ticks = 1
fig.add_subplot(ax)
## A parasite axes with given transform
## This is the axes to plot the data to.
ax2 = ParasiteAxesAuxTrans(ax, tr)
## note that ax2.transData == tr + ax1.transData
## Anything you draw in ax2 will match the ticks and grids of ax1.
ax.parasites.append(ax2)
intp = cbook.simple_linear_interpolation
ax2.plot(intp(np.array([150, 230]), 50),
intp(np.array([9., 3]), 50),
linewidth=2.0)
ax.set_aspect(1.)
ax.set_xlim(-12, 1)
ax.set_ylim(-5, 5)
ax.grid(True, zorder=0)
wp = plt.Rectangle((0,-5),width=1,height=10, facecolor="w", edgecolor="none")
ax.add_patch(wp)
ax.axvline(0, color="grey", lw=1)
plt.show()
I am trying to create a 3-D plot and a 2-D plot side-by-side in python. I need equal aspect ratios for both plots, which I managed using code provided by this answer: https://stackoverflow.com/a/31364297/125507. The problem I'm having now is how to effectively "crop" the 3-D plot so it doesn't take up so much white space. That is to say, I want to reduce the length of the X and Y axes while maintaining equal scale to the (longer) Z-axis. Here is a sample code and plot:
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
import matplotlib.pyplot as plt
import numpy as np
def set_axes_equal(ax):
'''Make axes of 3D plot have equal scale so that spheres appear as spheres,
cubes as cubes, etc.. This is one possible solution to Matplotlib's
ax.set_aspect('equal') and ax.axis('equal') not working for 3D.
Input
ax: a matplotlib axis, e.g., as output from plt.gca().
'''
x_limits = ax.get_xlim3d()
y_limits = ax.get_ylim3d()
z_limits = ax.get_zlim3d()
x_range = abs(x_limits[1] - x_limits[0])
x_middle = np.mean(x_limits)
y_range = abs(y_limits[1] - y_limits[0])
y_middle = np.mean(y_limits)
z_range = abs(z_limits[1] - z_limits[0])
z_middle = np.mean(z_limits)
# The plot bounding box is a sphere in the sense of the infinity
# norm, hence I call half the max range the plot radius.
plot_radius = 0.5*max([x_range, y_range, z_range])
ax.set_xlim3d([x_middle - plot_radius, x_middle + plot_radius])
ax.set_ylim3d([y_middle - plot_radius, y_middle + plot_radius])
ax.set_zlim3d([z_middle - plot_radius, z_middle + plot_radius])
ax = [None]*2
fig = plt.figure()
ax[0] = fig.add_subplot(121, projection='3d', aspect='equal')
ax[1] = fig.add_subplot(122, aspect='equal')
nn = 30
phis = np.linspace(0,np.pi, nn).reshape(1,nn)
psis = np.linspace(0,np.pi*2,nn).reshape(nn,1)
ones = np.ones((nn,1))
el_h = np.linspace(-5, 5, nn).reshape(1,nn)
x_sph = np.sin(phis)*np.cos(psis)
y_sph = np.sin(phis)*np.sin(psis)
z_sph = np.cos(phis)*ones
x_elp = np.sin(phis)*np.cos(psis)*.25
y_elp = np.sin(phis)*np.sin(psis)*.25
z_elp = el_h*ones
ax[0].scatter(x_sph, y_sph, z_sph)
ax[0].scatter(x_elp, y_elp, z_elp)
ax[1].scatter(y_sph, z_sph)
ax[1].scatter(y_elp, z_elp)
for ii in range(2):
ax[ii].set_xlabel('X')
ax[ii].set_ylabel('Y')
ax[0].set_zlabel('Z')
set_axes_equal(ax[0])
plt.savefig('SphereElipse.png', dpi=300)
And here is its image output:
3-D and 2-D sphere and ellipse side-by-side
Clearly the 2D plot automatically modifies the length of the axes while maintaining the scale, but the 3D plot doesn't, leading to a tiny representation which does not well use the space allotted to its subplot. Is there any way to do this? This question is similar to an earlier unanswered question How do I crop an Axes3D plot with square aspect ratio?, except it adds the stipulation of multiple subplots, which means the answers provided there do not work.
I would like to use pcolormesh within a Lon/Lat
grid of a satellite granule. The satellite has an inclination of
65 degree. So pcolormesh is kind of skewed.
I have prepared 2 snippets below.
Snippet (1) is partly from that questions
Python quiver and pcolormesh not lining up exactly right
In pcolormesh each pixel corresponds to data on its lower left corner,
(see Plot (1)).
Data points are always the scatterplots with the x-marker.
What I afterwards did, is shifting the pixels, so that the pixels are centered on the
actual data points.
First I shifted the pixels with have of the resolution (see Plot(2)).
Then I shifted the pixels with the individual difference of the Y (Latitude) and
X (Longitude) (see Plot(3))
I did the last step (shifting with Lat/Lon) because in my actual example
(snippet 2) the resolution or lets say the difference between latitudes and the difference between
longitudes is not the same and changes. So I have not a regular grid.
I inserted a column of 0 at the beginning, because otherwise I could not use
pcolormesh.
In my case it is not a problem because I do not really need the first column.
import numpy as np
import matplotlib.pyplot as plt
# Snippet (1)
# Note: In the snippets Latitude is convertible with Y
# Longitude is convertible with X
def middle_pc_X(X):
# X convertible with lon
X_half = np.abs(np.diff(X)/2.0)
X_half = np.insert(X_half, 0, 0, axis=1)
return X_half
def middle_pc_Y(Y):
# Y convertible with lat
Y_half = np.abs(np.diff(Y, axis=0)/2.0)
Y_half = Y_half.T
Y_half = np.insert(Y_half, 0, 0, axis=1)
return Y_half
x = np.arange(5)
y = np.arange(5)
X, Y = np.meshgrid(x, y)
C = np.random.rand(len(x), len(y))
res = 1.0 # 1.0 distance between x and y
# combination of X/Y
X_mega = middle_pc_X(X)
Y_mega = middle_pc_Y(Y)
#Plots
# Plot (1)
# Plot Pcolormesh default
plt.figure(figsize=(8, 8))
plt.subplot(2,2,1)
plt.pcolormesh(X, Y, C)
plt.colorbar()
plt.scatter(X,Y, s=20, c='black', marker='x')
# Plot (2)
# Plot with res
plt.subplot(2,2,2)
plt.pcolormesh(X-(res/2.0),
Y-(res/2.0),
C)
plt.colorbar()
plt.scatter(X,Y, s=20, c='black', marker='x')
# Plot(3)
# Plot in combination with X/Y
plt.subplot(2,2,3)
plt.pcolormesh(X-X_mega,
Y-Y_mega,
C)
plt.colorbar()
plt.scatter(X,Y, s=20, c='black', marker='x')
Plot 1-3:
Snippet (2):
Now I come to my second snippet and my question
Here I prepared respectively snippets of a satellite granule.
As I mentioned the satellite has a inclination of 65 degree.
The satellite is here on its descending node (it is flying from North to South).
So in other situations the satellite is on its ascending node (it is flying from
South to North).
The granule has originally the shape of Lon(7933, 24),
Lat(7933, 24), Rain(7933, 24)
In snippet (2) I did a shortcut of the data (4 x 4)
In Plot (4) of snippet (2) you can see the originally data.
I would like to plot the same as in snippet one,
so that the pixels are centered on the actual data points
Sadly a shifting with half of the resolution does not work,
the resolution is not the same.
I think that I have to use the difference method?
import numpy as np
import matplotlib.pyplot as plt
# Snippet (2)
Lon_bin = np.array([[-42.84589005, -42.84976959, -42.85368347, -42.85772705],
[-42.73979187, -42.74386215, -42.74796677, -42.75219727],
[-42.63357925, -42.63784409, -42.64214325, -42.64657211],
[-42.52753067, -42.53199387, -42.53648758, -42.54111099]])
Lat_bin = np.array([[ 65.62378693, 65.57657623, 65.52971649, 65.48249817],
[65.62098694, 65.57377625, 65.52692413, 65.47970581],
[65.61811066, 65.57090759, 65.52405548, 65.47683716],
[65.61515045, 65.56794739, 65.52110291, 65.47389221]])
rain = np.arange(16).reshape(4,4)
# Plots
# Plot (4)
plt.figure()
plt.figure(figsize=(10, 10))
plt.pcolormesh(Lon_bin,
Lat_bin,
rain)
plt.scatter(Lon_bin,
Lat_bin,
s=30, c='black', marker='x')
Plot 4:
Thank you for your help
Markus