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()
Related
I am working on an assignment that I have pretty much already completed, but I wanted to add a small touch to it that attempts to fill the area between the two lines with a colormap based on temperature instead of just a simple color. The way the lines are plotted makes them separate entities essentially, so I know that I'll likely need two colormaps that meet each other or overlap to accomplish this but I'm not too sure how to accomplish this. Any assistance is greatly appreciated.
from datetime import datetime
import pandas as pd
import numpy as np
import matplotlib.colors as mcol
import matplotlib.cm as cm
bin = 400
hash = 'fb441e62df2d58994928907a91895ec62c2c42e6cd075c2700843b89'
Temp = pd.read_csv('fb441e62df2d58994928907a91895ec62c2c42e6cd075c2700843b89.csv'.format(bin, hash))
Temp['Date'] = pd.to_datetime(Temp['Date'])
#Only doing this here because the mplleaflet in my personal jupyter notebook is bugged
#will take longer to execute, will take more lines of code for conversions and ultimately is less efficient than simply doing it with pandas.
#print(datetime.strptime(Temp['Date'].to_json(), '%y-%m-%d')) = datetime.strptime(Temp['Date'], format)
Temp['Y'] = Temp['Date'].dt.year
Temp['M'] = Temp['Date'].dt.month
Temp['D'] = Temp['Date'].dt.day
Temp['DV'] = Temp['Data_Value'].div(10)
Temp['E'] = Temp['Element']
Temp = Temp[~((Temp['M']==2) & (Temp['D']==29))]
GrMin = Temp[(Temp['E']=='TMIN') & (Temp['Y']>=2005) & (Temp['Y']<2015)].groupby(['M','D']).agg({'DV':np.min})
FinMin = Temp[(Temp['E']=='TMIN') & (Temp['Y']==2015)].groupby(['M','D']).agg({'DV':np.min})
GrMax = Temp[(Temp['E']=='TMAX') & (Temp['Y']>=2005) & (Temp['Y']<2015)].groupby(['M','D']).agg({'DV':np.max})
FinMax = Temp[(Temp['E']=='TMIN') & (Temp['Y']==2015)].groupby(['M','D']).agg({'DV':np.max})
#x = GrMax
#y = GrMin
#X, Y = np.meshgrid(x,y)
#Z = f(X, Y)
AnomMin = FinMin[FinMin['DV'] < GrMin['DV']]
AnomMax = FinMax[FinMax['DV'] > GrMax['DV']]
#temps = range(-30,40)
plt.figure(figsize=(18, 10), dpi = 80)
red = '#FF0000'
blue = '#0800FF'
cm1 = mcol.LinearSegmentedColormap.from_list('Temperature Map',[blue, red])
cnorm = mcol.Normalize(vmin=min(GrMin['DV']),vmax=max(GrMax['DV']))
cpick = cm.ScalarMappable(norm=cnorm,cmap=cm1)
cpick.set_array([])
plt.title('Historical Temperature Analysis In Ann Arbor Michigan')
plt.xlabel('Month')
plt.ylabel('Temperature in Celsius')
plt.plot(GrMax.values, c = red, linestyle = '-', label = 'Highest Temperatures (2005-2014)')
#plt.scatter(AnomMax, FinMax.iloc[AnomMax], c = red, s=5, label = 'Anomolous High Readings (2015)')
plt.plot(GrMin.values, c = blue, linestyle = '-', label = 'Lowest Temperatures (2005-2014)')
#plt.scatter(AnomMin, FinMin.iloc[AnomMin], c = blue, s=5, label = 'Anomolous Low Readings (2015)')
plt.xticks(np.linspace(0,60 + 60*11, num=12),(r'January',r'February',r'March',r'April',r'May',r'June',r'July',r'August',r'September',r'October',r'November',r'December'))
#Failed Attempt
#plt.contourf(X, Y, Z, 20, cmap = cm1)
#for i in temps
# plt.fill_between(len(GrMin['DV']), GrMin['DV'], i ,cmap = cm1)
#for i in temps
# plt.fill_between(len(GrMin['DV']), i ,GrMax['DV'], cmap = cm1)
#Kind of Close but doesn't exactly create the colormap
plt.gca().fill_between(range(len(GrMin.values)), GrMin['DV'], GrMax['DV'], cmap = cm1)
plt.legend(loc = '0', title='Temperature Guide')
plt.colorbar(cpick, label='Temperature in Celsius')
plt.show()
Current result:
You could draw a colored rectangle covering the curves. And use the polygon created by fill_between to clip that rectangle:
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap
import numpy as np
x = np.linspace(0, 10, 200)
y1 = np.random.normal(0.02, 1, 200).cumsum() + 20
y2 = np.random.normal(0.05, 1, 200).cumsum() + 50
cm1 = LinearSegmentedColormap.from_list('Temperature Map', ['blue', 'red'])
polygon = plt.fill_between(x, y1, y2, lw=0, color='none')
xlim = (x.min(), x.max())
ylim = plt.ylim()
verts = np.vstack([p.vertices for p in polygon.get_paths()])
gradient = plt.imshow(np.linspace(0, 1, 256).reshape(-1, 1), cmap=cm1, aspect='auto', origin='lower',
extent=[verts[:, 0].min(), verts[:, 0].max(), verts[:, 1].min(), verts[:, 1].max()])
gradient.set_clip_path(polygon.get_paths()[0], transform=plt.gca().transData)
plt.xlim(xlim)
plt.ylim(ylim)
plt.show()
A more complicated alternative, would color such that the upper curve corresponds to red and the lower curve to blue:
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 10, 200)
y1 = np.random.normal(0.02, 1, 200).cumsum() + 20
y2 = np.random.normal(0.05, 1, 200).cumsum() + 50
polygon = plt.fill_between(x, y1, y2, lw=0, color='none')
ylim = plt.ylim()
verts = np.vstack([p.vertices for p in polygon.get_paths()])
ymin, ymax = verts[:, 1].min(), verts[:, 1].max()
gradient = plt.imshow(np.array([np.interp(np.linspace(ymin, ymax, 200), [y1i, y2i], np.arange(2))
for y1i, y2i in zip(y1, y2)]).T,
cmap='turbo', aspect='auto', origin='lower', extent=[x.min(), x.max(), ymin, ymax])
gradient.set_clip_path(polygon.get_paths()[0], transform=plt.gca().transData)
plt.ylim(ylim)
plt.show()
A variant could be to smooth out the color values in the horizontal direction (but still clip using the original curves):
from scipy.ndimage import gaussian_filter
gradient = plt.imshow(np.array([np.interp(np.linspace(ymin, ymax, 200), [y1i, y2i], np.arange(2))
for y1i, y2i in zip(gaussian_filter(y1, 4, mode='nearest'),
gaussian_filter(y2, 4, mode='nearest'))]).T,
cmap='turbo', aspect='auto', origin='lower', extent=[x.min(), x.max(), ymin, ymax])
After correcting some functional errors within my code then applying the code provided by JohanC as well as asking for some other much-needed guidance, I was able to successfully complete the colormap. It would probably be more visually appealing if the upper and lower line plots were a different color but as far as the colormap is concerned, mission accomplished. Thanks again for the assistance!
from datetime import datetime
import pandas as pd
import numpy as np
import matplotlib.colors as mcol
import matplotlib.cm as cm
bin = 400
hash = 'fb441e62df2d58994928907a91895ec62c2c42e6cd075c2700843b89'
Temp = pd.read_csv('fb441e62df2d58994928907a91895ec62c2c42e6cd075c2700843b89.csv'.format(bin, hash))
Temp['Date'] = pd.to_datetime(Temp['Date'])
#Only doing this here because the mplleaflet in my personal jupyter notebook is bugged
#will take longer to execute, will take more lines of code for conversions and ultimately is less efficient than simply doing it with pandas.
#print(datetime.strptime(Temp['Date'].to_json(), '%y-%m-%d')) = datetime.strptime(Temp['Date'], format)
Temp['Y'] = Temp['Date'].dt.year
Temp['M'] = Temp['Date'].dt.month
Temp['D'] = Temp['Date'].dt.day
Temp['DV'] = Temp['Data_Value'].div(10)
Temp['E'] = Temp['Element']
Temp = Temp[~((Temp['M']==2) & (Temp['D']==29))]
GrMin = Temp[(Temp['E']=='TMIN') & (Temp['Y']>=2005) & (Temp['Y']<2015)].groupby(['M','D']).agg({'DV':np.min})
FinMin = Temp[(Temp['E']=='TMIN') & (Temp['Y']==2015)].groupby(['M','D']).agg({'DV':np.min})
GrMax = Temp[(Temp['E']=='TMAX') & (Temp['Y']>=2005) & (Temp['Y']<2015)].groupby(['M','D']).agg({'DV':np.max})
FinMax = Temp[(Temp['E']=='TMAX') & (Temp['Y']==2015)].groupby(['M','D']).agg({'DV':np.max})
GrMax = GrMax.reset_index()
GrMin = GrMin.reset_index()
FinMax = FinMax.reset_index()
FinMin = FinMin.reset_index()
#x = GrMax
#y = GrMin
#X, Y = np.meshgrid(x,y)
#Z = f(X, Y)
AnomMin = FinMin[FinMin['DV'] < GrMin['DV']]
AnomMax = FinMax[FinMax['DV'] > GrMax['DV']]
#temps = range(-30,40)
plt.figure(figsize=(18, 10), dpi = 160)
red = '#FF0000'
blue = '#0800FF'
cm1 = mcol.LinearSegmentedColormap.from_list('Temperature Map',[blue, red])
cnorm = mcol.Normalize(vmin=min(GrMin['DV']),vmax=max(GrMax['DV']))
cpick = cm.ScalarMappable(norm=cnorm,cmap=cm1)
cpick.set_array([])
plt.title('Historical Temperature Analysis In Ann Arbor Michigan')
plt.xlabel('Month')
plt.ylabel('Temperature in Celsius')
plt.plot(GrMax['DV'], c = red, linestyle = '-', label = 'Highest Temperatures (2005-2014)')
plt.scatter(AnomMax.index, AnomMax['DV'], c = red, s=2, label = 'Anomolous High Readings (2015)')
plt.plot(GrMin['DV'], c = blue, linestyle = '-', label = 'Lowest Temperatures (2005-2014)')
plt.scatter(AnomMin.index, AnomMin['DV'], c = blue, s=2, label = 'Anomolous Low Readings (2015)')
plt.xticks(np.linspace(0,365,12, endpoint = True),(r'January',r'February',r'March',r'April',r'May',r'June',r'July',r'August',r'September',r'October',r'November',r'December'))
#Start: Assisted from StackOverFlow user JohanC v
x = np.arange(len(GrMin['DV'].fillna(0).astype('float64').ravel()))
y1 = GrMax['DV'].fillna(0).astype('float64').ravel()
y2 = GrMin['DV'].fillna(0).astype('float64').ravel()
polygon = plt.fill_between(x, y1, y2, lw=0, color='none')
xlim = (x.min(), x.max())
ylim = plt.ylim()
verts = np.vstack([p.vertices for p in polygon.get_paths()])
gradient = plt.imshow(np.linspace(1, 0, 256).reshape(-1, 1), cmap=cm1, aspect='auto',
extent=[verts[:, 0].min(), verts[:, 0].max(), verts[:, 1].min(), verts[:, 1].max()])
gradient.set_clip_path(polygon.get_paths()[0], transform=plt.gca().transData)
plt.xlim(xlim)
plt.ylim(ylim)
#Finish: Assisted from StackOverFlow user JohanC ^
#Failed Attempt at gradient fill with colormap
#plt.contourf(X, Y, Z, 20, cmap = cm1)
#for i in temps
# plt.fill_between(len(GrMin['DV']), GrMin['DV'], i ,cmap = cm1)
#for i in temps
# plt.fill_between(len(GrMin['DV']), i ,GrMax['DV'], cmap = cm1)
#Kind of Close but doesn't exactly create the colormap
#plt.gca().fill_between(range(len(GrMin.values)), GrMin['DV'], GrMax['DV'], facecolor = 'grey', alpha = 0.10)
plt.legend(loc = 'lower center', title='Temperature Guide')
plt.colorbar(cpick, label='Temperature in Celsius')
plt.show()
enter image description here
I have 9 temperature points. 1 in the center and 8 on the circle. I need to create a heatmap in a circle. I set the points at which to perform calculations, and use the scipy.interpolate.griddata, but the full circle is not drawn, program draws an octagon. How can i fill in the missing parts?
import scipy.interpolate
import numpy
import matplotlib
import matplotlib.pyplot as plt
import math
# close old plots
plt.close("all")
# some parameters
xy_center = [2,2] # center of the plot
radius = 2 # radius
# mostly original code
meanR = [33.9, 34.2, 33.1, 33.5, 33., 32.7, 32.3, 31.8, 35.]
x = numpy.array([2, 2, 2+math.sqrt(2), 4, 2+math.sqrt(2), 2, 2+(-math.sqrt(2)), 0, 2+(-math.sqrt(2))])
y = numpy.array([2, 4, 2+math.sqrt(2), 2, 2+(-math.sqrt(2)), 0, 2+(-math.sqrt(2)), 2, 2+math.sqrt(2)])
z = meanR
xi, yi = numpy.mgrid[x.min():x.max():500j, y.min():y.max():500j]
zi = scipy.interpolate.griddata((x, y), z, (xi, yi), method='cubic')
# make figure
fig = plt.figure(figsize=(10, 10))
# set aspect = 1 to make it a circle
ax = fig.add_subplot(111, aspect = 1)
# use different number of levels for the fill and the lines
CS = ax.contourf(xi, yi, zi, 300, cmap=plt.cm.viridis, zorder=1)
# make a color bar
cbar = fig.colorbar(CS, ax=ax)
# add the data points
ax.scatter(x, y, marker = 'o', c = 'b', s = 15, zorder = 3)
for i in range(9):
ax.annotate(str(z[i]), (x[i],y[i]))
# draw a circle
circle = matplotlib.patches.Circle(xy = xy_center, radius = radius, edgecolor = "k", facecolor = "none")
ax.add_patch(circle)
# remove the ticks
ax.set_xticks([])
ax.set_yticks([])
# set axes limits
ax.set_xlim(-0.5, 4.5)
ax.set_ylim(-0.5, 4.5)
plt.show()
Radial basis functions (Rbf) can be used to interpolate/extrapolate your data.
scipy.interpolation Here is a modified code that produces the plot you need.
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import math
from scipy.interpolate import Rbf
# some parameters
xy_center = [2,2] # center of the plot
radius = 2 # radius
# Data part
# ---------
# mostly original code
meanR = [33.9, 34.2, 33.1, 33.5, 33., 32.7, 32.3, 31.8, 35.] #9 points data
x = np.array([2, 2, 2+math.sqrt(2), 4, 2+math.sqrt(2), 2, 2+(-math.sqrt(2)), 0, 2+(-math.sqrt(2))])
y = np.array([2, 4, 2+math.sqrt(2), 2, 2+(-math.sqrt(2)), 0, 2+(-math.sqrt(2)), 2, 2+math.sqrt(2)])
z = meanR
# use RBF (Radial basis functions) that allows extrapolation
rbf = Rbf(x, y, z, epsilon=radius+1) #epsilon is based on some parameters of the data
# Interpolation/extrapolation
# ---------------------------
xi, yi = np.mgrid[x.min():x.max():500j, y.min():y.max():500j]
# applies and get inter/extra-polated values
zi = rbf(xi, yi)
# make zi outside circle --> np.none
midr,midc = zi.shape[0]/2, zi.shape[1]/2
for er in range(zi.shape[0]):
for ec in range(zi.shape[1]):
if np.abs(math.sqrt((er-midr)**2 + (ec-midc)**2))>zi.shape[0]/2:
# outside the circle, dont plot this pixel
zi[er][ec] = np.nan
pass
pass
# make figure
fig = plt.figure(figsize=(8, 8))
# set aspect = 1 to make it a circle
ax = fig.add_subplot(111, aspect = 1)
# add the data points
ax.scatter(x, y, marker = 'o', c = 'b', s = 15, zorder = 3)
for i in range(9):
ax.annotate(str(z[i]), (x[i],y[i]))
# draw a circle
circle = matplotlib.patches.Circle(xy = xy_center, radius = radius, edgecolor = "k", facecolor = "none")
ax.add_patch(circle)
CS = ax.contourf(xi, yi, zi, 300, cmap=plt.cm.viridis, zorder=1)
cbar = fig.colorbar(CS, ax=ax, shrink=0.7) # make a color bar
# remove the ticks
ax.set_xticks([])
ax.set_yticks([])
# set axes limits
ax.set_xlim(-0.5, 4.5)
ax.set_ylim(-0.5, 4.5)
plt.show()
The result:
I would like to create 3d scatter plot with colormap range from min(u), u =64 to max(u), u=100. u is a 1d array
The code works as expected, u is increasing from the center (x,y,z)=(0,0,0) but the colors is incorrect, the color gradient should range according to u, from min(u) to max(u) instead of depending on x,y,z coordinate. Also colorbar is not correct (should be from 0 to 100)
fig = plt.figure(figsize = (8,6))
ax = fig.add_subplot(111, projection='3d')
ax.set_title('normal distribution')
#add the line/data in our plot
x = 18 * np.random.normal(size =500)
y = 18 * np.random.normal(size =500)
z = 18 * np.random.normal(size =500)
u = np.linspace(64, 100, 500)
norma = mpl.colors.Normalize(min(u), max(u))
color = np.linalg.norm([x,y,z], axis=0)
track = ax.scatter(x,y,z, s=35, c = color, alpha = 1, cmap='inferno', norm = norma)
plt.colorbar(track, label='color map', shrink=0.6)
fig = plt.figure(figsize = (8,6))
ax = fig.add_subplot(111, projection='3d')
ax.set_title('normal distribution')
the above code figure
When the color map Normalise to vmin=min(u) and vmax=max(u), the color gradient is lost and colormap gradient values are spread randomly along the x,y,z axis instead of being in ordered array.
Does someone know how to fix the color gradient along the axis, while the center of u is at (0,0,0) with the correct color bar (0-100) please?
fig = plt.figure(figsize = (8,6))
ax = fig.add_subplot(111, projection='3d')
ax.set_title('normal distribution')
#add the line/data in our plot
x = 18 * np.random.normal(size =500)
y = 18 * np.random.normal(size =500)
z = 18 * np.random.normal(size =500)
u = np.linspace(100, 64, 500)
norma = mpl.colors.Normalize(vmin=0, vmax = 100)
color = np.linalg.norm([u], axis=0)
track = ax.scatter(x,y,z, s=35, c = color, alpha = 1, cmap='inferno', norm = norma)
plt.colorbar(track, label='color map', shrink=0.6)
The result of the second example
x = 18 * np.random.normal(size =500)
y = 18 * np.random.normal(size =500)
z = 18 * np.random.normal(size =500)
# collect all data in array
data = np.array([x,y,z])
# center in a given dimension is the mean of all datapoints:
# reshape to allow easy subtraction
center = np.mean(data, axis=1).reshape(3,-1)
# for each datapoint, calculate distance to center and use as color value
color = np.linalg.norm(data - center, axis=0)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
track = ax.scatter(x,y,z, s=35, c = color, alpha = 1, cmap='inferno')
plt.colorbar(track, label='color map', shrink=0.6)
I found this question which seems to answer your question about the coordinates. The answers also show how to evenly distribute coordinates if you prefer to do that.
After getting the coordinates, you can then get the distance from the center as the color value (like warped did in his answer). I adjusted the distance to reflect your specifications. This is the resulting code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import Normalize
from mpl_toolkits.mplot3d import Axes3D
number_of_particles = 500
sphere_radius = 18
# create the particles
radius = sphere_radius * np.random.uniform(0.0, 1.0, number_of_particles)
theta = np.random.uniform(0., 1., number_of_particles) * 2 * np.pi
phi = np.random.uniform(0., 1., number_of_particles) * 2 * np.pi
x = radius * np.sin(theta) * np.cos(phi)
y = radius * np.sin(theta) * np.sin(phi)
z = radius * np.cos(theta)
# collect all data in array
data = np.array([x, y, z])
# for each datapoint, calculate distance to center and use as color value
color = radius
color /= sphere_radius
color = color * 36 + 64
# initialize a figure with a plot
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
# add the points and the colorbar
track = ax.scatter(x, y, z, s=35, c=color, alpha=1, cmap='inferno',
norm=Normalize(0, 100))
plt.colorbar(track, label='color map', shrink=0.6)
plt.show()
My result looks like this:
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.
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: