Matplotlib - how to superimpose a contour map with coastlines and countries - python

I have this python script that plots a contour map of geopotential heights -
nc_f = './hgt_500_2014_12_5_00Z.nc' # Your filename
nc_fid = Dataset(nc_f, 'r')
lats = nc_fid.variables['lat'][:] # extract/copy the data
lons = nc_fid.variables['lon'][:]
time = nc_fid.variables['time'][:]
hgt = nc_fid.variables['hgt'][:] # shape is time, lat, lon as shown above
x, y = np.meshgrid(lons, lats,copy=False)
rbf = scipy.interpolate.Rbf(x, y, hgt, function='linear')
zi = rbf(x, y)
plt.contour(x,y,zi)
plt.show()
I want to be able to superimpose this plot with coastlines and countries.
I tried this but this gives me the coastlines and countries but the geopotential height contours are missing
m = Basemap(width=5000000,height=3500000,
resolution='l',projection='stere',\
lat_ts=40,lat_0=lat_0,lon_0=lon_0)
x, y = np.meshgrid(lons, lats,copy=False)
rbf = scipy.interpolate.Rbf(x, y, hgt, function='linear')
zi = rbf(x, y)
cs = m.pcolor(x,y,np.squeeze(hgt))
m.drawcoastlines()
m.drawcountries()
cs = m.contour(x,y,zi,15,linewidths=1.5)
cbar = m.colorbar(cs, location='bottom', pad="10%")
cbar.set_label(hgt_units)
plt.title('500 hPa Geopotential Height')
plt.savefig('testplot.png')
plt.show()

Your code is totally broken. Look at the example of hgt data:
from netCDF4 import Dataset
import scipy.interpolate
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
nc_f = 'hgt_500.nc'
nc_fid = Dataset(nc_f, 'r')
lats = nc_fid.variables['lat']
lons = nc_fid.variables['lon']
time = nc_fid.variables['time']
hgt = nc_fid.variables['hgt']
m = Basemap(width=5000000,height=3500000,
resolution='l',projection='stere', lat_0 = 60, lon_0 = 70, lat_ts = 40)
m.drawcoastlines()
m.drawcountries()
lons, lats = np.meshgrid(lons, lats)
x, y = m(lons, lats)
# plot the first ZZ of hgt500
clevs = np.arange(400.,604.,4.)
cs = m.contour(x, y, hgt[0] * .1, clevs, linewidths=1.5, colors = 'k')
plt.clabel(cs, inline=1, fontsize=15, color='k', fmt='%.0f')
# color grid
pcl = m.pcolor(x,y,np.squeeze(hgt[0]*.1))
cbar = m.colorbar(pcl, location='bottom', pad="10%")
cbar.set_label("hPa")
plt.title('500 hPa Geopotential Height')
plt.show()
Result:

Related

Can we put a flat heatmap on a 3D axis?

Can we plot a straight heatmap on a 3D axis? The heatmap is as follows:
I am able to get a 3D elevation map, but I am not looking for that. I just want this straight lying on a 3D axis.
Code:
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from scipy import interpolate
excel_data_df = pd.read_excel('test.xlsx')
X= excel_data_df['x'].tolist()
Y= excel_data_df['y'].tolist()
Z= excel_data_df['z'].tolist()
X = np.array(X)
Y = np.array(Y)
Z = np.array(Z)
# Flatten trial dataset to meet your requirement:
x = X.ravel()
y = Y.ravel()
z = Z.ravel()
# Resampling on as square grid with given resolution:
resolution = 8
xlin = np.linspace(min(x), max(x), resolution)
ylin = np.linspace(min(y), max(y), resolution)
Xlin, Ylin = np.meshgrid(xlin, ylin)
# Linear multi-dimensional interpolation:
interpolant = interpolate.NearestNDInterpolator([r for r in zip(x, y)], z)
Zhat = interpolant(Xlin.ravel(), Ylin.ravel()).reshape(Xlin.shape)
cmap = 'jet'
# Render and interpolate again if necessary:
fig, axe = plt.subplots()
axe.imshow(Zhat, origin="lower", cmap=cmap, interpolation='bicubic',extent=[min(x),max(x),min(y),max(y)])
plt.xticks(np.arange(min(x), max(x)+1, 1.0))
plt.yticks(np.arange(min(y), max(y)+1, 1.0))
axe.grid(True, linewidth=0.3, color='w')
norm = matplotlib.colors.Normalize(vmin = min(z), vmax = max(z), clip = False)
plt.colorbar(plt.cm.ScalarMappable(cmap = cmap, norm=norm))
plt.show()

Contour/Scatter Plot - Interpolated data not covering entire scatter plot

I am trying to create a map showing an interpolated values over a scatter plot, here my code so far
# define map extent
lllon = dd
lllat = bb
urlon = cc
urlat = aa
# Set up Basemap instance
m = Basemap(
projection = 'merc',
llcrnrlon = lllon, llcrnrlat = lllat, urcrnrlon = urlon, urcrnrlat = urlat,
resolution='h')
# transform lon / lat coordinates to map projection
newdf['LONGITUDE'], newdf['LATITUDE'] = m(*(newdf['LONGITUDE'].values, newdf['LATITUDE'].values))
# grid data
#numcols, numrows = count_col, count_row
#xi = np.linspace(dd, cc, numcols)
#yi = np.linspace(bb, aa, numrows)
#xi, yi = np.meshgrid(xi, yi)
count_row = newdf.shape[0] # gives number of row count
count_col = newdf.shape[1] # gives number of col count
xi = np.linspace(newdf['LONGITUDE'].min(), newdf['LONGITUDE'].max(), count_col)
yi = np.linspace(newdf['LATITUDE'].min(), newdf['LATITUDE'].max(), count_row)
xi, yi = np.meshgrid(xi, yi)
x, y, z = newdf['LONGITUDE'].values, newdf['LATITUDE'].values, newdf['MUD_WGHT'].values
#zi = griddata(x, y, z, xi, yi)
zi = griddata((x,y),z,(xi,yi),method='linear')
# interpolate
#x, y, z = newdf['LONGITUDE'].values, newdf['LATITUDE'].values, newdf['MUD_WGHT'].values
#zi = griddata((x,y),z,(xi,yi),method='linear')
# draw map details
m.drawmapboundary(fill_color = 'white')
m.fillcontinents(color='#C0C0C0', lake_color='#7093DB')
m.drawcountries(
linewidth=.75, linestyle='solid', color='#000073',
antialiased=True,
ax=ax, zorder=3)
m.drawparallels(
np.arange(lllat, urlat, 2.),
color = 'black', linewidth = 0.5,
labels=[True, False, False, False])
m.drawmeridians(
np.arange(lllon, urlon, 2.),
color = '0.25', linewidth = 0.5,
labels=[False, False, False, True])
# contour plot
con = m.contourf(xi, yi, zi, zorder=4, alpha=0.6, cmap='RdPu')
# scatter plot
m.scatter(
newdf['LONGITUDE'],
newdf['LATITUDE'],
color='#545454',
edgecolor='#ffffff',
alpha=.75,
s=50 * norm(newdf['MUD_WGHT'].values),
cmap='RdPu',
ax=ax,
vmin=zi.min(), vmax=zi.max(), zorder=4)
# add colour bar and title
# add colour bar, title, and scale
cbar = plt.colorbar(orientation='horizontal', fraction=.057, pad=0.05)
cbar.set_label("Mud Weight - PPG")
#m.drawmapscale(
# 24., -9., 28., -13,
# 100,
# units='km', fontsize=10,
#yoffset=None,
#barstyle='fancy', labelstyle='simple',
#fillcolor1='w', fillcolor2='#000000',
#fontcolor='#000000',
#zorder=5)
plt.title("Regional Mud Weights in The Lateral")
plt.show()
The result is the following:
How can I get the interpolated contour region to extend to the full scatter plot? I have been focusing on if this is an issue with the meshgrid, so I am not sure if the meshgrid isn't interpolating all of the data or if it is an issue with the plotting.
You need to do triangulation and use tricontour and/or tricontourf. Here is a demonstration code and sample plot.
from mpl_toolkits.basemap import Basemap
from matplotlib.tri import Triangulation
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import cm
# data coordinates must conform to the projection
x = np.random.random((30))
y = np.random.random((30))
z = x * y
yn = 30*x
xn = 80 + 30*y
tri = Triangulation(xn,yn) #create tri mesh
fig = plt.figure(figsize=(7, 7))
m = Basemap(projection = 'cyl',
llcrnrlat = 0,
urcrnrlat = 30,
llcrnrlon = 80,
urcrnrlon = 110,
resolution = 'l')
ctf = plt.tricontourf(tri, z, cmap=cm.coolwarm, zorder=10, alpha=0.75)
#plt.tricontour(tri, z, )
plt.scatter(xn, yn, c='g', zorder=13)
m.drawparallels(np.arange(-90, 90,10), labels=[1,0,0,0])
m.drawmeridians(np.arange(-180, 180, 10), labels = [0,0,0,1])
m.drawcoastlines(linewidth=0.8, color='blue', zorder=12)
m.fillcontinents(color='#C0C0C0', lake_color='#7093DB')
cbar = plt.colorbar(ctf , orientation='horizontal', fraction=.045, pad=0.08)
plt.show()

3D plot of the CONE using matplotlib

I'm looking for help to draw a 3D cone using matplotlib.
My goal is to draw a HSL cone, then base on the vertex coordinats i will select the color.
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
theta1 = np.linspace(0, 2*np.pi, 100)
r1 = np.linspace(-2, 0, 100)
t1, R1 = np.meshgrid(theta1, r1)
X1 = R1*np.cos(t1)
Y1 = R1*np.sin(t1)
Z1 = 5+R1*2.5
theta2 = np.linspace(0, 2*np.pi, 100)
r2 = np.linspace(0, 2, 100)
t2, R2 = np.meshgrid(theta2, r2)
X2 = R2*np.cos(t2)
Y2 = R2*np.sin(t2)
Z2 = -5+R2*2.5
ax.set_xlabel('x axis')
ax.set_ylabel('y axis')
ax.set_zlabel('z axis')
# ax.set_xlim(-2.5, 2.5)
# ax.set_ylim(-2.5, 2.5)
# ax.set_zlim(0, 5)
ax.set_aspect('equal')
ax.plot_surface(X1, Y1, Z1, alpha=0.8, color="blue")
ax.plot_surface(X2, Y2, Z2, alpha=0.8, color="blue")
# ax.plot_surface(X, Y, Z, alpha=0.8)
#fig. savefig ("Cone.png", dpi=100, transparent = False)
plt.show()
HSL CONE
My cone
So my question now is how to define color of each element.
i have found a solution, maybe it will be usefull for others.
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
import matplotlib.pyplot as plt
import numpy as np
import colorsys
from matplotlib.tri import Triangulation
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
n_angles = 80
n_radii = 20
# An array of radii
# Does not include radius r=0, this is to eliminate duplicate points
radii = np.linspace(0.0, 0.5, n_radii)
# An array of angles
angles = np.linspace(0, 2*np.pi, n_angles, endpoint=False)
# Repeat all angles for each radius
angles = np.repeat(angles[..., np.newaxis], n_radii, axis=1)
# Convert polar (radii, angles) coords to cartesian (x, y) coords
# (0, 0) is added here. There are no duplicate points in the (x, y) plane
x = np.append(0, (radii*np.cos(angles)).flatten())
y = np.append(0, (radii*np.sin(angles)).flatten())
# Pringle surface
z = 1+-np.sqrt(x**2+y**2)*2
print(x.shape, y.shape, angles.shape, radii.shape, z.shape)
# NOTE: This assumes that there is a nice projection of the surface into the x/y-plane!
tri = Triangulation(x, y)
triangle_vertices = np.array([np.array([[x[T[0]], y[T[0]], z[T[0]]],
[x[T[1]], y[T[1]], z[T[1]]],
[x[T[2]], y[T[2]], z[T[2]]]]) for T in tri.triangles])
x2 = np.append(0, (radii*np.cos(angles)).flatten())
y2 = np.append(0, (radii*np.sin(angles)).flatten())
# Pringle surface
z2 = -1+np.sqrt(x**2+y**2)*2
# NOTE: This assumes that there is a nice projection of the surface into the x/y-plane!
tri2 = Triangulation(x2, y2)
triangle_vertices2 = np.array([np.array([[x2[T[0]], y2[T[0]], z2[T[0]]],
[x2[T[1]], y2[T[1]], z2[T[1]]],
[x2[T[2]], y2[T[2]], z2[T[2]]]]) for T in tri2.triangles])
triangle_vertices = np.concatenate([triangle_vertices, triangle_vertices2])
midpoints = np.average(triangle_vertices, axis=1)
def find_color_for_point(pt):
c_x, c_y, c_z = pt
angle = np.arctan2(c_x, c_y)*180/np.pi
if (angle < 0):
angle = angle + 360
if c_z < 0:
l = 0.5 - abs(c_z)/2
#l=0
if c_z == 0:
l = 0.5
if c_z > 0:
l = (1 - (1-c_z)/2)
if c_z > 0.97:
l = (1 - (1-c_z)/2)
col = colorsys.hls_to_rgb(angle/360, l, 1)
return col
facecolors = [find_color_for_point(pt) for pt in midpoints] # smooth gradient
# facecolors = [np.random.random(3) for pt in midpoints] # random colors
coll = Poly3DCollection(
triangle_vertices, facecolors=facecolors, edgecolors=None)
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.add_collection(coll)
ax.set_xlim(-1, 1)
ax.set_ylim(-1, 1)
ax.set_zlim(-1, 1)
ax.elev = 50
plt.show()
Inspired from Jake Vanderplas with Python Data Science Handbook, when you are drawing some 3-D plot whose base is a circle, it is likely that you would try:
# Actually not sure about the math here though:
u, v = np.mgrid[0:2*np.pi:100j, 0:np.pi:20j]
x = np.cos(u)*np.sin(v)
y = np.sin(u)*np.sin(v)
and then think about the z-axis. Since viewing from the z-axis the cone is just a circle, so the relationships between z and x and y is clear, which is simply: z = np.sqrt(x ** 2 + y ** 2). Then you can draw the cone based on the codes below:
from mpl_toolkits import mplot3d
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
def f(x, y):
return np.sqrt(x ** 2 + y ** 2)
fig = plt.figure()
ax = plt.axes(projection='3d')
# Can manipulate with 100j and 80j values to make your cone looks different
u, v = np.mgrid[0:2*np.pi:100j, 0:np.pi:80j]
x = np.cos(u)*np.sin(v)
y = np.sin(u)*np.sin(v)
z = f(x, y)
ax.plot_surface(x, y, z, cmap=cm.coolwarm)
# Some other effects you may want to try based on your needs:
# ax.plot_surface(x, y, -z, cmap=cm.coolwarm)
# ax.scatter3D(x, y, z, color="b")
# ax.plot_wireframe(x, y, z, color="b")
# ax.plot_wireframe(x, y, -z, color="r")
# Can set your view from different angles.
ax.view_init(azim=15, elev=15)
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_zlabel("z")
plt.show()
ax.set_ylabel("y")
ax.set_zlabel("z")
plt.show()
And from my side, the cone looks like:
and hope it helps.

How could I plot an array with conditions over a contour map?

I have plotted a global map of GPP using the code below:
( 'lon' and 'lat' are both netCDF4 attributes and have a shape of (144, ) and (90, ) respectively, whilst 'gpp_avg' is a numpy array with a shape of (90, 144) )
import numpy as np
import netCDF4 as n4
import matplotlib.pyplot as plt
import cartopy as cart
import cartopy.crs as ccrs
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
from mpl_toolkits.basemap import Basemap
>> gpp_avg = n4.Dataset('decadal_gpp.nc', 'r')
>> lon = gpp_avg.variables['lon'] # 144 grid cells every 2.5 degrees (east-west)
>> lat = gpp_avg.variables['lat'] # 90 grid cells every 2 degrees (north-south)
>> # Plotting data on a map with Cartopy
>> plt.figure()
>> ax = plt.axes(projection=ccrs.PlateCarree())
>> ax.coastlines() # Adding coastlines
>> ax.add_feature(cart.feature.OCEAN, zorder=100, edgecolor='k')
>> cs = ax.contourf(lon[:], lat[:], gpp_avg[:], cmap = 'Spectral')
>> cbar = plt.colorbar(cs, ax=ax) # Additional necessary information
>> cbar.set_label('g[C]/m^2/day')
>> gridl = ax.gridlines(color="black", linestyle="dotted",
draw_labels=True) # Adding axis labels - latitude & longitude
>> gridl.xformatter=LONGITUDE_FORMATTER
>> gridl.yformatter=LATITUDE_FORMATTER
>> gridl.xlabels_top = False
>> gridl.ylabels_right = False
>> plt.show()
I have a numpy array 'ci_95_gpp' which has the shape (90, 144) which contains the p-values for each grid cell of the global map. I want to plot points on top of the global contour map where the p-values are greater than 2.
How would I go about doing this? Many thanks.
I generate a set of data for contour plot on a Cartopy map. The data points for contouring are separated into 2 groups, with negative and positive z-values. Numpy maskedarray is used in that operation. I hope that this is useful for the general readers, including the OP.
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.ticker as mticker
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
import pandas as pd
from numpy.random import uniform, seed
from matplotlib.mlab import griddata
# TODO, use newer scipy.interpolate() instead of `griddata`
import numpy.ma as ma
# make up some data around long,lat: (90, 18)
seed(0)
npts = 200
x0, y0 = 90, 18 # center of map in (long, lat), degrees
x = x0+uniform(-2, 2, npts)
y = y0+uniform(-2, 2, npts)
#z = x*np.exp(-x**2 - y**2)
z = (x-x0)*np.exp(-(x-x0)**2 - (y-y0)**2) # elevation in meters
# define grid, for points interpolation from the made-up data above
gridx, gridy = 50,50
xi = x0+np.linspace(-2.1, 2.1, gridx)
yi = y0+np.linspace(-2.1, 2.1, gridy)
# interpolate for gridded data of (gridx, gridy)
zi = griddata(x, y, z, xi, yi, interp='linear')
# xi.shape, yi.shape, zi.shape => ((50,), (50,), (50, 50))
xig,yig = np.meshgrid(xi, yi)
# projection
useproj = ccrs.PlateCarree()
fig = plt.figure(figsize = (9, 7))
rect = [0.05, 0.05, 0.95, 0.95] # for map extent
ax = fig.add_axes( rect, projection=useproj )
# contour the gridded data, plotting dots at the nonuniform data points.
CS = ax.contour(xig, yig, zi, 15, linewidths=0.5, colors='k')
CS = ax.contourf(xig, yig, zi, 15,
vmax=abs(zi).max(), vmin=-abs(zi).max())
plt.colorbar(CS) # draw colorbar
# prep points for scatterplot of the gridded points
# make 2 masked-arrays, based on `zi`
mag = ma.masked_greater(zi, 0) # mask points with +ve zi values
mal = ma.masked_less(zi, 0) # mask points with -ve zi values
# apply masking to xig,yig; borrowing mask from mag
xig_greater_masked = ma.MaskedArray(xig, mask=mag.mask) # must have compatible values
yig_greater_masked = ma.MaskedArray(yig, mask=mag.mask)
# apply masking to xig,yig; borrowing mask from mal
xig_less_masked = ma.MaskedArray(xig, mask=mal.mask)
yig_less_masked = ma.MaskedArray(yig, mask=mal.mask)
# for points with -ve z values (result of .masked_greater)
plt.scatter(xig_greater_masked, yig_greater_masked, s=3, color="w", \
alpha=1, zorder=15, label="masked_greater z")
# for points with +ve z values (result of .masked_less)
ax.scatter(xig_less_masked, yig_less_masked, s=3, color="r", alpha=1, \
zorder=15, label="masked_less z")
leg = ax.legend(title='Masked z', framealpha=1.0, facecolor="lightgray")
leg.set_zorder(20)
gl = ax.gridlines(crs=ccrs.PlateCarree(), draw_labels=True,
linewidth=2, color='gray', alpha=0.5, linestyle='--')
gl.xlabels_top = False
gl.ylabels_right = False
gl.xformatter = LONGITUDE_FORMATTER
gl.yformatter = LATITUDE_FORMATTER
gl.xlabel_style = {'size': 15, 'color': 'gray'}
#gl.xlabel_style = {'color': 'gray', 'weight': 'bold'}
plt.title('Masked data plot on contour')
plt.show()
The resulting plot:

A smooth contour plot covering and masked by shapefile in Basemap

I have a polygon shapefile (the state of Illinois) and a CSV file with (lat, lon, zvalue). I want to plot a smooth contour plot representing those zvalues. Following is my code:
import glob
import fiona
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
from mpl_toolkits.basemap import Basemap
from matplotlib.mlab import griddata
# Read in the tabulated data
tabfname = glob.glob("Outputs\\*.csv")[0]
df = pd.read_table(tabfname, sep=",")
print(df.head())
lat, lon, z = list(df.y), list(df.x), list(df["Theil Sen Slope"])
z0, z1, z2 = np.min(z)+0.03, np.mean(z), np.max(z)-0.01
# Read some metadata of the shapefile
shp = glob.glob("GIS\\*.shp")[0]
with fiona.drivers():
with fiona.open(shp) as src:
bnds = src.bounds
extent = [values for values in bnds]
lono = np.mean([extent[0], extent[2]])
lato = np.mean([extent[1], extent[3]])
llcrnrlon = extent[0]-0.5
llcrnrlat = extent[1]-0.5
urcrnrlon = extent[2]+0.5
urcrnrlat = extent[3]+0.5
# Create a Basemap
fig = plt.figure()
ax = fig.add_subplot(111)
m = Basemap(llcrnrlon=llcrnrlon, llcrnrlat=llcrnrlat,
urcrnrlon=urcrnrlon, urcrnrlat=urcrnrlat,
resolution='i', projection='tmerc' , lat_0 = lato, lon_0 = lono)
# Read in and display the shapefile
m.readshapefile(shp.split(".")[0], 'shf', zorder=2, drawbounds=True)
# Compute the number of bins to aggregate data
nx = 100
ny = 100
# Create a mesh and interpolate data
xi = np.linspace(llcrnrlon, urcrnrlon, nx)
yi = np.linspace(llcrnrlat, urcrnrlat, ny)
xgrid, ygrid = np.meshgrid(xi, yi)
xs, ys = m(xgrid, ygrid)
zs = griddata(lon, lat, z, xgrid, ygrid, interp='nn')
# Plot the contour map
conf = m.contourf(xs, ys, zs, 30, zorder=1, cmap='jet')
cbar = m.colorbar(conf, location='bottom', pad="5%", ticks=(z0, z1, z2))
# Scatter plot of the points that make up the contour
for x, y in zip(lon, lat):
X, Y = m(x,y)
m.scatter(X, Y, zorder=4, color='black', s=1)
plt.show()
fig.savefig("Myplot.png", format="png")
And this is the output I got(The scattered black dots are there to show the spatial distribution of the points from which the interpolation was generated. I used Nearest Neighbor interpolation method here.):
I basically referred to the examples given in the following two links to plot this:
https://gist.github.com/urschrei/29cd446ae8a8ec60ddbc
https://matplotlib.org/basemap/users/examples.html
Now this image has 3 problems:
The interpolated contour does not expand within the whole of the shapefile
The part of the contour plot protruding out of the shapefile boundary is not masked off
The contour is not smooth.
What I want is to overcome these three deficiencies of my plot and generate a smooth and nice looking plot similar to the ones shown below (Source: https://doi.org/10.1175/JCLI3557.1 ):
How do I achieve that?

Categories