I am attempting to plot 2d arrays on nonlinear surfaces in in a 3d plot. I have 2d arrays, where every column has an associated x, and y coordinate. Rows correspond to depth slices. I need to plot these 2d arrays in 3d space on a surface which has variable x and y coordinates. I have attempted using mayavi.imshow, but do not see a way to show the 2d array as an image on an irregular surface.
Example data:
x = np.linspace(100,120,50)
y = np.random.randint(50,60,x.shape)
z = np.arange(20)
data = np.random.rand(x.shape[0],y.shape[0])
# plot with mayavi
mlab.imshow(data)
mlab.show()
My goal is to be able to display this array in 3d space using the x,y,z values for each sample in my array. I am open to an plotting suggestions. originally attempted doing this in matplotlib, but was also unable to display my array on an irregular surface.
Related
I am trying to plot a 3D numpy array (256 x 256 x 256) as essentially an array of points, which should be colored by their value. I keep getting something like this
if I multiply by data by 1000, I get this:
My data is a 3D np array composed of slices, and the slices look like this:
There are about 50 or so slices, so the 3D visualization should be showing a sphere. I can visualize the slices just fine
My code is a modified example of this example: https://docs.pyvista.org/examples/02-plot/volume.html using the technique given here in this link to make a pyvista.UniformGrid object out of a 3D numpy array: https://docs.pyvista.org/examples/00-load/create-uniform-grid.html
def plot_3d_pyvista(self):
import pyvista as pv
values = self.reconstructed_source_3D
# Create the spatial reference
grid = pv.UniformGrid()
# Set the grid dimensions: shape because we want to inject our values on the
# POINT data
grid.dimensions = values.shape
# Edit the spatial reference
#grid.origin = (1, 1, 1) # The bottom left corner of the data set
#grid.spacing = (1, 1, 1) # These are the cell sizes along each axis
# Add the data values to the cell data
grid.point_data["values"] = values.flatten(order="F")
p = pv.Plotter()
p.add_volume(grid)
p.show()
If I use a sphere, eg, a 3D numpy array of zeros with only the center spherical elements set to 1, I get something like this which clearly shows a sphere but is way too transparent:
Alternatively, if anyone knows a way using a different package to plot a volume like this, I could use that. I just need a way to visualize what the 3D looks like.
I have a 3D numpy array Lattice[][][], and a 2D numpy array Trajectory[][].
I want the indices i/j/k of Lattice to be the coordinate axes and the value contained in the tensor element to be plotted as a colored dot, making a 3D mesh maybe
and I want to plot Trajectory where the Trajectory[p][0]/[1]/[2] contains the coordinates, and the index p is the iteration number - so this I want to plot as a 3D line, hopefully inside the mesh. I have no idea from where to start, is it possible in matplotlib?
You could definetly use matplotlib. Check this link: https://matplotlib.org/2.0.2/mpl_toolkits/mplot3d/tutorial.html
But you could also try a different package, like Plotly: https://plotly.com/python/3d-charts/
I'm struggling with an issue relating to Matplotlib and Numpy.
I am trying to create hillshading on my surface plots.
My input data is an irregular spacing of XYZ points derived from LiDAR.
I can generate a trisurf3D plot or 3Dscatter no problem. Save it, change the camera angles, colour it based on Z and animate it but for the life of me I can't get any sort of shading in there at all.
I'm getting stuck at Matplotlib requiring 2D arrays for X and Y and Z. My input data is honestly tiny: 376704 points, each with an XYZ value. I have converted the points to a euclidean coordinate system starting at 0:
from laspy.file import File as LAS
import numpy as np
def lasToNumpy(lasFile):
f = LAS(lasFile,mode='r')
## Establish min values
xmin = min(f.x)
ymin = min(f.y)
zmin = min(f.z)
## Arrays now in meters from 0 to max
x = np.array(f.x-xmin)
y = np.array(f.y-ymin)
z = np.array(f.z-zmin)
## Assign a max of each x and y
xmax = max(x)
ymax = max(y)
The issue is my next step is to create a meshgrid (as is seemingly required to generate a 2D array).
This eats about 50GB of RAM:
X, Y = np.meshgrid(x,y)
And rightfully so.
All I want to do is add hillshading to my surface but the whole 2D array seems so illogically unnecessary! What are my options here? Is this just not going to happen? For reference my my trisurf3D works fine:
fig = plt.figure(figsize=(60.0,60.0))
ax = fig.add_subplot(111, projection='3d')
ax.plot_trisurf(x,y,z, cmap='plasma', edgecolor='black', alpha=0.5)
Really want to throw some hill shading in there as well.
This question may be obsolete now, but for other users, the problem here is that you are trying to make a mesh of 376704 points in each direction using np.meshgrid. The purpose of np.meshgrid is to take the x and y ranges and create a grid. For example:
x=np.arange(0,100) #1D array
y=np.linspace(-50,50,1111) # 1D array
xgrid,ygrid=np.meshgrid(x,y) #Outputs 2D arrays
Only use np.meshgrid if you want to grid your data. You can grid your data to lower resolution using a 3D interpolator such as RegularGridInterpolator and is one way to solve your problem and create your hill.
A quicker and better option in my opinion is using tricontourf. The function takes in the 1D arrays that you have to create the hill shading figure you desire. If you can't get this to work, update your question with a some data.
I have some surface data given as x_mesh, y_mesh, z_mesh.
x_mesh and y_mesh were generated from steps by longitude and latitude on a geotiff (so, their shapes are equal and regular but steps by x and y are not).
z_mesh is the height from tangential plane to Earth ellipsoid at the center of map.
I can easily plot the surface with matplotlib.pyplot.pcolormesh(x_mesh, y_mesh, z_mesh). It works.
Now I want to set a line by mouse and somehow take a Z profile under this line. I need some interpolator to make xy --> z, but don't know which one to use.
I tried to do this:
scipy.interpolate.interp2d(x_mesh, y_mesh, z_mesh)
But it gives me an error: OverflowError: Too many data points to interpolate
Don't you have any ideas how to interpolate such data?
P.S. The geotiff is not very big, it is 6K x 6K pixels. And I see that pcolormesh somehow interpolates the color value between pixels if I zoom in.
I am having trouble contouring some data in matplotlib. I am trying to plot a vertical cross-section of temperature that I sliced from a 3d field of temperature.
My temperature array (T) is of size 50*300 where 300 is the number of horizontal levels which are evenly spaced. However, 50 is the number of vertical levels that are: a) non-uniformly spaced; and b) have a different starting level for each vertical column. As in there are always 50 vertical levels, but sometimes they span from 100 - 15000 m, and sometimes from 300 - 20000 m (due to terrain differences).
I also have a 2d array of height (Z; same shape as T), a 1d array of horizontal location (LAT), and a 1d array of terrain height (TER).
I am trying to get a similar plot to one like here in which you can see the terrain blacked out and the data is contoured around it.
My first attempt to plot this was to create a meshgrid of horizontal distance and height, and then contourf temperature with those arguments as well. However numpy.meshgrid requires 1d inputs, and my height is a 2d variable. Doing something like this only begins contouring upwards from the first column:
ax1 = plt.gca()
z1, x1 = np.meshgrid(LAT, Z[:,0])
plt.contourf(z1, x1, T)
ax1.fill_between(z1[0,:], 0, TER, facecolor='black')
Which produces this. If I use Z[:,-1] in the meshgrid, it contours underground for columns to the left, which obviously I don't want. What I really would like is to use some 2d array for Z in the meshgrid but I'm not sure how to go about that.
I've also looked into the griddata function but that requires 1D inputs as well. Anyone have any ideas on how to approach this? Any help is appreciated!
For what I understand your data is structured. Then you can directly use the contourf or contour option in matplotlib. The code you present have the right idea but you should use
x1, z1 = np.meshgrid(LAT, Z[:,0])
plt.contourf(x1, Z, T)
for the contours. I have an example below
import numpy as np
import matplotlib.pyplot as plt
L, H = np.pi*np.mgrid[-1:1:100j, -1:1:100j]
T = np.cos(L)*np.cos(2*H)
H = np.cos(L) + H
plt.contourf(L, H, T, cmap="hot")
plt.show()
Look that the grid is generated with the original bounding box, but the plot is made with the height that has been transformed and not the initial one. Also, you can use tricontour for nonstructured data (or in general), but then you will need to generate the triangulation (that in your case is straightforward).