I would like to mask oceans when plotting the data from a netCDF dataset. I followed the great instructions given in the answer to this question. It works great for half of the world, but somehow, everything west of Greenwich is masked as well, both ocean and land.
Here is my code:
import netCDF4
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import mpl_toolkits
from mpl_toolkits import basemap
from mpl_toolkits.basemap import Basemap, maskoceans
filename = 'myfile.nc'
vmin = 0.
vmax = 1
nc = netCDF4.Dataset(filename, 'r')
data = nc.variables['sum'][:]
lats_1d = nc.variables['lat'][:]
lons_1d = nc.variables['lon'][:]
lons, lats = np.meshgrid(lons_1d, lats_1d)
labels = ['DJF', 'MAM', 'JJA', 'SON']
cmap = cm.RdYlBu
cmap.set_over('#00FF00')
my_dpi = 96
fig = plt.figure(figsize=(1200/my_dpi, 800./my_dpi))
for season in range(4):
ax = fig.add_subplot(2, 2, season+1)
map1 = basemap.Basemap(resolution='c', projection='kav7', lon_0=0)
map1.drawcoastlines()
map1.drawcountries()
nc_new = maskoceans(lons,lats,data[season,:,:],resolution='c', grid = 1.25)
datapc = map1.pcolormesh(lons, lats, nc_new, vmin=vmin, vmax=vmax, cmap=cmap, latlon=True)
plt.title(labels[season])
fig.tight_layout(pad=1, w_pad=1, h_pad=4)
ax = fig.add_axes([0.05, 0.52, 0.9, 0.025])
cb = plt.colorbar(cax=ax, orientation='horizontal', cmap=cmap,
extend='max', format="%.2f",
ticks=[0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1])
plt.show()
I know that a somewhat similar issue was raised here but never got answered, and it appears that in the end, the problem was mixing up lat-long coordinates with x-y ones. I tried switching to x-y coordinates but got the same half-map. Any idea of what can be happening here?
N.B. when plotting the unmasked data using datapc = map1.pcolormesh(lons, lats, data[season,:,:], vmin=vmin, vmax=vmax, cmap=cmap, latlon=True) the whole world is plotted (land + oceans).
As you've identified, the points with longitudes -180 to 0 are not being plotted. Assuming they're in your data, they must be being masked or discarded for some reason.
My intuition was that the dataset longitudes ran 0-360 instead of -180 to 180, which was confirmed in the comments.
The quick fix for this is to add
lons_1d[lons_1d>180]-=360
just after you pull out lons_1d from nc. This works because lons_1d is a numpy array and it uses numpy boolean array indexing (often called "fancy" indexing) to conditionally select the longitude values greater than 180 and subtract 360 from them.
As you note that the pcolormesh plot works if you omit the mask, this looks like a bug with wrapping in the maskoceans function, or at least unexpected behaviour.
For reference - I do not think you are the first to experience similar "wrapping" type issues with masks, I think this issue on the matplotlib github looks rather similar.
Related
I have been playing around with heatmaps as a way to display a lot of information in a visually impactful way. Using a linear colorbar scale I was able to compute the ticks and number of colors necessary to get a nice looking colorbar. However, when switching to a logarithmic colorbar, using matplotlib.colors.LogNorm, it becomes a bit trickier.
I am able to set meaningful max and min values based on the data set, and get the ticks at the intersection of the colors (see Fig 1).
Figure 1. Each color spans one order of magnitude
The issue here is that the resolution is a bit low, and I would like to have twice as many colors. I can do this by multiplying by a factor of 2 when determining the number of colors, and the result is in Fig. 2.
Figure 2. Each color spans the same area of the color bar, but spans different data ranges.
However, due to the logarithmic scale, the two colors within one order of magnitude cover different ranges of data, and ideally I would like the intersection of the colors to be right at the 0.5*10^n mark.
Is there a relatively straightforward way of achieving this?
Snippet for reproducing the figures:
import matplotlib
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
import math
data = [[0.0065513626834508175, 0.476779092353869, 0.021513472812344454, 0.37316274465007165, 0.11149228130361034, 0.05917159763313834, 0.07437156031533118, 0.1083790557474745, 0.0733214622394398, 0.17898426430010372], [0.18343815513625047, 0.9005827300017748, 0.629269079761209, 0.16754245678166085, 1.1749571183533365, 1.2228796844181362, 1.6064257028112423, 1.4021540337329765, 0.5813344506127501, 0.47729137146693623], [0.4192872117400517, 0.4414621225498852, 0.10218899585865526, 0.441702840606187, 0.03430531732417843, 0.05917159763313834, 0.3421091774505488, 0.216758111494949, 0.12045668796480033, 0.1416958759042417], [0.032756813417179606, 0.14126787921597278, 0.4141343516377186, 0.09138679460818258, 0.1543739279588273, 0.0657462195923697, 0.11899449650453413, 0.2235318024791601, 0.2435318024791601, 0.10440748750837972], [0.5634171907756903, 0.4591206074518771, 0.6131339751519469, 0.8300967176909628, 1.9125214408233189, 1.2294543063773862, 3.004611036739566, 0.8128429181060683, 0.6337069236409385, 1.1708553956297958], [0.27515723270441295, 0.4414621225498852, 0.1237024686709997, 0.0837712283908196, 0.13722126929673809, 0.039447731755425555, 0.20824036888293998, 0.34545824019507615, 0.04713522572536054, 0.2386456857334575]]
grid = pd.DataFrame(data)
exp_max = math.ceil(math.log10(grid.max().max()))
exp_min = math.floor(math.log10(grid.min().min()))
maxval = 10 ** exp_max
minval = 10 ** exp_min
n_colors = int(exp_max - exp_min) * 1
cmap = sns.color_palette('ch:start=2,rot=2,light=0.9,d=0.2', n_colors=n_colors)
norm = matplotlib.colors.LogNorm(vmax=maxval, vmin=minval)
kws = {
'data': grid,
'cmap': cmap,
'square': True,
'vmax': maxval,
'linecolor': 'black',
'lw': .3,
'norm': norm
}
sns.heatmap(**kws)
plt.tight_layout()
I'm not sure how to do it with Seaborn, but if you're willing to fall back to Matplotlib, this should get your in the right direction.
The code below defines the levels manually. There is probably a way to get these automatically based on the min, max you already defined. But I could not find it right away, something along the lines of np.geomspace or np.logspace etc.
Currently it also loses the minor ticks on the colorbar. I'm not sure if that's important to you? They can probably be set by specifying different ticks, not only the (color) levels, and the changing the minor_thresholds to make it skip the minor ones.
The formatting of the LogFormatter is also slightly different, using the scientific notation (adding the "e"). I'm not sure if Matplotlib already has an available Ticker that uses the formatting from Seaborn. Maybe Seaborn has a custom function that works in combination with Matplotlib's FuncFormatter, I'm not that familiar with Seaborn unfortunately.
from matplotlib.ticker import LogFormatter, IndexLocator
exp_max = math.ceil(math.log10(grid.max().max()))
exp_min = math.floor(math.log10(grid.min().min()))
maxval = 10 ** exp_max
minval = 10 ** exp_min
n_colors = int(exp_max - exp_min) * 1
cmap = sns.color_palette('ch:start=2,rot=2,light=0.9,d=0.2', n_colors=n_colors*2, as_cmap=True)
fig, ax = plt.subplots(figsize=(8,6), constrained_layout=True, dpi=86, facecolor='w')
levels = [0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1.0, 5.0, 10.0]
norm = mpl.colors.BoundaryNorm(levels, ncolors=cmap.N, clip=True)
im = ax.imshow(grid, cmap=cmap, norm=norm)
formatter = LogFormatter(5, labelOnlyBase=False, minor_thresholds=(np.inf, np.inf))
cb = fig.colorbar(im, ticks=levels, format=formatter, spacing="uniform", shrink=0.6)
cb.outline.set_visible(False)
ax.xaxis.set_minor_locator(IndexLocator(base=1, offset=0))
ax.yaxis.set_minor_locator(IndexLocator(base=1, offset=0))
ax.xaxis.set_major_locator(IndexLocator(base=1, offset=0.5))
ax.yaxis.set_major_locator(IndexLocator(base=1, offset=0.5))
ax.grid(which='minor', color='k', linewidth=2)
Results in:
I would like to plot a matrix as image and a vector as a line in this image.
something like that:
I manage to do the code for the matrix image, but I'm not able to make the black line (here I did just
an example in powerpoint).
this is my code so far:
from mpl_toolkits.axes_grid1 import make_axes_locatable
from matplotlib.ticker import LogLocator
from matplotlib import rcParams
import matplotlib.pyplot as plt
from matplotlib.colors import LogNorm
import numpy as np
rcParams['font.size']=35
x = np.arange(1,16,1)
y = np.arange(-50,0,1)
z = 100 * np.random.random_sample((15, 50))
line = np.linspace(0,100,50)
fig, ax = plt.subplots(figsize=(25,25))
divider = make_axes_locatable(ax)
cax = divider.append_axes('right', size='5%', pad=0.1)
im = ax.pcolor(x,y,z.T,norm=LogNorm(0.1, 100),cmap= 'jet')
cbar = fig.colorbar(im,cax=cax, orientation='vertical')
cbar.ax.yaxis.set_major_locator(LogLocator()) # <- Why? See above.
cbar.ax.set_ylabel('Resistividade \u03C1 [ohm.m]', rotation=270)
#ax2=ax.twinx()
#ax2.plot(line,y,'k--',linewidth=10)
ax.set_xlabel('Aquisição')
ax.set_ylabel('Profundidade [m]')
plt.savefig('mrec_1'+'.png',bbox_inches = "tight", format='png', dpi=300)
plt.show()
I have tried to use the ax.twinx() but since the order of magnetude is different the values on x-axis doesn' match.
Would someone help me please?
I tried this but it depends on the kind of data you have for the black line "track". I think you can just make an array of different (x,y) coordinates for the vertices (where the line changes direction + point of origin and point of end). Basing on what I saw in the image, you can add these two lines (is an approximation)
coordinates=np.array([[7,0],[7,-5],[9,-5],[9,-13], [7,-13],[7,-23],[13,-23],[13,-60]])
ax.plot(coordinates[:,0], coordinates[:,1], c='k', linewidth=10)
where every element of coordinates is the [x,y] couple. But if you have x and y from a well log, for example, you can just use those as arrays instead of coordinates[:,0] and coordinates[:,1]
As #BlueScr33n mentioned I used the twiny and works fine.
I'm considerably new to python and making a map of a room. I've plotted the room, obstacles etc. And some points (which are sensors). Now I want make a 2-D cone which shows the area in which sensors see. I will have an angle and radius for the cone.
I've tried searching but mostly 3-D cones have been discussed here in previous questions. How the cone should look
Any guidance is appreciated
You would use matplotlib.patches.Wedge such as this example. Another example that I've reduced to the more relevant bits is:
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.collections import PatchCollection
import numpy as np
fig, ax = plt.subplots()
patches = []
wedge = mpatches.Wedge((.5, .5), 0.5, 30, 270, ec="none")
patches.append(wedge)
colors = np.linspace(0, 1, len(patches))
collection = PatchCollection(patches, cmap=plt.cm.hsv, alpha=0.3)
collection.set_array(np.array(colors))
ax.add_collection(collection)
plt.show()
Which produces something like:
Obviously you will need to tweak the theta1 and theta2 from their 30 and 270 to fit whatever angle you are trying to represent, and move the origin to wherever the sensors are located. Additionally you may want to color them all the same, instead of a rainbow but I'll let you figure out the details XD
Ended up using the following:
import matplotlib.pyplot as plt
from matplotlib.patches import Wedge
fig, ax = plt.subplots()
patches=[]
ax.axis('equal')
we = Wedge((2756.6747,5339751.8148),10,30,180,edgecolor='b',facecolor='none')
patches.append(we)
ax.add_artist(we)
ax.set(xlim=[2740, 2800], ylim=[5339740, 5339780])
plt.show()
thanks to the direction given by #reedinationer
How to change the location the 'r' axis for matplotlib polar plot?
I am trying to change the location of the r axis in a polar plot.
At the moment it is being covered up by the data, but there is a gap in the data at theta = 340-360 degrees (in my real data example this is actually approx. 45 degrees) so it would be good if i could put axis labels there, in the data gap.
import random
import numpy as np
import matplotlib.pyplot as plt
sampleSize=1000
az=[]
inc=[]
for i in range(sampleSize):
az.append(random.randint(0,340)) #not to the full 360 to represent my natural gap in the data
inc.append(random.randint(0,90))
plt.figure()
plt.polar(np.radians(az),inc,'o')
plt.show()
One way that I can think of is to use .set_rgrids method:
f=plt.figure()
ax = f.add_axes([0.1, 0.1, 0.8, 0.8], projection='polar')
ax.plot(np.radians(az), inc, 'o')
ax.set_rgrids([10,20,30,40,50,60,70,80,90], angle=345.)
I am making Polar Stereographic Projection maps of some climate model outputs. For some of these data, the plot looks weird. For example, in this figure:
only two color contours showed up while the actual data should span much wider range. Furthermore, a large portion of the region should be blank since the data are masked out by netcdf module already (they are undefined).
from netCDF4 import Dataset
import matplotlib
matplotlib.use('agg')
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.basemap import Basemap
from pylab import *
fig_index=1
fig = plt.figure(num=fig_index, figsize=(12,7), facecolor='w')
fbot_levels = arange(0.05,1.0,0.05)
fname='alb.nc4'
ncfile = Dataset(fname, 'r', format='NETCDF4')
TS2=ncfile.variables['SIALB'][0]
LON=ncfile.variables['lon'][:]
LAT=ncfile.variables['lat'][:]
ncfile.close()
lon,lat=np.meshgrid(LON,LAT)
ax2 = plt.axes([0.2, 0.225, 0.6, 0.6])
meridians=[0,1,1,1]
m = Basemap(projection='spstere',lon_0=0,boundinglat=-45)
m.drawcoastlines()
x, y =m(lon,lat)
plt.contourf(x,y,TS2, fbot_levels, origin='lower')
m.drawparallels(np.arange(-90.,120.,15.),labels=[1,0,0,0]) # draw parallels
m.drawmeridians(np.arange(0.,420.,30.),labels=meridians) # draw meridians
coloraxis = [0.1, 0.1, 0.8, 0.035]
cx = fig.add_axes(coloraxis, label='m', title='K')
cbar=plt.colorbar(cax=cx,orientation='horizontal',ticks=list(fbot_levels))
plt.show()
You can find the dataset in netcdf format which is used to generate the figure here
https://dl.dropboxusercontent.com/u/45427012/alb.nc4
I am using basemap-1.0.6 with matplotlib-1.2.1 on py2.7.
Your Basemap object (m) also serves as the mpl axes. When plotting, you should use that instead of using plt.. So:
m.contourf(x,y,TS2, fbot_levels, origin='lower')
Stretching the levels between 0.5 and 0.9 highlights the different contours further.