Add stippling to surface plot - matlab

I have a variable A the size 100x150 with temperature values that I've plotted using the surface command.
Now I'd like to plot another variable B (consisting of 1's and NaN's) over A such that the 1's are displayed as some sort of stippling (in black), while nothing happens for the NaN's.
The result should look something like this IPCC figure.
I was able to plot the stippling in B separately, by using Markers and setting the colormap to white:
figure
surface(longitude, latitude, B, 'edgecolor', 'none', 'marker', 'o');
colormap([1 1 1]);
but I don't know how to plot both A and B in one figure.
I've also tried other commands such as plot3 but I couldn't figure it out so far.
Any help, also just a hint as to which function to use, would be much appreciated!

You're going to want to create two surfaces, one with your image data and the other will be of the other black regions that you would like to display. To do this, you can either alter the AlphaData property of the overlay (shown below) or you can set the CData to NaN and those values should be see-through in the resulting image. I think that the AlphaData approach is more appropriate since you want the grid to be uniform regardless of the topology of the data.
I have included an example of the AlphaData approach below
load mri
im = double(squeeze(D(:,:,12)));
figure;
surface(im, 'EdgeColor', 'none');
axis image
% Create an overlay image that will display as black
[w,h] = size(im);
nWide = ceil(w / 10);
nHigh = ceil(h / 10);
checker = checkerboard(10, nWide, nHigh);
checker = checker(1:w, 1:h);
% Now plot the overlay
hold on
[xx,yy] = meshgrid(1:w, 1:h);
s = surface(xx, yy, ones(size(xx))*max(im(:)));
set(s, 'EdgeColor', 'none', ...
'FaceAlpha', 'flat', ...
'AlphaData', checker, ...
'FaceColor', 'black');
And here is the view from the side just so you can see how it was done.
Alternately, rather than using surface you could use pcolor for the overlay:
s = pcolor(checker);
set(s, 'EdgeColor', 'none', ...
'FaceAlpha', 'flat', ...
'AlphaData', checker, ...
'FaceColor', 'black)
% And set the ZData so it's above your data
set(s, 'ZData', ones(size(checker)) * max(im(:)))
The other nice thing about this approach is that it uses RGB for the color of the overlay so it's appearance is independent of the colormap used for the underlying data.

Related

Matlab: `mesh()` plot with less number of grid

Let's say data is a matrix of size 129 * 129.
by using
mesh(data, 'FaceColor', 'none', 'EdgeColor', 'black')
we get something like
We can find that the grid are quite intense. I would like to have the same figure but with less number of mesh lines, something like
It is of course doable to plot a smaller data, for example data(1:10:end, 1:10:end). But in this way, the plot are not accurate as before anymore.
Another example is plot(..., 'MarkerIndices', ...). This can give you a plot with less number of markers without modifying the plot. https://www.mathworks.com/help/matlab/creating_plots/create-line-plot-with-markers.html
An alternative approach is to use plot3 to plot the mesh lines manually. That way you can plot each line smoothly using all the data points, but not have as many lines.
[X,Y,Z] = peaks(201);
step = 5;
plot3(X(:,1:step:end),Y(:,1:step:end),Z(:,1:step:end),'k')
hold on
plot3(X(1:step:end,:).',Y(1:step:end,:).',Z(1:step:end,:).','k')
hold off
I think your best option would be to create a surf plot with no grid lines (showing a colored surface with the full resolution of your data), then overlay a down-sampled mesh plot. Something like this:
surf(data, 'EdgeColor', 'none');
hold on;
mesh(data(1:10:end, 1:10:end), 'EdgeColor', 'black');
You could also add some transparency to the surf plot to make the mesh visible through it:
surf(data, 'FaceAlpha', 0.7, 'EdgeColor', 'none');
The answer from #David is good. In addition to his approach, we can also replace plot3 with many infinitesimal mesh. The idea is to plot mesh for single vectors many times.
[X,Y,Z] = peaks(201);
tempz = NaN(201, 201);
tempz(1, :) = Z(1, :);
mesh(X, Y, tempz, 'EdgeColor', 'interp');
hold on
% plot x lines
for i = 2:10:201
tempz = NaN(201, 201);
tempz(i, :) = Z(i, :);
mesh(X, Y, tempz, 'EdgeColor', 'interp');
end
% plot y lines
for i = 2:10:201
tempz = NaN(201, 201);
tempz(:, i) = Z(:, i);
mesh(X, Y, tempz, 'EdgeColor', 'interp');
end
The original is
By using the snippet above, it gives
The benefits of this over #David's answer is that you can preserve all of the fancy properties of mesh, for example shading interp etc.

Add custom legend without any relation to the graph

I wish to insert a legend that is not related to the graph whatsoever:
figure;
hold on;
plot(0,0,'or');
plot(0,0,'ob');
plot(0,0,'ok');
leg = legend('red','blue','black');
Now I wish to add it to another figure:
figure;
t=linspace(0,10,100);
plot(t,sin(t));
%% ADD THE LEGEND OF PLOT ABOVE
This is how I have solved this problem in the past:
figure
t=linspace(0,10,100);
plot(t,sin(t));
hold on;
h = zeros(3, 1);
h(1) = plot(NaN,NaN,'or');
h(2) = plot(NaN,NaN,'ob');
h(3) = plot(NaN,NaN,'ok');
legend(h, 'red','blue','black');
This will plot the additional points, but because the coordinates are at NaN they will not be visible on the plot itself:
EDIT 26/10/2016: My original answer results in greyed out legend entries in 2016b. The updated code above works, but the answer below is only relevant pre-2016b:
figure
t=linspace(0,10,100);
plot(t,sin(t));
hold on;
h = zeros(3, 1);
h(1) = plot(0,0,'or', 'visible', 'off');
h(2) = plot(0,0,'ob', 'visible', 'off');
h(3) = plot(0,0,'ok', 'visible', 'off');
legend(h, 'red','blue','black');
This will plot the additional points, but they will not be visible on the plot itself.
You can also use copyobj to copy graphics elements from one figure to another if you have a lot of elements, then use set(x, 'visible', 'off') to hide them before showing the legend, but it depends on what your final application is.
Your question is a little unclear. However, the first thing I thought of when reading it was the text function in Matlab.
You can use the text function to add text to a Matlab figure. It's use is
>> text(x, y, str);
where x and y are the coordinates in the figure where you want to add the text str. You can use the Color option of text for colours and TeX to draw lines or even _. I've gotten very creative with plots using text.
Here's a quick and dirty example of emulating a legend with text
x = 0:pi/20:2*pi;
y = sin(x);
plot(x,y)
axis tight
legend('sin(x)');
text(5.7, 0.75, 'sin(x)');
text(5.1, 0.78, '_____', 'Color', 'blue');
which produces
For this specific case you could use the specific command (noted by #Hoki in the comments).
ht = text(5, 0.5, {'{\color{red} o } Red', '{\color{blue} o } Blue', '{\color{black} o } Black'}, 'EdgeColor', 'k');
to produce
by retrieving the handle to the text object it becomes trivial to copy it to a new figure, copyobj(ht, newfig). [1]

Visualize sparsity pattern with intensity using Matlab spy function

Matlab has a function spy for visualizing sparsity patterns of graph adjacency matrices.
Unfortunately it does not display the points by taking into account the magnitude of the values in the matrix. It uses a single color with same intensity to display all entries.
I wish to display the same spy plot but with the points "color-coded" like in a heatmap to indicate the magnitude of the entries. How can I do that?
spy function uses plot, which cannot have different marker colors in a lineseries object.
On the other hand, patch object can have different marker colors for different vertices. patch is originally for drawing polygons, but with no face color and no edge color, one can get similar result to plot with no line style.
S = bucky();
[m, n] = size(S);
[X, Y] = meshgrid(1:m, 1:n);
S = (X + Y) .* S;
nonzeroInd = find(S);
[x, y] = ind2sub([m n], nonzeroInd);
figure();
hp = patch(x, y, S(nonzeroInd), ...
'Marker', 's', 'MarkerFaceColor', 'flat', 'MarkerSize', 4, ...
'EdgeColor', 'none', 'FaceColor', 'none');
set(gca, 'XLim', [0, n + 1], 'YLim', [0, m + 1], 'YDir', 'reverse', ...
'PlotBoxAspectRatio', [n + 1, m + 1, 1]);
colorbar();
You can easily use different colormap, e.g., colormap(flipud(hot)).
If your matrix is not very large you could try to view it as an image using imagesc(). (Well you could use it for quite large matrices as well, but the pixels become very small.)
Here is an example of 20 random points in a 100x100 matrix, using colormap hot:
N = 100;
n = 20;
x = randi(N,1,n);
y = randi(N,1,n);
z = randi(N,1,n);
data = sparse(x,y,z);
imagesc(data)
axis square
colormap('hot')
This is the resulting image.
This can be compared to the plot we get using spy(data) where the markers are a bit larger.
If a white background is desired an easy way to achieve this is to flip the colormap:
figure
imagesc(data)
axis square
cmap = flipud(colormap('hot'));
colormap(cmap)
Hack solution redefining spy()
Googling a bit I found this thread at Matlab Central:
Spy with color for values?
There a solution is suggested that redefines spy(). It's however worth noting that (further down in the thread) this solution can also cause Matlab to crash for larger matrices.
I submitted a file on matlab exchange that also performs the spy task with points colored according to their value. Please see here.

How to set colorbar labels

I have some points in a 'jet' colormap. The points have a coefficient that can go from 0 to 1, but usually they dont cover all the range, e.g 0.75-0.9.
When I plot those points I colour them so 0.75 is the lesser colour in the colormap and 0.9 is the maximum color in the colormap, so all the colormap is shown. What I want to do is show that in the colorbar also. When I plot the colorbar the labels on it go to 64, but I want them from 0.75 to 0.9. How can I do that?
EDIT
I don't think the code itself helps a lot but here it goes, just in case. In the colors variable I convert the ZNCC to the range of the colormap.
EDIT2
I found the reason why caxis is not working for me. Here is the code:
%this is why it doesnt work
im=imread('someimageyouwanttotest_inRGB.png')
imshow(im)
points=[1, 2;1 , 2 ;0.3,0.7]
ZNCC=points(3,:)
cmap=colormap('jet');
colors=cmap(round( ((1-min(ZNCC))+ZNCC-1).*(size(cmap,1)-1)/max((1-min(ZNCC))+ZNCC-1))+1,: );
hold on
for i=1:length(ZNCC)
plot(points(1,i),points(2,i),'.','Color',colors(i,:));
end
colorbar()
hold off
I think that is your code displays all your colours correctly then rather just set up the colour bar first on no image:
points=[1, 2;1 , 2 ;0.3,0.7]
ZNCC=points(3,:)
cmap=colormap('jet');
caxis([min(ZNCC) max(ZNCC)]);
colorbar();
hold on
%this is why it doesnt work
im=imread('someimageyouwanttotest_inRGB.png')
imshow(im)
colors=cmap(round( ((1-min(ZNCC))+ZNCC-1).*(size(cmap,1)-1)/max((1-min(ZNCC))+ZNCC-1))+1,: );
for i=1:length(ZNCC)
plot(points(1,i),points(2,i),'.','Color',colors(i,:));
end
hold off
I can't test it as I don't have imshow :/
If caxis is not working for you, you could store the return from colorbar - it is a handle to the colorbar object. Then you can set its properties, like 'YTick' and 'YLim'. The full list of properties you can set is the same as the Axes Properties (because the colorbar is just an axes object, after all).
Here is an example:
% Generate some random data
z = rand(10);
[x, y] = meshgrid(1:size(z, 1));
% Plot colour map
pcolor(x, y, z);
shading interp; % Comment out to disable colour interpolation
colormap jet;
% Setup colorbar
c = colorbar();
set(c, 'YTick', [0.75 0.875 1]); % In this example, just use three ticks for illustation
ylim(c, [0.75 1]);
It is only necessary to do this once, after you've finished plotting.
Edit: If you need the limits and ticks automatically from the data, then you can do something like
% Find the limits
lims = [min(z(:)) max(z(:))];
% Function for rounding to specified decimal places
dprnd = #(x, dps)round(x*(10.^dps))./(10.^dps);
% Generate ticks
nTicks = 5;
nDps = 2;
ticks = dprnd(linspace(lims(1), lims(2), nTicks), nDps);
set(c, 'YTick', ticks);

waterfall plot using ribbon

I have a series of spectral data which I want to plot in a waterfall style plot.
waterfall itsself is no that usefull, because the thin lines have too many differences in each spectrum, that is is not very usefull
I therefore want to try the ribbon function, which looks promising in the docs
.
But the result is completely different and useless!
figure(2); clf;
ribbon(spectralSeries);
shading flat % otherwise complete dark
axis tight
EDIT:
I created now a manual waterfall plot, which is close to what I wanted:
hold on;
stepsize = 0.35;
for k = length(series):-1:1
color = cmap(k,:);
data = spectralSeries(k,:) + (k-1)*stepsize;
hplot(k) = filledcurve(xaxis, data, 0);
set(hplot(k), 'FaceColor' , color*1.2)
set(hplot(k), 'EdgeColor' , color*0.5)
end
hold off;
axis tight
Nevertheless I am still interested in a solution of the original problem.
EDIT 2:
Here an example using the same data with waterfall, ribbon and my custom code. Only my code is usefull to visualise the data. I would still like to know how to make ribbon and waterfall look like a decent plot...
This code is now used to create some data
xaxis = linspace(-pi/2,3/2*pi, 1000);
variation = [ 0.5 1 5 10];
spectralSeries = abs(sin(xaxis)'*ones(1,4) + sin(xaxis'*variation)*0.25);
Here a result using ribbon
ribbon(spectralSeries);
shading flat % otherwise complete dark
axis tight
And here with waterfall
hplot = waterfall(spectralSeries);
set( hplot, 'LineWidth', 4 );
hidden off;
and for comparison a plot using my own written code, which is similar to a waterfall, but without the depth axis. However it is the only one which looks decent and displays the data curves such that the variations between each curve can be seen.
You can still use waterfall, but set some patch and axes properties to get nicer output. The important thing to notice is that spectralSeries should be transposed.
figure
xaxis = linspace(-pi/2,3/2*pi, 200);
variation = [ 0.5 1 5 10 7 3.5 8];
spectralSeries = abs(sin(xaxis)'*ones(1,7) + sin(xaxis'*variation)*0.25);
h = waterfall(spectralSeries');
cameratoolbar;
%%
set(h, 'FaceColor', 'flat');
set(h, 'FaceAlpha', 0.7);
set(h, 'EdgeColor', 'k');
set(h, 'FaceVertexCData', rand(7,3))
set(gca, 'Color', [1 1 1]*0.85)
set(gca, 'GridLineStyle', 'none');
%%
myaa
The (optional) last statement, myaa, produces an anti-aliased figure; get the script here.