Surface plot for square matrix and labelling data points - matlab

I want to plot 3D surface plots (which look like mountains). My data are square matrices. I want to be able to label the data where there is kind of a big cliff.
How can I go about it? Thanks

For a single 2D matrix you can use SURF function to plot 3D surface:
% generate random square 2D matrix 20x20
x = rand(20);
% make some (10) mountains
x(randi(numel(x),10,1))=rand(10,1)+5;
% plot surface
surf(x)
How do you want to plot multiple surfaces? On a single figure?
To label the large points lets threshold the data:
cutvalue = 1;
iHigh = find(x(:) > cutvalue);
[irow,icol] = ind2sub(size(x), iHigh);
hold on
plot3(icol, irow, x(iHigh), 'ro')
hold off

Related

Draw only the border of scatter plot in Matlab

I have a bunch of 2 dimensional vectors in Matlab. If I plot them in two dimension space using scatter they fill an area which is not convex. Is there ay way to simply draw only the border of the area? I have tried hist plus contour or convex hull but they do not do the job.
From matlab help:
x = gallery('uniformdata',[10,1],0);
y = gallery('uniformdata',[10,1],1);
DT = delaunayTriangulation(x,y);
k = convexHull(DT)
figure
plot(DT.Points(:,1),DT.Points(:,2), '.','markersize',10);
hold on
plot(DT.Points(k,1),DT.Points(k,2),'r')
hold off

matlab: Graph 2d lines on x,y and z axis in a 3d plot

Given a complex signal I have 3 dimensions: I-real part, Q-imaginary part, T-time.
I have been able to use the:
plot3(I,T,Q)
to plot the signal in matlab. I would now like to add the Q-imaginary part line graph to the z-plane and the I-real part line graph to the x,y plane. How can I add the additional lines to the graph? I included a picture of what I want it to look like:
What I have so far is this:
Commented code below:
% hold on for multiple plots
figure(1)
hold on
% Plot 3D Figure
plot3(I,T,Q)
% Plot on XY plane - means function in Z = 0
plot3(I,T,Q.*0)
% Plot on YZ plane - means function in X = 0
plot(I.*0,T,Q)
hold off
In your case the planes being plotted on aren't actually the axes' zeros. You may want to set the zero vector in each 2D plot to be any single-valued vector, which you can make the correct length by methods like this:
% vector of 2s the same size as Q
A = (Q./Q).*2;
% or
A = ones(size(Q)).*2;
% or
A = A.*0 + 2;
For instance, plotting a similar function to your image:
x = linspace(0,20,1000);
hold on
plot3(sin(x),x,cos(x))
plot3(sin(x),x,cos(x).*0 - 1)
plot3(sin(x).*0 + 1,x,cos(x))
grid on
hold off

Plot data on to an isosurface in MatLab

I have two 3D arrays, one contains the 3D data of an electric field and the other is a 3D mask of the object that I am interested in, taking the product of these two arrays gives me an array with the electric field of just the mask. I have successfully created an isosurface of the mask but when I try to make an isosurface of the product of the mask and the electric field array I get the same graph as when I plot just the mask (which is just an array of zeroes and 1's). Is there a way to represent the data on the isosurface?
For the moment I simply put my arrays in to the isosurface function:
isosurface(mask), which gives me:
I then try to plot the product of my mask and data array:
isosurface(mask.*EArr) and I get the following:
But if I look at just a single slice using the code
imagesc(mask(:,:,35).*EArr(:,:,35)) I get:
What I'm looking to do is to get view the slices of the last picture as a 3D object similar to the way that I can view my mask as a 3D object in the first picture.
If I understand well, you want to plot your field on the surface of your mask.
To do so, get the patch returned by isosurface, then intrepolate your field on vertices of this patch. Finaly, plot it using the resulting interpolated data as 'FaceVertexCData'.
Here is an example with dummy data :
% PREPARATION
% ===========
% GENERATE A GRID
[X Y Z] = meshgrid([0:1/100:1],[0:1/100:1],[0:1/100:1]);
% GENERATE RANDOM DATA
DATA = zeros(101,101,101);
DATA(:) = interp3([0:1/10:1],[0:1/10:1],[0:1/10:1],rand(11,11,11),X(:),Y(:),Z(:),'cubic');
% GENERATE A RANDOM MASK
MASK = zeros(101,101,101);
MASK(:) = sqrt(sum([X(:)-0.5 Y(:)-0.5 Z(:)-0.5].^2,2)) - 0.3 - interp3([0:1/10:1],[0:1/10:1],[0:1/10:1],0.1*rand(11,11,11),X(:),Y(:),Z(:),'cubic');
%
% ACTUAL PROBLEM
% ==============
% EXTRACT THE MASK SURFACE
SURF = isosurface(X,Y,Z,MASK,0);
% INTERPOLATE DATA ON MASK SURFACE
DATA_SURF = interp3(X,Y,Z,DATA,SURF.vertices(:,1),SURF.vertices(:,2),SURF.vertices(:,3));
% PLOT THE MASK SURFACE AND DATA
hold on; axis square; axis([0 1 0 1 0 1]); view(3); camlight
patch('Faces',SURF.faces,'Vertices',SURF.vertices,'EdgeColor','none','FaceColor','interp','FaceVertexCData',DATA_SURF);
This gives things like that :
Try using isosurface(mask,th) where th is the value where the isosurface will be created. As I dont know the magnitude of your data I can not suggest you a value. Try different values, e.g. 0.01

How to plot a surface with a texture map

I want to plot a surface with a texture map on it, but the conditions are not the "ideal" ones.
first lets explain what I have.
I have a set of points (~7000) that are image coordinates, in a grid. This points do NOT define perfect squares. IT IS NOT A MESHGRID. For the sake of the question, lets assume that we have 9 points. Lets ilustrate what we have with an image:
X=[310,270,330,430,410,400,480,500,520]
Y=[300,400,500,300,400,500,300,400,500]
Lets say we can get the "structure" of the grid, so
size1=3;
size2=3;
points=zeros(size1,size2,2)
X=[310,270,330;
430,410,400;
480,500,520]
Y=[300,400,500;
300,400,500;
300,400,500]
points(:,:,1)=X;
points(:,:,2)=Y;
And now lets say we have a 3rd dimension, Z.
EDIT: Forgot to add a piece if info. I triangulate the points in the image and get a 3D correspondence, so when displayed in a surface they don't have the X and Y coords of the image, for a simplification of the given data lets say X=X/2 Y=Y/3
And we have:
points=zeros(size1,size2,3)
Z=[300,330,340;
300,310,330;
290,300,300]
surf(points(:,:,1)/2,points(:,:,2)/3,points(:,:,3))
What I want is to plot the surface in 3D with the image texture. Each element should have the texture piece that have in the first image.
This needs to work for huge datasheets. I don't specially need it to be fast.
related post (but I has a meshgrid as initial set of points) : Texture map for a 2D grid
PD: I can post original images + real data if needed, just posted this because i think it is easier with small data.
You can use the texturemap property of surf which works with rectangular meshes as well as with non-rectangular ones.
Creating non-rectangular data points
% creating non-rectangular data points
[X, Y] = meshgrid(1:100, 1:100);
X = X+rand(size(X))*5;
Y = Y+rand(size(X))*5;
which results in the following data points:
Generating height data:
Z = sin(X/max(X(:))*2*pi).*sin(Y/max(Y(:))*2*pi);
Loading picture:
[imageTest]=imread('peppers.png');
and mapping it as texture to the mesh:
surf(X,Y,Z, imageTest, ...
'edgecolor', 'none','FaceColor','texturemap')
Note that, for the sake of demonstration, this non-rectangular grid is quite sparsely populated which results in a rather jagged texture. With more points, the result gets much better, irrespective of the distortion of the grid points.
Note also that the number of grid points does not have to match the number of pixels in the texture image.
~edit~
If X and Y coordinates are only available for parts of the image, you can adjust the texture accordingly by
minX = round(min(X(:)));
maxX = round(max(X(:)));
minY = round(min(Y(:)));
maxY = round(max(Y(:)));
surf(X,Y,Z, imageTest(minX:maxX, minY:maxY, :), ...
'edgecolor', 'none','FaceColor','texturemap')
I don't think you can do what you want with Matlab's built in commands and features. But using the technique from my other answer with a high-res version of the grid can do it for you.
By "high-res", I mean an interpolated version of the non-uniform grid with denser data points. That is used to sample the texture at denser data points so it can be drawn using the texturemap feature of surf. You can't use a normal 2D interpolation, however, because you need to preserve the non-uniform grid shape. This is what I came up with:
function g = nonUniformGridInterp2(g, sx, sy)
[a,b] = size(g);
g = interp1(linspace(0,1,a), g, linspace(0,1,sy)); % interp columns
g = interp1(linspace(0,1,b), g', linspace(0,1,sx))'; % interp rows
Note that you have to call this twice to interpolate the X and Y points independently. Here's an example of the original grid and an interpolated version with 10 points in each direction.
Here's how to use that high-res grid with interp2 and texturemap.
function nonUniformTextureMap
% define the non-uniform surface grid
X = [310,270,330; 430,410,400; 480,500,520];
Y = [300,400,500; 300,400,500; 300,400,500];
Z = [300,330,340; 300,310,330; 290,300,300];
% get texture data
load penny % loads data in variable P
% define texture grid based on image size
% note: using 250-550 so that a,b covers the range used by X,Y
[m,n] = size(P);
[a,b] = meshgrid(linspace(250,550,n), linspace(250,550,m));
% get a high-res version of the non-uniform grid
s = 200; % number of samples in each direction
X2 = nonUniformGridInterp2(X, s, s);
Y2 = nonUniformGridInterp2(Y, s, s);
% sample (map) the texture on the non-uniform grid
C = interp2(a, b, P, X2, Y2);
% plot the original and high-res grid
figure
plot(X(:),Y(:),'o',X2(:),Y2(:),'.')
legend('original','high-res')
% plot the surface using sampled points for color
figure
surf(X, Y, Z, C, 'edgecolor', 'none', 'FaceColor','texturemap')
colormap gray
I'm not sure I understand your question, but I think that what you need to do is sample (map) the texture at your grid's X,Y points. Then you can simply plot the surface and use those samples as colors.
Here's an example using the data you gave in your question. It doesn't look like much, but using more X,Y,Z points should give the result you're after.
% define the non-uniform surface grid
X = [310,270,330; 430,410,400; 480,500,520];
Y = [300,400,500; 300,400,500; 300,400,500];
Z = [300,330,340; 300,310,330; 290,300,300];
% get texture data
load penny % loads data in variable P
% define texture grid based on image size
% note: using 600 so that a,b covers the range used by X,Y
[m,n] = size(P);
[a,b] = meshgrid(linspace(0,600,n), linspace(0,600,m));
% sample (map) the texture on the non-uniform grid
C = interp2(a, b, P, X, Y);
% plot the surface using sampled points for color
figure
surf(X, Y, Z, C)
colormap gray

Representing three variables in a three dimension plot

I have a problem dealing with 3rd dimension plot for three variables.
I have three matrices: Temperature, Humidity and Power. During one year, at every hour, each one of the above were measured. So, we have for each matrix 365*24 = 8760 points. Then, one average point is taken every day. So,
Tavg = 365 X 1
Havg = 365 X 1
Pavg = 365 X 1
In electrical point of veiw, the power depends on the temperature and humidity. I want to discover this relation using a three dimensional plot.
I tried using mesh, meshz, surf, plot3, and many other commands in MATLAB but unfortunately I couldn't get what I want. For example, let us take first 10 days. Here, every day is represented by average temperature, average humidity and average power.
Tavg = [18.6275
17.7386
15.4330
15.4404
16.4487
17.4735
19.4582
20.6670
19.8246
16.4810];
Havg = [75.7105
65.0892
40.7025
45.5119
47.9225
62.8814
48.1127
62.1248
73.0119
60.4168];
Pavg = [13.0921
13.7083
13.4703
13.7500
13.7023
10.6311
13.5000
12.6250
13.7083
12.9286];
How do I represent these matrices by three dimension plot?
The challenge is that the 3-D surface plotting functions (mesh, surf, etc.) are looking for a 2-D matrix of z values. So to use them you need to construct such a matrix from the data.
Currently the data is sea of points in 3-D space, so, you have to map these points to a surface. A simple approach to this is to divide up the X-Y (temperature-humidity) plane into bins and then take the average of all of the Z (power) data. Here is some sample code for this that uses accumarray() to compute the averages for each bin:
% Specify bin sizes
Tbin = 3;
Hbin = 20;
% Create binned average array
% First create a two column array of bin indexes to use as subscripts
subs = [round(Havg/Hbin)+1, round(Tavg/Tbin)+1];
% Now create the Z (power) estimate as the average value in each bin
Pest = accumarray(subs,Pavg,[],#mean);
% And the corresponding X (temp) & Y (humidity) vectors
Tval = Tbin/2:Tbin:size(Pest,2)*Tbin;
Hval = Hbin/2:Hbin:size(Pest,1)*Hbin;
% And create the plot
figure(1)
surf(Tval, Hval, Pest)
xlabel('Temperature')
ylabel('Humidity')
zlabel('Power')
title('Simple binned average')
xlim([14 24])
ylim([40 80])
The graph is a bit coarse (can't post image yet, since I am new) because we only have a few data points. We can enhance the visualization by removing any empty bins by setting their value to NaN. Also the binning approach hides any variation in the Z (power) data so we can also overlay the orgional point cloud using plot3 without drawing connecting lines. (Again no image b/c I am new)
Additional code for the final plot:
%% Expanded Plot
% Remove zeros (useful with enough valid data)
%Pest(Pest == 0) = NaN;
% First the original points
figure(2)
plot3(Tavg, Havg, Pavg, '.')
hold on
% And now our estimate
% The use of 'FaceColor' 'Interp' uses colors that "bleed" down the face
% rather than only coloring the faces away from the origin
surfc(Tval, Hval, Pest, 'FaceColor', 'Interp')
% Make this plot semi-transparent to see the original dots anb back side
alpha(0.5)
xlabel('Temperature')
ylabel('Humidity')
zlabel('Power')
grid on
title('Nicer binned average')
xlim([14 24])
ylim([40 80])
I think you're asking for a surface fit for your data. The Curve Fitting Toolbox handles this nicely:
% Fit model to data.
ft = fittype( 'poly11' );
fitresult = fit( [Tavg, Havg], Pavg, ft);
% Plot fit with data.
plot( fitresult, [xData, yData], zData );
legend( 'fit 1', 'Pavg vs. Tavg, Havg', 'Location', 'NorthEast' );
xlabel( 'Tavg' );
ylabel( 'Havg' );
zlabel( 'Pavg' );
grid on
If you don't have the Curve Fitting Toolbox, you can use the backslash operator:
% Find the coefficients.
const = ones(size(Tavg));
coeff = [Tavg Havg const] \ Pavg;
% Plot the original data points
clf
plot3(Tavg,Havg,Pavg,'r.','MarkerSize',20);
hold on
% Plot the surface.
[xx, yy] = meshgrid( ...
linspace(min(Tavg),max(Tavg)) , ...
linspace(min(Havg),max(Havg)) );
zz = coeff(1) * xx + coeff(2) * yy + coeff(3);
surf(xx,yy,zz)
title(sprintf('z=(%f)*x+(%f)*y+(%f)',coeff))
grid on
axis tight
Both of these fit a linear polynomial surface, i.e. a plane, but you'll probably want to use something more complicated. Both of these techniques can be adapted to this situation. There's more information on this subject at mathworks.com: How can I determine the equation of the best-fit line, plane, or N-D surface using MATLAB?.
You might want to look at Delaunay triangulation:
tri = delaunay(Tavg, Havg);
trisurf(tri, Tavg, Havg, Pavg);
Using your example data, this code generates an interesting 'surface'. But I believe this is another way of doing what you want.
You might also try the GridFit tool by John D'Errico from MATLAB Central. This tool produces a surface similar to interpolating between the data points (as is done by MATLAB's griddata) but with cleaner results because it smooths the resulting surface. Conceptually multiple datapoints for nearby or overlapping X,Y coordinates are averaged to produce a smooth result rather than noisy "ripples." The tool also allows for some extrapolation beyond the data points. Here is a code example (assuming the GridFit Tool has already been installed):
%Establish points for surface
num_points = 20;
Tval = linspace(min(Tavg),max(Tavg),num_points);
Hval = linspace(min(Havg),max(Havg),num_points);
%Do the fancy fitting with smoothing
Pest = gridfit(Tavg, Havg, Pavg, Tval, Hval);
%Plot results
figure(5)
surfc(XI,YI,Pest, 'FaceColor', 'Interp')
To produce an even nicer plot, you can add labels, some transparancy and overlay the original points:
alpha(0.5)
hold on
plot3(Tavg,Havg,Pavg,'.')
xlabel('Temperature')
ylabel('Humidity')
zlabel('Power')
grid on
title('GridFit')
PS: #upperBound: Thanks for the Delaunay triangulation tip. That seems like the way to go if you want to go through each of the points. I am a newbie so can't comment yet.
Below is your solution:
Save/write the Myplot3D function
function [x,y,V]=Myplot3D(X,Y,Z)
x=linspace(X(1),X(end),100);
y=linspace(Y(1),Y(end),100);
[Xt,Yt]=meshgrid(x,y);
V=griddata(X,Y,Z,Xt,Yt);
Call the following from your command line (or script)
[Tavg_new,Pavg_new,V]=Myplot3D(Tavg,Pavg,Havg);
surf(Tavg_new,Pavg_new,V)
colormap jet;
xlabel('Temperature')
ylabel('Power/Pressure')
zlabel('Humidity')