Plotting contour for irregular xy data with holes - matlab

I am stuck with this problem for the last two days and haven't found a solution so far. I have a data in the following format:
x1, y1, val1
.. .. ..
.. .. ..
xn, yn, valn
The values val1, ..., valn are the field quantities I obtain after simulation on a geometry as below.
Only the grey region is the domain of interest whereas the one in blue/dark blue is not (including the inverted L shaped blue region in the interior). Thus the x and y coordinates of the data are scattered/irregular and with large gaps due to the hole in my original geometry. Is there a way to get a filled contour plot for this data? Trying the following in Matlab gives me triangulation with triangles outside the original polygon. Also, it fills the holes which is not what I want.
x = data(:,1);
y = data(:,2);
z = data(:,3);
%
dt = delaunayTriangulation(x,y) ;
tri = dt.ConnectivityList ;
xi = dt.Points(:,1) ;
yi = dt.Points(:,2) ;
F = scatteredInterpolant(x,y,z);
zI = F(xi,yi) ;
trisurf(tri,xi,yi,zI)
Another possibility was to import the data in ParaView and do filtering as Table-to-Points--> Delaunay Triangulation 2D. But this has the same problems as Matlab. The holes are not analytical to mask the unwanted interpolated regions with NaNs by using some mathematical expression.

Paraview seems to have the solution for this. Although I did not use finite elements to solve the pde, I could generate a finite element mesh inside GMsh for my geometry with holes. I then import both my CSV data file and the GMsh mesh file (in .vtk format) in ParaView. Resampling my field data with Dataset filter with the results of the Delaunay2D as the input gives me the contour only on the original geometry.

Related

MATLAB - 3D volume fill based on inequalities

I have three variables x, y and z. I have inequalities of the form
x >= a, y>= b, z>=c, x+y>=d, y+z>=e, x+z>=f, x+y+z>=g
where a to g are positive numbers. On a 3D plot with axes x, y and z, this is an open volume. I would like to fill the open side (i.e. away from 0) shape with color and show it in a plot. What is the way to do this on MATLAB?
I attempted to use fill3 and a mesh but the result was not very good
[x,y,z] = meshgrid(0:0.01:2,0:0.01:2,0:0.01:2);
ineq = (x>=1)& (y>0.5)&(z>=0.25)&(x+y>1.25)&(y+z>0.6)&(x+z>1.1)&(x+y+z>1.6);
fill3(x(:),y(:),z(:), 'r')
box on
grid on
Using plot3 also was not very good. Is there any other way to generate a nice 3D figure on MATLAB?
Mathematica does this using RegionPlot3D. I was hoping for a similar resultant image.
First of all, be careful when using 3D meshes, the one you defined contains 8M+ points.
Assuming your shape is convex, you can use convhull and trisurf:
Not that the option 'Simplify' is set as true to reduce the number of elements accounted for in the convex hull.
[x,y,z] = meshgrid(0:0.1:2,0:0.1:2,0:0.1:2);
ineq = (x>=1)& (y>0.5)&(z>=0.25)&(x+y>1.25)&(y+z>0.6)&(x+z>1.1)&(x+y+z>1.6);
figure;
x_ineq = x(ineq);
y_ineq = y(ineq);
z_ineq = z(ineq);
id_cvhl = convhull(x_ineq,y_ineq,z_ineq,'Simplify',true);
trisurf(id_cvhl,x_ineq,y_ineq,z_ineq,'FaceColor','cyan','edgecolor','none')
xlim([0 2])
ylim([0 2])
zlim([0 2])
In case you want the result to look a bit more than RegionPlot3D, don't use Simplify, and plot the edges (Be careful not too have a mesh with too many points!).
id_cvhl = convhull(x_ineq,y_ineq,z_ineq);
trisurf(id_cvhl,x_ineq,y_ineq,z_ineq,'Facecolor','yellow')

How to create a smoother heatmap

I'd like to create a heat map to analyze the porosity of some specimens that I have 3D-printed. the X-Y coordinates are fixed since they are the positions in which the specimens are printed on the platform.
Heatmap:
Tbl = readtable('Data/heatmap/above.csv');
X = Tbl(:,1);
Y = Tbl(:,2);
porosity = Tbl(:,3);
hmap_above = heatmap(Tbl, 'X', 'Y', 'ColorVariable', 'porosity');
The first question is: how can I sort the Y-axis of the plot? since it goes from the lower value (top) to the higher value (bottom) and I need it the other way around.
The second question is: I only have around 22 data points and most of the chart is without color, so I'd like to get a smoother heatmap without the black parts.
The data set is quite simple and is shown below:
X
Y
porosity
74.4615
118.3773
0.039172163
84.8570
69.4699
0.046314637
95.2526
20.5625
0.041855213
105.6482
-28.3449
0.049796110
116.0438
-77.2522
0.045010692
25.5541
107.9817
0.038562053
35.9497
59.0743
0.041553065
46.3453
10.1669
0.036152061
56.7408
-38.7404
0.060719664
67.1364
-87.6478
0.037756115
-23.3533
97.5861
0.052840845
-12.9577
48.6787
0.045216851
-2.5621
-0.2286
0.033645353
7.8335
-49.1360
0.030670865
18.2290
-98.0434
0.024952472
-72.2607
87.1905
0.036199237
-61.8651
38.2831
0.026725885
-51.4695
-10.6242
0.029212058
-41.0739
-59.5316
0.028572611
-30.6783
-108.4390
0.036796151
-121.1681
76.7949
0.031688096
-110.7725
27.8876
0.034619855
-100.3769
-21.0198
0.039070101
-89.9813
-69.9272
NaN
-79.5857
-118.8346
NaN
If you want to assign color to the "black parts" you will have to interpolate the porosity over a finer grid than you currently have.
The best tool for 2D interpolation over a uniformly sampled grid is griddata
First you have to define the X-Y grid you want to interpolate over, and choose a suitable mesh density.
% this will be the number of points over each side of the grid
gridres = 100 ;
% create a uniform vector on X, from min to max value, made of "gridres" points
xs = linspace(min(X),max(X),gridres) ;
% create a uniform vector on Y, from min to max value, made of "gridres" points
ys = linspace(min(Y),max(Y),gridres) ;
% generate 2D grid coordinates from xs and ys
[xq,yq]=meshgrid(xs,ys) ;
% now interpolate the pososity over the new grid
InterpolatedPorosity = griddata(X,Y,porosity,xq,yq) ;
% Reverse the Y axis (flip the `yq` matrix upside down)
yq = flipud(yq) ;
Now my version of matlab does not have the heatmap function, so I'll just use pcolor for display.
% now display
hmap_above = pcolor(xq,yq,InterpolatedPorosity);
hmap_above.EdgeColor = [.5 .5 .5] ; % cosmetic adjustment
colorbar
colormap jet
title(['Gridres = ' num2str(gridres)])
And here are the results with different grid resolutions (the value of the gridres variable at the beginning):
Now you could also ask MATLAB to further graphically smooth the domain by calling:
shading interp
Which in the 2 cases above would yield:
Notes: As you can see on the gridres=100, you original data are so scattered that at some point interpolating on a denser grid is not going to produce any meaningful improvment. No need to go overkill on your mesh density if you do not have enough data to start with.
Also, the pcolor function uses the matrix input in the opposite way than heatmap. If you use heatmap, you have to flip the Y matrix upside down as shown in the code. But if you end up using pcolor, then you don't need to flip the Y matrix.
The fact that I did it in the code (to show you how to do) made the result display in the wrong orientation for a display with pcolor. Simply comment the yq = flipud(yq) ; statement if you stick with pcolor.
Additionally, if you want to be able to follow the isolevels generated by the interpolation, you can use contour to add a layer of information:
Right after the code above, the lines:
hold on
contour(xq,yq,InterpolatedPorosity,20,'LineColor','k')
will yield:

Matlab- Given matrix X with xi samples, y binary column vector, and a vector w plot all these into 3d graph

I have started to learn Machine Learning, and programming in matlab.
I want to plot a matrix sized m*d where d=3 and m are the number of points.
with y binary vector I'd like to color each point with blue/red.
and plot a plane which is described with the vertical vector to it w.
The problem I trying to solve is to give some kind of visual representation of the data and the linear predictor.
All I know is how to single points with plot3, but no any number of points.
Thanks.
Plot the points using scatter3()
scatter3(X(y,1),X(y,2),X(y,3),'filled','fillcolor','red');
hold on;
scatter3(X(~y,1),X(~y,2),X(~y,3),'filled','fillcolor','blue');
or using plot3()
plot(X(y,1),X(y,2),X(y,3),' o','MarkerEdgeColor','red','MarkerFaceColor','red');
hold on;
plot(X(~y,1),X(~y,2),X(~y,3),' o','MarkerEdgeColor','blue','MarkerFaceColor','blue');
There are a few ways to plot a plane. As long as w(3) isn't very close to 0 then the following will work okay. I'm assuming your plane is defined by x'*w+b=0 where b is a scalar and w and x are column vectors.
x1min = min(X(:,1)); x2min = min(X(:,2));
x1max = max(X(:,1)); x2max = max(X(:,2));
[x1,x2] = meshgrid(linspace(x1min,x1max,20), linspace(x2min, x2max, 20));
x3 = -(w(1)*x1 + w(2)*x2 + b)/w(3);
surf(x1,x2,x3,'FaceColor',[0.6,0.6,0.6],'FaceAlpha',0.7,'EdgeColor',[0.4,0.4,0.4],'EdgeAlpha',0.4);
xlabel('x_1'); ylabel('x_2'); zlabel('x_3'); axis('vis3d');
Resulting plot

multiple matlab contour plots with one level

I have a number of 2d probability mass functions from 2 categories. I am trying to plot the contours to visualise them (for example at their half height, but doesn't really matter).
I don't want to use contourf to plot directly because I want to control the fill colour and opacity. So I am using contourc to generate xy coordinates, and am then using fill with these xy coordinates.
The problem is that the xy coordinates from the contourc function have strange numbers in them which cause the following strange vertices to be plotted.
At first I thought it was the odd contourmatrix format, but I don't think it is this as I am only asking for one value from contourc. For example...
contourmatrix = contourc(x, y, Z, [val, val]);
h = fill(contourmatrix(1,:), contourmatrix(2,:), 'r');
Does anyone know why the contourmatrix has these odd values in them when I am only asking for one contour?
UPDATE:
My problem seems might be a failure mode of contourc when the input 2D matrix is not 'smooth'. My source data is a large set of (x,y) points. Then I create a 2D matrix with some hist2d function. But when this is noisy the problem is exaggerated...
But when I use a 2d kernel density function to result in a much smoother 2D function, the problem is lessened...
The full process is
a) I have a set of (x,y) points which form samples from a distribution
b) I convert this into a 2D pmf
c) create a contourmatrix using contourc
d) plot using fill
Your graphic glitches are because of the way you use the data from the ContourMatrix. Even if you specify only one isolevel, this can result in several distinct filled area. So the ContourMatrix may contain data for several shapes.
simple example:
isolevel = 2 ;
[X,Y,Z] = peaks ;
[C,h] = contourf(X,Y,Z,[isolevel,isolevel]);
Produces:
Note that even if you specified only one isolevel to be drawn, this will result in 2 patches (2 shapes). Each has its own definition but they are both embedded in the ContourMatrix, so you have to parse it if you want to extract each shape coordinates individually.
To prove the point, if I simply throw the full contour matrix to the patch function (the fill function will create patch objects anyway so I prefer to use the low level function when practical). I get the same glitch lines as you do:
xc = X(1,:) ;
yc = Y(:,1) ;
c = contourc(xc,yc,Z,[isolevel,isolevel]);
hold on
hp = patch(c(1,1:end),c(2,1:end),'r','LineWidth',2) ;
produces the same kind of glitches that you have:
Now if you properly extract each shape coordinates without including the definition column, you get the proper shapes. The example below is one way to extract and draw each shape for inspiration but they are many ways to do it differently. You can certainly compact the code a lot but here I detailed the operations for clarity.
The key is to read and understand how the ContourMatrix is build.
parsed = false ;
iShape = 1 ;
while ~parsed
%// get coordinates for each isolevel profile
level = c(1,1) ; %// current isolevel
nPoints = c(2,1) ; %// number of coordinate points for this shape
idx = 2:nPoints+1 ; %// prepare the column indices of this shape coordinates
xp = c(1,idx) ; %// retrieve shape x-values
yp = c(2,idx) ; %// retrieve shape y-values
hp(iShape) = patch(xp,yp,'y','FaceAlpha',0.5) ; %// generate path object and save handle for future shape control.
if size(c,2) > (nPoints+1)
%// There is another shape to draw
c(:,1:nPoints+1) = [] ; %// remove processed points from the contour matrix
iShape = iShape+1 ; %// increment shape counter
else
%// we are done => exit while loop
parsed = true ;
end
end
grid on
This will produce:

How to plot a 3-column matrix as a color map in MATLAB?

I have a matrix containing the temperature value for a set of GPS coordinates. So my matrix looks like this :
Longitude Latitude Value
--------- -------- -----
12.345678 23.456789 25
12.345679 23.456790 26
%should be :
% x y z
etc.
I want to convert this matrix into a human-viewable plot like a color plot (2D or 3D), how can I do this?
3D can be something like this :
or just the 2-D version of this (looking from top z-axis).
What Have I Tried
I know MATLAB has surf and mesh functions but I cannot figure out how to use them.
If I call
surf(matrix(:,1) , matrix(:,2) , matrix(:,3));
I get the error :
Error using surf (line 75)
Z must be a matrix, not a scalar or vector
Thanks in advance for any help !
P.S : It would also be great if there is a function that "fills" the gaps by interpolation (smoothing, whatever :) ). Since I have discrete data, it would be more beautiful to represent it as a continous function.
P.S 2 : I also want to use plot_google_map in the z=0 plane.
A surprisingly hard-to-find answer. But I'm lucky that somebody else has asked almost the same question here.
I'm posting the answer that worked for me :
x = matrix(:,1);
y = matrix(:,2);
z = matrix(:,3);
xi=linspace(min(x),max(x),30)
yi=linspace(min(y),max(y),30)
[XI YI]=meshgrid(xi,yi);
ZI = griddata(x,y,z,XI,YI);
contourf(XI,YI,ZI)
which prints a nice color map.
One option that avoids unnecessarily gridding your data would be to compute the Delaunay triangulation of the scattered data points and then using a command like trisurf to plot the data. Here's an example:
N=50;
x = 2*pi*rand(N,1);
y = 2*pi*rand(N,1);
z = sin(x).*sin(y);
matrix = [x y z];
tri = delaunay(matrix(:,1),matrix(:,2));
trisurf(tri,matrix(:,1),matrix(:,2),matrix(:,3))
shading interp
Suppose your matrix is nx3. Then you can create the grid as follows:
xMin=min(myMat(:,1));
xMax=max(myMat(:,1));
yMin=min(myMat(:,2));
yMax=max(myMat(:,2));
step_x=0.5; %depends on your data
[xGrid,yGrid]=meshgrid(xMin:step_x:xMax,yMin:step_y:yMax);
Now, put your data in the third column to the appropriate indices, in the new matrix say, valMat.
You can use surf now as follows:
surf(xGrid,yGrid,valMat);
If you want interpolation, you can convolve a Gaussian kernel (maybe 3x3) with valMat.