How to superimpose two contour maps onto each other in matlab? - matlab

I have two contour maps in Matlab and each of the two maps has a single curve specifying a single Z-value. I want to super impose the two contour maps so that I can find the single solution where the two z-value curves intersect. How could I go about super imposing the two contour maps?
% the two contour maps are coded the exact same way, but with different z-values
x = 0.05:0.05:1;
y = 0.0:0.05:1;
[X, Y] = meshgrid(x, y);
% Z-value data is copied from excel and pasted into an array
Z = [data]
contourf(X, Y, Z);
pcolor(X, Y, Z); hold on
shading interp
title();
xlabel();
ylabel();
colorbar
val = %z-value to plot onto colormap
tol = %tolerance
idxZval = (Z <= val+tol) & (Z >= val-tol);
plot(X(idxZval), Y(idxZval))[enter image description here][1]

The end result you seek is possible using contourc or using contour specifying the same contours (isolines).
This answer extends this answer in Approach 1 using contourc and provides a simple solution with contour in Approach 2.
You might ask "Why Approach 1 when Approach 2 is so simple?"
Approach 1 provides a way to directly access the individual isolines in the event you require a numerical approach to searching for intersections.
Approach 1
Example Data:
% MATLAB R2018b
x = 0:0.01:1;
y = 0:0.01:1;
[X,Y] = meshgrid(x,y);
Z = sqrt(X.^3+Y); % Placeholder 1
W = sqrt(X.*Y + X.^2 + Y.^(2/3)); % Placeholder 2
Overlay Single Isoline from 2 Contour Plots
Mimicking this answer and using
v = [.5 0.75 .85 1]; % Values of Z to plot isolines
we can visualize these two functions, Z, and W, respectively.
We can overlay the isolines since they share the same (x,y) domain. For example, they both equal 0.8 as displayed below.
val = 0.8; % Isoline value to plot (for Z & W)
Ck = contourc(x,y,Z,[val val]);
Ck2 = contourc(x,y,W,[val val]);
figure, hold on, box on
plot(Ck(1,2:end),Ck(2,2:end),'k-','LineWidth',2,'DisplayName',['Z = ' num2str(val)])
plot(Ck2(1,2:end),Ck2(2,2:end),'b-','LineWidth',2,'DisplayName',['W = ' num2str(val)])
legend('show')
Overlay Multiple Isolines from 2 Contour Plots
We can also do this for more isolines at a time.
v = [1 0.5]; % Isoline values to plot (for Z & W)
figure, hold on, box on
for k = 1:length(v)
Ck = contourc(x,y,Z,[v(k) v(k)]);
Ck2 = contourc(x,y,W,[v(k) v(k)]);
p(k) = plot(Ck(1,2:end),Ck(2,2:end),'k-','LineWidth',2,'DisplayName',['Z = ' num2str(v(k))]);
p2(k) = plot(Ck2(1,2:end),Ck2(2,2:end),'b-','LineWidth',2,'DisplayName',['W = ' num2str(v(k))]);
end
p(2).LineStyle = '--';
p2(2).LineStyle = '--';
legend('show')
Approach 2
Without making it pretty...
% Single Isoline
val = 1.2;
contour(X,Y,Z,val), hold on
contour(X,Y,W,val)
% Multiple Isolines
v = [.5 0.75 .85 1];
contour(X,Y,Z,v), hold on
contour(X,Y,W,v)
It is straightforward to clean these up for presentation. If val is a scalar (single number), then c1 = contour(X,Y,Z,val); and c2 = contour(X,Y,W,val) gives access to the isoline for each contour plot.

Related

Extracting Z values after 3D plot is generated by 2D curve revolution with repmat

I am stuck with an apparently simple problem. I have to revolve of 360° a 2D curve around an axis, to obtain a 3D plot. Say, I want to do it with this sine function:
z = sin(r);
theta = 0:pi/20:2*pi;
xx = bsxfun(#times,r',cos(theta));
yy = bsxfun(#times,r',sin(theta));
zz = repmat(z',1,length(theta));
surf(xx,yy,zz)
axis equal
I now want to visualize the numerical values of the Z plane, stored in a matrix. I would normally do it this way:
ch=get(gca,'children')
X=get(ch,'Xdata')
Y=get(ch,'Ydata')
Z=get(ch,'Zdata')
If I visualize Z with
imagesc(Z)
I don't obtain the actual values of Z of the plot, but the "un-revolved" projection. I suspect that this is related to the way I generate the curve, and from the fact I don't have a function of the type
zz = f(xx,yy)
Is there any way I can obtain the grid values of xx and yy, as well as the values of zz at each gridpoint?
Thank you for your help.
Instead of bsxfun you can use meshgrid:
% The two parameters needed for the parametric equation
h = linspace(0,2) ;
th = 0:pi/20:2*pi ;
[R,T] = meshgrid(h,th) ;
% The parametric equation
% f(x) Rotation along Z
% ↓ ↓
X = sin(R) .* cos(T) ;
Y = sin(R) .* sin(T) ;
% Z = h
Z = R ;
surf(X,Y,Z,'EdgeColor',"none")
xlabel('X')
ylabel('Y')
zlabel('Z')
Which produce:
And if you want to extract the contour on the X plane (X = 0) you can use contour:
contour(Y,Z,X,[0,0])
Which produce:

Adding two color maps using imagesc between two sets of curves with specified boundaries (MATLAB)

I'm trying to add two color gradients between two curves (in this example these are lines).
This is the code for what I've done so far
% the mesh
ns=1000;
t_vec = linspace(0,100,ns);
x_vec = linspace(-120,120,ns);
[N, X] = meshgrid(t_vec, x_vec);
% the curves
x1 = linspace(0,100,ns); x2 = linspace(10,110,ns);
y1 = linspace(-50,50,ns); y2 = linspace(-20,80,ns);
X1 = repmat(x1, [size(N, 1) 1]); X2 = repmat(x2, [size(N, 1) 1]);
Y1 = repmat(y1, [size(N, 1) 1]); Y2 = repmat(y2, [size(N, 1) 1]);
% the gradient function
cc = #(x,x2,x1) ...
1./(1+(exp(-x)./(exp(-x1)-exp(-x2))));
for i=1:ns
CData1(:,i)=cc(x_vec,x2(i),x1(i));
CData2(:,i)=cc(x_vec,y2(i),y1(i));
end
CData=CData1+CData2; % here I've added the two gradients
% mask
mask = true(size(N));
mask((X > Y2 | X < Y1) & (X > X2 | X < X1)) = false;
% finalized data
Z = NaN(size(N));
Z(mask) = CData(mask);
Z = normalize(Z, 1, 'range');
% draw a figure!
figure(1); clf;
ax = axes; % create some axes
sc = imagesc(ax, t_vec, x_vec, Z); % plot the data
colormap('summer')
ax.YDir = 'normal' % set the YDir to normal again, imagesc reverses it by default;
hold on
plot(t_vec,x1,'r',t_vec,x2,'r',t_vec,y1,'k',t_vec,y2,'k')
ylim([-120 120]); xlim([0 100])
the result I get is
As you can see, the gradient stretches between the most lower line to the most upper line.
How can I separate between the two color data and present them in the same image (using imagesc) using a different colormap?
Here is a function called comat (see at the bottom of the answer) that I once made for something similar, I think you might find it useful in your case. Here's an example how to use it:
imagesc(t_vec, x_vec, comat(CData2.*mask,CData1.*mask));
colormap([summer(256).^2;flipud(bone(256).^0.5)]); % and the two colormaps
set(gca,'Ydir','normal')
The result is:
I'm not sure this is what you meant, but you can see how the data of the thin stripe is only visualized using the bone b&w colormap, while the rest is with summer. I also "abused" the colormaps with a ^ factor for emphasizing the range of the gradient.
function z = comat(z1,z2,DR)
% the function combines matrices z1 and z2 for the purpose of
% visualization with 2 different colormaps
% z1,z2 - matrices of the same size
% DR - the dynamic range for visualization (default 256)
%example
%imagesc(comat(z1,z2)); colormap([jet(256);bone(256)]);
%defaults
if (nargin < 3); DR=256; end
%normalize to dynamic range, integer values in the range 0 to DR
z1=double(uint32(DR*(z1-min(z1(:)))./(max(z1(:)-min(z1(:))))));
z2=double(uint32(DR*(z2-min(z2(:)))./(max(z2(:)-min(z2(:))))+DR+1));
thr=DR+2+10; %threshold where data is not important for z2, must be at least DR+2
z=z1.*(z2<thr)+z2.*(z2>thr);
end

Matlab: patch area between two curves which depend on the curves values

I'm trying to fill an area between two curves with respect to a function which depends on the values of the curves.
Here is the code of what I've managed to do so far
i=50;
cc = #(xx,x,y) 1./(1+(exp(-xx)/(exp(-x)-exp(-y))));
n_vec = 2:0.1:10;
x_vec = linspace(2,10,length(n_vec));
y_vec = abs(sin(n_vec));
N=[n_vec,fliplr(n_vec)];
X=[x_vec,fliplr(y_vec)];
figure(1)
subplot(2,1,1)
hold on
plot(n_vec,x_vec,n_vec,y_vec)
hp = patch(N,X,'b')
plot([n_vec(i) n_vec(i)],[x_vec(i),y_vec(i)],'linewidth',5)
xlabel('n'); ylabel('x')
subplot(2,1,2)
xx = linspace(y_vec(i),x_vec(i),100);
plot(xx,cc(xx,y_vec(i),x_vec(i)))
xlabel('x'); ylabel('c(x)')
This code produces the following graph
The color code which I've added represent the color coding that each line (along the y axis at a point on the x axis) from the area between the two curves should be.
Overall, the entire area should be filled with a gradient color which depends on the values of the curves.
I've assisted the following previous questions but could not resolve a solution
MATLAB fill area between lines
Patch circle by a color gradient
Filling between two curves, according to a colormap given by a function MATLAB
NOTE: there is no importance to the functional form of the curves, I would prefer an answer which refers to two general arrays which consist the curves.
The surf plot method
The same as the scatter plot method, i.e. generate a point grid.
y = [x_vec(:); y_vec(:)];
resolution = [500,500];
px = linspace(min(n_vec), max(n_vec), resolution(1));
py = linspace(min(y), max(y), resolution(2));
[px, py] = meshgrid(px, py);
Generate a logical array indicating whether the points are inside the polygon, but no need to extract the points:
in = inpolygon(px, py, N, X);
Generate Z. The value of Z indicates the color to use for the surface plot. Hence, it is generated using the your function cc.
pz = 1./(1+(exp(-py_)/(exp(-y_vec(i))-exp(-x_vec(i)))));
pz = repmat(pz',1,resolution(2));
Set Z values for points outside the area of interest to NaN so MATLAB won't plot them.
pz(~in) = nan;
Generate a bounded colourmap (delete if you want to use full colour range)
% generate colormap
c = jet(100);
[s,l] = bounds(pz,'all');
s = round(s*100);
l = round(l*100);
if s ~= 0
c(1:s,:) = [];
end
if l ~= 100
c(l:100,:) = [];
end
Finally, plot.
figure;
colormap(jet)
surf(px,py,pz,'edgecolor','none');
view(2) % x-y view
Feel free to turn the image arround to see how it looks like in the Z-dimention - beautiful :)
Full code to test:
i=50;
cc = #(xx,x,y) 1./(1+(exp(-xx)/(exp(-x)-exp(-y))));
n_vec = 2:0.1:10;
x_vec = linspace(2,10,length(n_vec));
y_vec = abs(sin(n_vec));
% generate grid
y = [x_vec(:); y_vec(:)];
resolution = [500,500];
px_ = linspace(min(n_vec), max(n_vec), resolution(1));
py_ = linspace(min(y), max(y), resolution(2));
[px, py] = meshgrid(px_, py_);
% extract points
in = inpolygon(px, py, N, X);
% generate z
pz = 1./(1+(exp(-py_)/(exp(-y_vec(i))-exp(-x_vec(i)))));
pz = repmat(pz',1,resolution(2));
pz(~in) = nan;
% generate colormap
c = jet(100);
[s,l] = bounds(pz,'all');
s = round(s*100);
l = round(l*100);
if s ~= 0
c(1:s,:) = [];
end
if l ~= 100
c(l:100,:) = [];
end
% plot
figure;
colormap(c)
surf(px,py,pz,'edgecolor','none');
view(2)
You can use imagesc and meshgrids. See comments in the code to understand what's going on.
Downsample your data
% your initial upper and lower boundaries
n_vec_long = linspace(2,10,1000000);
f_ub_vec_long = linspace(2, 10, length(n_vec_long));
f_lb_vec_long = abs(sin(n_vec_long));
% downsample
n_vec = linspace(n_vec_long(1), n_vec_long(end), 1000); % for example, only 1000 points
% get upper and lower boundary values for n_vec
f_ub_vec = interp1(n_vec_long, f_ub_vec_long, n_vec);
f_lb_vec = interp1(n_vec_long, f_lb_vec_long, n_vec);
% x_vec for the color function
x_vec = 0:0.01:10;
Plot the data
% create a 2D matrix with N and X position
[N, X] = meshgrid(n_vec, x_vec);
% evaluate the upper and lower boundary functions at n_vec
% can be any function at n you want (not tested for crossing boundaries though...)
f_ub_vec = linspace(2, 10, length(n_vec));
f_lb_vec = abs(sin(n_vec));
% make these row vectors into matrices, to create a boolean mask
F_UB = repmat(f_ub_vec, [size(N, 1) 1]);
F_LB = repmat(f_lb_vec, [size(N, 1) 1]);
% create a mask based on the upper and lower boundary functions
mask = true(size(N));
mask(X > F_UB | X < F_LB) = false;
% create data matrix
Z = NaN(size(N));
% create function that evaluates the color profile for each defined value
% in the vectors with the lower and upper bounds
zc = #(X, ub, lb) 1 ./ (1 + (exp(-X) ./ (exp(-ub) - exp(-lb))));
CData = zc(X, f_lb_vec, f_ub_vec); % create the c(x) at all X
% put the CData in Z, but only between the lower and upper bound.
Z(mask) = CData(mask);
% normalize Z along 1st dim
Z = normalize(Z, 1, 'range'); % get all values between 0 and 1 for colorbar
% draw a figure!
figure(1); clf;
ax = axes; % create some axes
sc = imagesc(ax, n_vec, x_vec, Z); % plot the data
ax.YDir = 'normal' % set the YDir to normal again, imagesc reverses it by default;
xlabel('n')
ylabel('x')
This already looks kinda like what you want, but let's get rid of the blue area outside the boundaries. This can be done by creating an 'alpha mask', i.e. set the alpha value for all pixels outside the previously defined mask to 0:
figure(2); clf;
ax = axes; % create some axes
hold on;
sc = imagesc(ax, n_vec, x_vec, Z); % plot the data
ax.YDir = 'normal' % set the YDir to normal again, imagesc reverses it by default;
% set a colormap
colormap(flip(hsv(100)))
% set alpha for points outside mask
Calpha = ones(size(N));
Calpha(~mask) = 0;
sc.AlphaData = Calpha;
% plot the other lines
plot(n_vec, f_ub_vec, 'k', n_vec, f_lb_vec, 'k' ,'linewidth', 1)
% set axis limits
xlim([min(n_vec), max(n_vec)])
ylim([min(x_vec), max(x_vec)])
there is no importance to the functional form of the curves, I would prefer an answer which refers to two general arrays which consist the curves.
It is difficult to achieve this using patch.
However, you may use scatter plots to "fill" the area with coloured dots. Alternatively, and probably better, use surf plot and generate z coordinates using your cc function (See my seperate solution).
The scatter plot method
First, make a grid of points (resolution 500*500) inside the rectangular space bounding the two curves.
y = [x_vec(:); y_vec(:)];
resolution = [500,500];
px = linspace(min(n_vec), max(n_vec), resolution(1));
py = linspace(min(y), max(y), resolution(2));
[px, py] = meshgrid(px, py);
figure;
scatter(px(:), py(:), 1, 'r');
The not-interesting figure of the point grid:
Next, extract the points inside the polygon defined by the two curves.
in = inpolygon(px, py, N, X);
px = px(in);
py = py(in);
hold on;
scatter(px, py, 1, 'k');
Black points are inside the area:
Finally, create color and plot the nice looking gradient colour figure.
% create color for the points
cid = 1./(1+(exp(-py)/(exp(-y_vec(i))-exp(-x_vec(i)))));
c = jet(101);
c = c(round(cid*100)+1,:); % +1 to avoid zero indexing
% plot
figure;
scatter(px,py,16,c,'filled','s'); % use size 16, filled square markers.
Note that you may need a fairly dense grid of points to make sure the white background won't show up. You may also change the point size to a bigger value (won't impact performance).
Of cause, you may use patch to replace scatter but you will need to work out the vertices and face ids, then you may patch each faces separately with patch('Faces',F,'Vertices',V). Using patch this way may impact performance.
Complete code to test:
i=50;
cc = #(xx,x,y) 1./(1+(exp(-xx)/(exp(-x)-exp(-y))));
n_vec = 2:0.1:10;
x_vec = linspace(2,10,length(n_vec));
y_vec = abs(sin(n_vec));
% generate point grid
y = [x_vec(:); y_vec(:)];
resolution = [500,500];
px_ = linspace(min(n_vec), max(n_vec), resolution(1));
py_ = linspace(min(y), max(y), resolution(2));
[px, py] = meshgrid(px_, py_);
% extract points
in = inpolygon(px, py, N, X);
px = px(in);
py = py(in);
% generate color
cid = 1./(1+(exp(-py)/(exp(-y_vec(i))-exp(-x_vec(i)))));
c = jet(101);
c = c(round(cid*100)+1,:); % +1 to avoid zero indexing
% plot
figure;
scatter(px,py,16,c,'filled','s');

Edit the x limits of least squares line

I created two scatter plots and then used lsline to add regression lines for each plot. I used this code:
for i=1:2
x = ..;
y = ..;
scatter(x, y, 50, 'MarkerFaceColor',myColours(i, :));
end
h_lines = lsline;
However, the darker line extends far beyond the last data point in that scatter plot (which is at around x=0.3):
lsline doesn't seem to have properties that allow its horizontal range to be set. Is there a workaround to set this separately for the two lines, in Matlab 2016a?
For a single data set
This is a workaround rather than a solution. lsline internally calls refline, which plots a line filling the axis as given by their current limits (xlim and ylim). So you can change those limits to the extent you want for the line, call lsline, and then restore the limits.
Example:
x = randn(1,100);
y = 2*x + randn(1,100); % random, correlated data
plot(x, y, '.') % scatter plot
xlim([-1.5 1.5]) % desired limit for line
lsline % plot line
xlim auto % restore axis limit
For several data sets
In this case you can apply the same procedure for each data set sequentially, but you need to keep only one data set visible when you call lsline; otherwise when you call it to create the second line it will also create a new version of the first (with the wrong range).
Example:
x = randn(1,100); y = 2*x + randn(1,100); % random, correlated data
h = plot(x, y, 'b.'); % scatter plot
axis([min(x) max(x) min(y) max(y)]) % desired limit for line
lsline % plot line
xlim auto % restore axis limit
hold on
x = 2*randn(1,100) - 5; y = 1.2*x + randn(1,100) + 6; % random, correlated data
plot(x, y, 'r.') % scatter plot
axis([min(x) max(x) min(y) max(y)]) % desired limit for line
set(h, 'HandleVisibility', 'off'); % hide previous plot
lsline % plot line
set(h, 'HandleVisibility', 'on'); % restore visibility
xlim auto % restore axis limit
Yet another solution: implement your own hsline. It's easy!
In MATLAB, doing a least squares fit of a straight line is trivial. Given column vectors x and y with N elements, b = [ones(N,1),x] \ y; are the parameters to the best fit line. [1,x1;1,x2]*b are the y locations of two points along the line with x-coordinates x1 and x2. Thus you can write (following Luis' example, and getting the exact same output):
N = 100;
x = randn(N,1); y = 2*x + randn(N,1); % random, correlated data
h = plot(x, y, 'b.'); % scatter plot
hold on
b = [ones(N,1),x] \ y;
x = [min(x);max(x)];
plot(x,[ones(2,1),x] * b, 'b-')
x = 2*randn(N,1) - 5; y = 1.2*x + randn(N,1) + 6; % random, correlated data
plot(x, y, 'r.') % scatter plot
b = [ones(N,1),x] \ y;
x = [min(x);max(x)];
plot(x,[ones(2,1),x] * b, 'r-')
You can get the points that define the line using
h_lines =lsline;
h_lines(ii).XData and h_lines(ii).YData will contain 2 points that define the lines for each ii=1,2 line. Use those to create en equation of a line, and plot the line in the range you want.

ContourPlot and inequalitie plot together

Im new to matlab and I want to plot a ContourPlot together with two inequalities but I dont know how. The function that I want to plot its ContourPlot is something like this:
Z = (X-2).^2 + (Y-1).^2;
and here are the two inequalities:
ineq1 = X.^2 - Y <= 2;
ineq2 = X + Y <= 2;
this is what I have dodne so far:
[X,Y] = meshgrid(-4:.2:4,-4:.2:4);
Z = (X-2).^2 + (Y-1).^2;
[C,h] = contour(X,Y,Z);
clabel(C,h)
ineq1 = X.^2 - Y <= 2;
ineq2 = X + Y <= 2;
range = (-4:.2:4);
hold on
plot(range,ineq1, range, ineq2)
hold off
but this does not feel right.
What I want to do is to visualize an optimization problem. First I want to plot the ContourPlot of the function and then the possible areas in that same plot, It would be great if I could show the intersection areas too.
If you want to draw the boundaries of the inequalities onto the contour plot, you can do this with line.
[X,Y] = meshgrid(-4:.2:4,-4:.2:4);
Z = (X-2).^2 + (Y-1).^2;
[C,h] = contour(X,Y,Z);
clabel(C,h)
x_vect = (-4:.05:4);
y_ineq1 = x_vect.^2 - 2;
y_ineq2 = 2 - x_vect;
line(x_vect, y_ineq1);
line(x_vect, y_ineq2);
Coloring the intersection area is a bit more tricky, and I'm not sure if it's exactly what you want to do, but you could look into using patch, something like
% Indexes of x_vect, y_ineq1 and y_ineq2 for region where both satisfied:
region_indexes = find(y_ineq1<y_ineq2);
region_indexes_rev = region_indexes(end:-1:1);
% To plot this area need to enclose it
% (forward along y_ineq1 then back along y_ineq2)
patch([x_vect(region_indexes),x_vect(region_indexes_rev)],...
[y_ineq1(region_indexes),y_ineq2(region_indexes_rev)],...
[1 0.8 1]) % <- Color as [r g b]
% May or may not need following line depending on MATLAB version to set the yaxes
ylim([-4 4])
% Get the original plot over the top
hold on
[C,h] = contour(X,Y,Z);
clabel(C,h)
hold off