strange plot surface of matplotlib - python

I'm trying to generate a 3D height figure, I have a regular grid, the height data collected by the sensor and data store in a file which name is "data.txt". data stored one data per line. the file link on github
import numpy as np
import matplotlib.pyplot as pit
from mpl_toolkits.mplot3d import Axes3D
from scipy.stats import multivariate_normal
from matplotlib import cm
x = np.linspace(0,350,18)
y = np.linspace(0,350,15)
z = np.loadtxt('data.txt')
xx,yy = np.meshgrid(x,y)
fig = pit.figure()
ax = fig.add_subplot(111,projection='3d')
ax.scatter(xx,yy,z)
use the code above, I got a scatter. It looks good! I found this , I want convert the figure to surface, than I add the code below, but it looks very strange
xa = np.reshape(xx, (18,15))
ya = np.reshape(yy, (18,15))
za = np.reshape(z, (18,15))
surf=ax.plot_surface(xa,ya,za,cmap="summer",linewidth=0,antialiased=False, alpha=0.5)
fig.colorbar(surf)
pit.show()
the image
i don't know what happened, it look too strange! Should i smooth it?

You need to use xx and yy defined earlier and reshape z to the same shape as xx:
za = z.reshape(xx.shape)
fig = pit.figure()
ax = fig.add_subplot(111,projection='3d')
surf=ax.plot_surface(xx,yy,za,cmap="summer",linewidth=0,antialiased=False, alpha=0.5)
fig.colorbar(surf)
pit.show()
Note that I have rotate the chart for better clarity.

I think you need scipy.griddata. Try this code:
from scipy.interpolate import griddata
za = griddata(x, y, z, (xx, yy), method='linear')
surf=ax.plot_surface(xx,yy,za,cmap="summer",linewidth=0,antialiased=False, alpha=0.5)
fig.colorbar(surf)
plt.show()

Related

How do I remove overflow along the z-axis for a 3D matplotlib surface?

I'm trying to graph a 3d mesh surface with matplotlib and constrain the limits of the graph. The X and Y axes are correctly constrained, but there is overflow in the Z-Axis.
What am I missing? Here's my code:
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits import mplot3d
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure(figsize=(10,10))
x = np.linspace(-6,6,100)
y = np.linspace(-6,6,100)
X,Y = np.meshgrid(x,y)
def f(x,y):
return x**2 + 3*y
Z = f(X,Y)
ax = plt.axes(projection = '3d')
ax.plot_surface(X,Y,Z,cmap='viridis')
ax.title.set_text("z=x**2+3y")
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
ax.set_zlim3d(zmin=-3,zmax=5)
ax.set_xlim3d(xmin=-6,xmax=6)
ax.set_ylim3d(ymin=-6,ymax=6)
plt.show()
The graph:
Edit:
When I add clipping/min/max to the Z values, the graph is a little better, but it sets z values outside the bounds to the bounds themselves. Both of the following suggestions do this. Perhaps it's because I'm on a mac?
z_tmp = np.maximum(np.minimum(5,Z),-3)
z_temp = np.clip(Z, -3, 5, None)
Your data is outside the axis boundaries. Try rotate the view and you will notice.
z = x**2 + 3*y
If you want to only show a defined area of the data you could add a max() min() limitation on the Z data to exclude the data outside your wanted limitations.
Z = f(X,Y)
z_tmp = np.maximum(np.minimum(5,Z),-3)
ax = plt.axes(projection = '3d')
ax.plot_surface(X,Y,z_tmp,cmap='viridis')
I'm not sure the matplotlib behaves as it should in your default case.

Plotly plot_trisurf isn't working with arange arrays

I've basically just copied the example code found on the Matplotlib website, but I replaced their radii and angles with simple arange arrays.
I've tried different array functions and I can't seem to figure out anything.
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
from Equation import Expression
x = np.arange(0,100,0.01)
y = np.arange(0,100,0.01)
x2 = np.append(0,x.flatten())
y2 = np.append(0,y.flatten())
z = x2 + y2
print(z)
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot_trisurf(x, y, z, linewidth=0.2, antialiased=True)
plt.show()
I'm just trying to make a graph of z = x + y but I'm getting a confusing error.
"RuntimeError: Error in qhull Delaunay triangulation calculation: singular input data (exitcode=2); use python verbose option (-v) to see original qhull error."
Edit: I've also tried it without calling flatten() but I get the same result though.
The error you are getting is because your z is not a surface but a line. You need to use at least 3 points that would define a plane. One option could be to use np.meshgrid to create your surface for plotting and then flatten everything to insert into the function. Try going back to some example code here. Note you may also want to change your resolution depending on the detail of your surface.
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(0,100,1)
y = np.arange(0,100,1)
x2 = np.append(0,x.flatten())
y2 = np.append(0,y.flatten())
x2,y2 = np.meshgrid(x2,y2) #This is what you were missing
z = x2 + y2
fig = plt.figure(figsize=(12,12))
ax = fig.gca(projection='3d')
ax.plot_trisurf(x2.flatten(), y2.flatten(), z.flatten(), linewidth=0.2, antialiased=True) #flatten all the arrays here
plt.show()

Plot a surface with lists of latitude, longitude and elevation data (hillshading)

I found this script on the matplotlib website:
"""
Demonstrates using custom hillshading in a 3D surface plot.
"""
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cbook
from matplotlib import cm
from matplotlib.colors import LightSource
import matplotlib.pyplot as plt
import numpy as np
filename = cbook.get_sample_data('jacksboro_fault_dem.npz', asfileobj=False)
with np.load(filename) as dem:
z = dem['elevation']
nrows, ncols = z.shape
x = np.linspace(dem['xmin'], dem['xmax'], ncols)
y = np.linspace(dem['ymin'], dem['ymax'], nrows)
x, y = np.meshgrid(x, y)
region = np.s_[5:50, 5:50]
x, y, z = x[region], y[region], z[region]
fig, ax = plt.subplots(subplot_kw=dict(projection='3d'))
ls = LightSource(270, 45)
# To use a custom hillshading mode, override the built-in shading and pass
# in the rgb colors of the shaded surface calculated from "shade".
rgb = ls.shade(z, cmap=cm.gist_earth, vert_exag=0.1, blend_mode='soft')
surf = ax.plot_surface(x, y, z, rstride=1, cstride=1, facecolors=rgb,
linewidth=0, antialiased=False, shade=False)
plt.show()
They use the file jacksboro_fault_dem.npz to plot the elevation data and they get something like that:
Thanks to Google Earth I was able to get the text file maido_elevation_data.txt with latitude, longitude and elevation data of the following area (Maïdo, Reunion Island):
I made a function to get 3 lists for each coordinate from the text file:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
def get_LAT_LONG_ALT(text_file):
ch=""
LAT=[]
LONG=[]
ALT=[]
with open(text_file,"r") as fich:
for ligne in fich:
for e in ligne:
ch+=e
liste=ch.replace("\n","").split("\t")
LAT.append(float(liste[0]))
LONG.append(float(liste[1]))
ALT.append(float(liste[2]))
ch=""
return LAT,LONG,ALT
fig = plt.figure()
axes = fig.add_subplot(111, projection="3d")
X = get_LAT_LONG_ALT("maido_elevation_data.txt")[0]
Y = get_LAT_LONG_ALT("maido_elevation_data.txt")[1]
Z = get_LAT_LONG_ALT("maido_elevation_data.txt")[2]
axes.scatter(X,Y,Z, c="r", marker="o")
axes.set_xlabel("Latitude")
axes.set_ylabel("Longitude")
axes.set_zlabel("Altitude")
plt.show()
How should I modify the script to get a good surface plot with my own data like they do?
PS: I will give you the links of the files in the comments because I'm not allowed to put more than 2 links... yes, I'm new :)
You should reshape your data it is a three column data x,y and z
You should have a file with only z values in a 2D table columns are x and rows are y.
Meshgrid fucntion in python should help.

creating surface data for axes3d

Okay, apologies for this question but I'm pulling my hair out here.
I have a data structure loaded in python in the form:
[(1,0,#),(1,1,#),(1,2,#),(1,3,#),(2,0,#),(2,1,#) ... (26,3,#)]
with # being a different number each time that I wish to represent on the z-axis. You can see that x and y are always integers.
Plotting a scatter graph is simple:
x,y,z = zip(*data)
fig = plt.figure()
ax = fig.gca(projection = '3d')
surface = ax.scatter(x, y, z)
plt.show()
But when it comes to surfaces, I can see two methods:
1) Call ax.plot_trisurf(), which should work with 1D arrays similar to ax.scatter() and apparently works here, but for me gives me an error:
"AttributeError: Axes3D subplot object has not attribute 'plot_trisurf'"
This error also appears if I use the example source code at:
http://matplotlib.org/mpl_toolkits/mplot3d/tutorial.html#tri-surface-plots, suggesting it's something wrong with my installation - my Matplotlib version is 1.1.1rc,. This error does not appear if, for example, ax.plot_surface() is called, nor ax.scatter().
2) Use meshgrid() or griddata() in combination with ax.plot_surface() - in either case, after two days' of pouring over the documentation and examples, I still don't understand how to correctly use these in my case, particularly when it comes to generating the values for Z.
Any help would be much appreciated.
To address your first question (1) I believe you need to import Axes3D from the mplot3d library, even if you're not directly calling it. Maybe try adding
from mpl_toolkits.mplot3d import Axes3D
before your main code (this line triggered a memory while reading the tutorial).
As for (2), X, Y and Z need to be matrix (2d array) type objects. This can get confusing, but you may consider an example:
# two arrays - one for each axis
x = np.arange(-5, 5, 0.25)
y = np.arange(-5, 5, 0.25)
# create a mesh / matrix like object from the arrays
X, Y = np.meshgrid(x, y)
# create Z values - also in a mesh like shape
Z = np.sin(np.sqrt(X**2 + Y**2))
# plot!
surface = ax.plot_surface(X, Y, Z)
Here is an example of how could you extract your z-values from data
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
import matplotlib.pyplot as plt
import numpy as np
data = [(j,i,i**2 + j) for j in range(1,27) for i in range(4)]
print data
fig = plt.figure()
ax = fig.gca(projection='3d')
X = np.arange(0, 4, 1)
Y = np.arange(1, 27, 1)
X, Y = np.meshgrid(X, Y)
print X.shape
print Y.shape
Z = np.array([z for _,_,z in data]).reshape(26,4)
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.coolwarm,
linewidth=0, antialiased=True)
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.xlabel('X')
plt.ylabel('Y')
plt.show()

Plotting A Hyperboloid

Teacher in class gave this formula
w = x**2 + y**2 - z**2
and showed its 3d graphic in class seen below. How do I plot this using Matplotlib (minus the intersecting plane)? I guess first a specific value for w needs to be selected, for example 10, otherwise 3d plotting would not be possible. Then should I convert to polar coordinates because of the z**2 in the formula? I tried this and failed. Any help would be appreciated. Also, does this shape have a name?
Got it. Found some good stuff here, and following the formulas presented, I have the Python code below.
http://msenux.redwoods.edu/Math4Textbook/Plotting/ParametricSurfaces.pdf
from __future__ import division
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure(figsize=plt.figaspect(1)) # Square figure
ax = fig.add_subplot(111, projection='3d')
r=1;
u=np.linspace(-2,2,200);
v=np.linspace(0,2*np.pi,60);
[u,v]=np.meshgrid(u,v);
a = 1
b = 1
c = 1
x = a*np.cosh(u)*np.cos(v)
y = b*np.cosh(u)*np.sin(v)
z = c*np.sinh(u)
ax.plot_surface(x, y, z, rstride=4, cstride=4, color='b')
plt.show()

Categories