Contour Labels not showing up - python

I am trying to overlay contours on top of a filled contour plot in matplotlib for some atmospheric data. However, my contour labels are not always showing up onscreen. Below is an example:
As you can see, the contour labels are only appearing on the innermost few contours.
Knowing that my contour range is defined earlier as
list(range(950,1052,4))
I have the following code to actually plot:
parallels = np.arange(0.,90,5.)
basem.drawparallels(parallels,labels=[1,0,0,0],fontsize=10)
# draw meridians
meridians = np.arange(180.,360.,5.)
basem.drawmeridians(meridians,labels=[0,0,0,1],fontsize=10)
basem.drawstates()
basem.drawcountries()
if clevs != 0:
cs = basem.contourf(x,y, plotted_var, clevs)
cl = basem.contour(x,y, plotted_var, clevsl, colors='k')
plt.clabel(cl, fmt="%1.0f", fontsize=8)
else:
cs = basem.contourf(x,y, plotted_var, cmap=plt.get_cmap(colorbar),
vmin = vmin, vmax = vmax)
cbar = basem.colorbar(cs, location='bottom', pad = "5%")
cbar.set_label(units)
Additionally, my basemap definition is:
basem = Basemap(width=5800000,height=3000000,
rsphere=(6378137.00,6356752.3142),\
resolution='h',area_thresh=1000.,projection='lcc',\
lat_1=45.,lat_2=55,lat_0=40,lon_0=-102.)
Is this a bug or is there just something I'm missing? I'm attempting to avoid the use of manual if I can.
The input data is a global dataset (GFS weather model). x, y are obtained by:
lons2, lats2 = np.meshgrid(lons, lats)
x,y = basem(lons2, lats2)
where lons, lats are:
lons = [0.0, 0.25, 0.5, 0.75, 1.0, ..., 359.75, 360.0]
lats = [-90, -89.75, ..., 89.75, 90]

I seem to have resolved the issue like so:
What I had to do was bound the data to what is able to be seen on basemap. My input dataset was a global dataset, and when I bound it (seen in the white areas below), the contour labels showed up mostly within the map boundaries. This still seems like a bug- my choice of a display region changes the locations of the contour labels in almost every other meteorological graphics program (like GrADS), but I'm going to mark this answered for now.

Related

How to make an inset plot with mollweide projection?

I want to make a skymap using the Mollweide projection for a main set of axes and for an inset axes. This is easy for the main axes but not for the inset. I've tried a few different things but it doesn't work for the inset. Please help!
Here you can find the latitude and longitude data, and here you can find the sky location probability density data.
First, I make the main plot:
xmin = min(l)
xmax = max(l)
ymin = min(b)
ymax = max(b)
X, Y = np.meshgrid(np.linspace(xmin, xmax, 100), np.linspace(ymin, ymax, 100))
mpl.rcParams["text.usetex"] = True
fig = plt.figure(1)
fig.set_figheight(8)
fig.set_figwidth(8)
ax = plt.axes(projection='mollweide')
ax.grid()
# skypost is the sky location probability-density data accessible above
plt.contour(X, Y, skypost, colors='blue', levels=[5, 50, 95])
which works fine. Next, I define the inset axes and plot the contours, however there seems to be no way that completely works for this. What I want is for the inset to zoom-in on the contours while keeping the mollweide projection. I've tried to do as the example on ligo.skymaps, i.e.,
axesinset = plt.axes(
[0.0, 0.2, 0.25, 0.25],
projection='astro degrees zoom',
center='110d +20d',
radius='10 deg' )
plt.sca(axesinset)
axesinset.contour(X, Y, skypost, colors='blue', levels=[5, 50, 95])
axesinset.grid()
but this doesn't work since the contours don't even appear! I don't understand why they don't appear. I also do not understand why the x-axis of the inset is backwards?
Instead, I've tried just plotting a new mollweide projection in the inset, and restricting the xlim and ylim, but it says these options are not supported for the mollweide projection. Is there a way around this to restrict the axes limits?
Lastly, I've tried just doing a regular inset without the mollweide, which works, however the shape of the contours are distorted relative to the contours on the main mollweide plot which is physically relevant for my case. So this is very sub-optimal.
Any suggestions and advice are greatly appreciated.
To have the axis in the correct way, you can rotate the subplot by using rotate.
Concerning the fact that your contour are not shown, it is probably because you have to add the transform keyword. If you don't specify it, it is plotted in pixel coordinates by default (https://docs.astropy.org/en/stable/visualization/wcsaxes/overlays.html).
The example below shows that the desired point (in blue) is obtained by adding ax.get_transform("world").
The blue and green points are in the lower right corner because of the rotate.
I guess that it should be the same for contour.
ax = plt.subplot(111, projection='geo degrees zoom',
center="0d - 0d", radius='10 deg', rotate='180 deg')
ax.grid()
ax.set_xlabel(r"$\phi \, [deg]$")
ax.set_ylabel(r"$\theta \, [deg]$")
ax.scatter(0,0, color = "blue")
ax.scatter(100,0, color = "green")
ax.scatter(0,0, color = "red", transform = ax.get_transform("world"))
I'm a bit late to the party, but I thought its worth mentioning that I've created a nice inset-map functionality for EOmaps...
It lets you create inset-maps in arbitrary projections and you can add whatever features you want!
from eomaps import Maps
m = Maps(Maps.CRS.Mollweide())
m.add_feature.preset.coastline()
# create a rectangular inset-map that shows a 5 degree rectangle
# centered around a given point
inset = m.new_inset_map(xy=(6, 43), xy_crs=4326,
radius=5, radius_crs=4326,
inset_crs=Maps.CRS.Mollweide(),
shape="rectangles")
inset.add_feature.preset.coastline()
inset.add_feature.preset.ocean()
inset.add_feature.cultural_10m.urban_areas(fc="r", ec="none")
m.apply_layout(
{'0_map': [0.01, 0.17333, 0.98, 0.65333],
'1_map': [0.05, 0.11667, 0.43341, 0.76667]})

How do I correctly implement contours of histograms with logscale binning in numpy/matplotlib

I am trying to plot contours of data that his been binned using numpy.hist2d, except the bins are set using numpy.logscale (equal binning in log space).
Unfortunately, this results in a strange behavior that I can't seem to resolve: the placement of the contours does not match the location of the points in x/y. I plot both the 2d histogram of the data, and the contours, and they do not overlap.
It looks like what is actually happening is the contours are being placed on the physical location of the plot in linear space where I expect them to be placed in log space.
It's a strange phenomenon that I think can be best described by the following plots, using identical data but binned in different ways.:
Here is a minimum working example to produce the logbinned data:
import numpy as np
import matplotlib.pyplot as plt
x = np.random.normal(loc=500, scale=100,size=10000)
y = np.random.normal(loc=600, scale=60, size=10000)
nbins = 50
bins = (np.logspace(np.log10(10),np.log10(1000),nbins),np.logspace(np.log10(10),np.log10(1000),nbins))
HH, xe, ye = np.histogram2d(x,y,bins=bins)
plt.hist2d(x,y,bins=bins,cmin=1);
grid = HH.transpose()
extent = np.array([xe.min(), xe.max(), ye.min(), ye.max()])
cs = plt.contourf(grid,2,extent=extent,extend='max',cmap='plasma',alpha=0.5,zorder=100)
plt.contour(grid,2,extent=extent,colors='k',zorder=100)
plt.yscale('log')
plt.xscale('log')
It's fairly clear what is happening -- the contour is getting misplaced do the scaling of the bins. I'd like to be able to plot the histogram and the contour here together.
If anyone has an idea of how to resolve this, that would be very helpful - thanks!
This is your problem:
cs = plt.contourf(grid,2,extent=extent,...)
You are passing in a single 2d array specifying the values of the histograms, but you aren't passing the x and y coordinates these data correspond to. By only passing in extent there's no way for pyplot to do anything other than assume that the underlying grid is uniform, stretched out to fit extent.
So instead what you have to do is to define x and y components for each value in grid. You have to think a bit how to do this, because you have (n, n)-shaped data and (n+1,)-shaped edges to go with it. We should probably choose the center of each bin to associate a data point with. So we need to find the midpoint of each bin, and pass those arrays to contour[f].
Something like this:
import numpy as np
import matplotlib.pyplot as plt
rng = np.random.default_rng()
size = 10000
x = rng.normal(loc=500, scale=100, size=size)
y = rng.normal(loc=600, scale=60, size=size)
nbins = 50
bins = (np.geomspace(10, 1000, nbins),) * 2
HH, xe, ye = np.histogram2d(x, y, bins=bins)
fig, ax = plt.subplots()
ax.hist2d(x, y, bins=bins, cmin=1)
grid = HH.transpose()
# compute bin midpoints
midpoints = (xe[1:] + xe[:-1])/2, (ye[1:] + ye[:-1])/2
cs = ax.contourf(*midpoints, grid, levels=2, extend='max', cmap='plasma', alpha=0.5, zorder=100)
ax.contour(*midpoints, grid, levels=2, colors='k', zorder=100)
# these are a red herring during debugging:
#ax.set_yscale('log')
#ax.set_xscale('log')
(I've cleaned up your code a bit.)
Alternatively, if you want to avoid having those white strips at the top and edge, you can keep your bin edges, and pad your grid with zeros:
grid_padded = np.pad(grid, [(0, 1)])
cs = ax.contourf(xe, ye, grid_padded, levels=2, extend='max', cmap='plasma', alpha=0.5, zorder=100)
ax.contour(xe, ye, grid_padded, levels=2, colors='k', zorder=100)
This gives us something like
This seems prettier, but if you think about your data this is less exact, because your data points are shifted with respect to the bin coordinates they correspond to. If you look closely you can see the contours being shifted with respect to the output of hist2d. You could fix this by generating geomspaces with one more final value which you only use for this final plotting step, and again use the midpoints of these edges (complete with a last auxiliary one).

how to change specific value's color in pcolormesh, such as -1,-2,-3, not NaN

I know there is a func cmap.set_bad to change NaN's color but I want to change color of more values such as -1,-2,-3, how to do it?
this plot is base on basemap
and I want to hold the color of the other area so I can't use the colormap built by myself
##### plot IMERG ####
latcorners = ([20.05, 54.051])
loncorners = ([73.05, 136.05])
m = Basemap(projection='cyl',llcrnrlat=latcorners[0],urcrnrlat=latcorners[1],llcrnrlon=loncorners[0],urcrnrlon=loncorners[1])
# Draw coastlines, state and country boundaries, edge of map.
m.drawcoastlines(color='darkgray')
m.drawstates(color='darkgray')
m.drawcountries(color='darkgray')
# Define the latitude and longitude data
X,Y = np.float32(np.meshgrid(theLons, theLats))
X1,Y1 = np.float16(np.meshgrid(lons_resized, lats_resized))
# Mask the values less than 0 because there is no data to plot.
masked_array = np.ma.masked_where(precip < 0,precip)
# Plot every masked value as grey
cmap = plt.get_cmap('gnuplot').copy()
cmap.set_bad('darkslategray', 1.)
# Draw filled contours.
clevs = np.arange(0,5,0.2)
m.readshapefile(r'../beijing/beijing', 'Shape_Leng', color='darkgrey')
# Plot the data,resized_precip is an ndarray which contains precipitation data, with shape(3401,6301), X1,Y1 are lon/lat of these points
cs = m.pcolormesh(X1,Y1,resized_precip, cmap=cmap, vmin=0, vmax=10)
parallels = np.arange(-90.,91,5.)
m.drawparallels(parallels,labels=[True,False,True,False],color='darkgray')
meridians = np.arange(-180.,180.,10.)
m.drawmeridians(meridians,labels=[False,False,False,True],color='darkgray')
# Add colorbar
cbar = m.colorbar(cs,location='right',pad="5%")
cbar.set_label('mm/h')
plt.show()
plt.close()
this is a program which is used to plot some precipitation data.
I want to plot these precipitation data properly,but I want to highlight some specific area with special color, and these points are flaged by special value.so how can I highlight these area by the same way as pcolormesh but not scatter?

How to plot coordinates onto a 3D image and display it

I have a 3D image as a layered tif file, its a binary volume showing blobs at specific locations. I also have an output from a prediction algorithm that predicts the coordinates of the said blobs in the image.
Up until now I have been reading in and writing the tif files using imageio.volread and imageio.volwrite but I want to see how accurately the prediction algorithm is working so I would like to plot the coordinates onto the image. The coordinates are [x,y,z] values where the number of rows equal the number of blobs.
I searched and found out that there is no easy way for python to achieve this in 3D. taking guidance from here: https://www.raddq.com/dicom-processing-segmentation-visualization-in-python/, what I did attempt was to utilize skimage.measure.marching_cubes to convert the image into a 2D surface mesh so that it can be plotted using matplotlib and then use that to plot my image.
def make_mesh(image):
print('Transposing surface')
p = image.transpose(2, 1, 0)
print('Calculating surface')
verts, faces, norm, val = measure.marching_cubes_lewiner(p, allow_degenerate=True)
return verts, faces
def plt_3d(verts, faces):
print('Drawing')
x, y, z = zip(*verts)
fig = plt.figure(figsize=(10, 10))
ax = fig.add_subplot(111, projection='3d')
# Fancy indexing: `verts[faces]` to generate a collection of triangles
mesh = Poly3DCollection(verts[faces], linewidths=0.05, alpha=1)
face_color = [1, 1, 0.9]
mesh.set_facecolor(face_color)
ax.add_collection3d(mesh)
ax.set_xlim(0, max(x))
ax.set_ylim(0, max(y))
ax.set_zlim(0, max(z))
# ax.set_axis_bgcolor((0.7, 0.7, 0.7))
plt.show()
img_gt = io.volread(gt_path)
v, f = make_mesh(img_gt)
plt_3d(v, f)
The image is a [21,512,1024] and consists of 5 'blobs', but the plot_3d generates the following:
am I not using the marching_cubes function properly? Why is the plot so distorted? The original blobs are clean spheres, not stretched at all.
Furthermore if this is the only way to plot my image, how would I plot my [x,y,z] coordinates on top of this?

Contour on color map: not continuous (Python)

I made some color maps in python. On top of them I wanted to add some continental contours, using the land-sea mask provided in the model I run. It consists of just 1 or 0, 1 for land and 0 for no-land.
There is some strange characters written into the contour plot. Does anyone here knows how I could get the contour to connect to itself so it's smooth instead of being broken with those small strange characters in between the ends of each line?
Here is what the figure looks like:
And here is a piece of the code (note this map was part of a plot containing other maps, so this is the map of index 9).
lsmfile = netcdf.netcdf_file("/Volumes/LaCie/Plasim/Earth2/high/1367/SOL1367earth050.nc","r")
lat = lsmfile.variables["lat"].data
lon = lsmfile.variables["lon"].data
mask = lsmfile.variables["lsm"].data
mask = mask[0]
cmap = plt.get_cmap('bwr')
fig, ax = plt.subplots(nrows=5,ncols=2,figsize=(16,14))
im9 = ax.flat[9].pcolormesh(lon, lat, surfalbearth, cmap=cmap,norm=norm)
fig.colorbar(im9, ax=ax.flat[9])
ax.flat[9].set_xlim(xmin=0, xmax=355)
ax.flat[9].set_ylim(ymin=-86, ymax=86)
CS = plt.contour(lon,lat,mask, 1,colors='k')
plt.clabel(CS, fontsize=3, inline=1)
fig.tight_layout()
plt.savefig('Maps')
plt.show()
It seems you have asked for having those contour labels (clabel) in your plot by using the line
plt.clabel(CS, fontsize=3, inline=1)
So if you remove that line, the contour labels should disappear.

Categories