Related
This should be simple, but I'm having trouble getting the scaling of a custom color map to match the range of values that I'm plotting. I don't quite understand how to get the range on the colorbar to match the range of the Z-dimension data. Here's a simple example that I'm trying to get working:
maxval = 5;
num = 10;
X = linspace(0,maxval,num);
Y = linspace(0,maxval,num);
Z = linspace(0,maxval,num);
cMap = interp1([0;1],[1 .5 0; .5 1 0],linspace(0,1,num));
scatter3(X,Y,Z,50,cMap,'Filled');
view(-75,20);
colormap(cMap)
colorbar
Which gives this:
I want the top value in the colorbar to be 5 in this example. I've tried modifying the [0;1] in the interp1 function, but I can't seem to get the correct settings.
I'm using version 2017a, if that matters.
The 5th input in scatter3 can represent a completely independent axis of data. If you want the color to match the Z axis data, then you need to pass it in twice.
The custom colormap (which can be a different size than the data) in interpolated to this 5th input to make the display.
Pragmatically, I think you want to make the following change to your code:
maxval = 5;
num = 10;
X = linspace(0,maxval,num);
Y = linspace(0,maxval,num);
Z = linspace(0,maxval,num);
cMap = interp1([0;1],[1 .5 0; .5 1 0],linspace(0,1,num));
scatter3(X,Y,Z,50,Z,'Filled'); % <-- note change here
view(-75,20);
colormap(cMap)
colorbar
I have this 3D plot that I'm making but the data are not linear. This implies that on my plot, the distance between the ticks that I want to show is not equal. How can I adapt the scale of the x and y axis so that this is the case, i.e. so that the axis gets divided into equal parts with the current ticks?
I want the same ticks and tick labels, but that they just have an equal distance in between them on the axes, in stead of small between 0.1 and 0.5 and large between 1 and 5.
The current plot looks like this:
RMSEval = xlsread('RMSEvalues.xlsx');
X = RMSEval(:,1);
Y = RMSEval(:,2);
Z = RMSEval(:,3);
figure(1);
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);
colormap('jet');
xticks([1e-13 5e-13 1e-12 5e-12 1e-11]);
yticks([1e-18 5e-18 1e-17 5e-17 1e-16]);
colorbar;
A plot where the distance between 0.1 and 0.5 is the same as between 1 and 5 would be a log plot, specifically a log-log plot since you want it on both axes. One way to achieve this would be to transform the X and Y values of your data logarithmically and modify the tick mark labels to match the untransformed values rather than the logarithmic ones you're actually plotting.
A rough guess at a solution is below. I say a rough guess because without posting the data you're importing from the xlsx file or a paired down version of that data (as in an MWE) I can't actually test it.
RMSEval = xlsread('RMSEvalues.xlsx');
X = log(RMSEval(:,1));
Y = log(RMSEval(:,2));
Z = RMSEval(:,3);
figure(1);
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);
colormap('jet');
xticks(log([1e-13 5e-13 1e-12 5e-12 1e-11]));
xticklabels(cellfun(#num2str,num2cell(),'UniformOutput',false));
yticks(log([1e-18 5e-18 1e-17 5e-17 1e-16]));
yticklabels(cellfun(#num2str,num2cell([1e-18 5e-18 1e-17 5e-17 1e-16]),'UniformOutput',false));
colorbar;
I'm trying to make a contour that follows the edges of the 'pixels' in a pcolor plot in Matlab. This is probably best explained in pictures. Here is a plot of my data. There is a distinct boundary between the yellow data (data==1) and the blue data (data==0):
Note that this is a pcolor plot so each 'square' is essentially a pixel. I want to return a contour that follows the faces of the yellow data pixels, not just the edge of the yellow data.
So the output contour (green line) passes through the mid-points of the face (red dots) of the pixels.
Note that I don't want the contour to follow the centre points of the data (black dots), which would do something like this green line. This could be achieved easily with contour.
Also, if it's any help, I have a few grids which may be useful. I have the points in the middle of the pixels (obviously, as that's what I've plotted here), I also have the points on the corners, AND I have the points on the west/east faces and the north/south faces. IF you're familiar with Arakawa grids, this is an Arakawa-C grid, so I have the rho-, u-, v- and psi- points.
I've tried interpolation, interweaving grids, and a few other things but I'm not having any luck. Any help would be HUGELY appreciated and would stop me going crazy.
Cheers, Dave
EDIT:
Sorry, I simplified the images to make what I was trying to explain more obvious, but here is a larger (zoomed out) image of the region I'm trying to separate:
As you can see, it's a complex outline which heads in a "southwest" direction before wrapping around and moving back "northeast". And here is the red line that I'd like to draw, through the black points:
You can solve this with a couple of modifications to a solution I posted to a related question. I used a section of the sample image mask in the question for data. First, you will need to fill the holes in the mask, which you can do using imfill from the the Image Processing Toolbox:
x = 1:15; % X coordinates for pixels
y = 1:17; % Y coordinates for pixels
mask = imfill(data, 'holes');
Next, apply the method from my other answer to compute an ordered set of outline coordinates (positioned on the pixel corners):
% Create raw triangulation data:
[cx, cy] = meshgrid(x, y);
xTri = bsxfun(#plus, [0; 1; 1; 0], cx(mask).');
yTri = bsxfun(#plus, [0; 0; 1; 1], cy(mask).');
V = [xTri(:) yTri(:)];
F = reshape(bsxfun(#plus, [1; 2; 3; 1; 3; 4], 0:4:(4*nnz(mask)-4)), 3, []).';
% Trim triangulation data:
[V, ~, Vindex] = unique(V, 'rows');
V = V-0.5;
F = Vindex(F);
% Create triangulation and find free edge coordinates:
TR = triangulation(F, V);
freeEdges = freeBoundary(TR).';
xOutline = V(freeEdges(1, [1:end 1]), 1); % Ordered edge x coordinates
yOutline = V(freeEdges(1, [1:end 1]), 2); % Ordered edge y coordinates
Finally, you can get the desired coordinates at the centers of the pixel edges like so:
ex = xOutline(1:(end-1))+diff(xOutline)./2;
ey = yOutline(1:(end-1))+diff(yOutline)./2;
And here's a plot showing the results:
imagesc(x, y, data);
axis equal
set(gca, 'XLim', [0.5 0.5+size(mask, 2)], 'YLim', [0.5 0.5+size(mask, 1)]);
hold on;
plot(ex([1:end 1]), ey([1:end 1]), 'r', 'LineWidth', 2);
plot(ex, ey, 'k.', 'LineWidth', 2);
Take a look at the following code:
% plotting some data:
data = [0 0 0 0 0 0 1 1
0 0 0 0 0 1 1 1
0 0 0 0 1 1 1 1
0 0 0 0 0 1 1 1
0 0 0 0 1 1 1 1
0 0 0 0 1 1 1 1
0 0 0 0 1 1 1 1];
p = pcolor(data);
axis ij
% compute the contour
x = size(data,2)-cumsum(data,2)+1;
x = x(:,end);
y = (1:size(data,1));
% compute the edges shift
Y = get(gca,'YTick');
y_shift = (Y(2)-Y(1))/2;
% plot it:
hold on
plot(x,y+y_shift,'g','LineWidth',3,'Marker','o',...
'MarkerFaceColor','r','MarkerEdgeColor','none')
It produces this:
Is this what you look for?
The most important lines above is:
x = size(data,2)-cumsum(data,2)+1;
x = x(:,end);
which finds the place of shifting between 0 to 1 for every row (assuming there is only one in a row).
Then, within the plot I shift y by half of the distance between two adjacent y-axis tick, so they will be placed at the center of the edge.
EDIT:
After some trials with this kind of data, I have got this result:
imagesc(data);
axis ij
b = bwboundaries(data.','noholes');
x = b{1}(:,1);
y = b{1}(:,2);
X = reshape(bsxfun(#plus,x,[0 -0.5 0.5]),[],1);
Y = reshape(bsxfun(#plus,y,[0 0.5 -0.5]),[],1);
k = boundary(X,Y,1);
hold on
plot(X(k),Y(k),'g','LineWidth',3,'Marker','o',...
'MarkerFaceColor','r','MarkerEdgeColor','none')
It's not perfect, but may get you closer to what you want in a more simple approach:
OK, I think I've solved it... well close enough to be happy.
First I take the original data (which I call mask_rho and use this to make masks mask_u, mask_v, which is similar to mask_rho but is shifted slightly in the horizontal and vertical directions, respectively.
%make mask_u and mask_v
for i = 2:size(mask_rho,2)
for j = 1:size(mask_rho,1)
mask_u(j, i-1) = mask_rho(j, i) * mask_rho(j, i-1);
end
end
for i = 1:size(mask_rho,2)
for j = 2:size(mask_rho,1)
mask_v(j-1, i) = mask_rho(j, i) * mask_rho(j-1, i);
end
end
I then make modified masks mask_u1 and mask_v1 which are the same as mask_rho but averaged with the neighbouring points in the horizontal and vertical directions, respectively.
%make mask which is shifted E/W (u) and N/S (v)
mask_u1 = (mask_rho(1:end-1,:)+mask_rho(2:end,:))/2;
mask_v1 = (mask_rho(:,1:end-1)+mask_rho(:,2:end))/2;
Then I use the difference between the masks to locate places where the masks change from 0 to 1 and 1 to 0 in the horizontal direction (in the u mask) and in the vertical direction (in the v mask).
% mask_u-mask_u1 gives the NEXT row with a change from 0-1.
diff_mask_u=logical(mask_u-mask_u1);
lon_u_bnds=lon_u.*double(diff_mask_u);
lon_u_bnds(lon_u_bnds==0)=NaN;
lat_u_bnds=lat_u.*double(diff_mask_u);
lat_u_bnds(lat_u_bnds==0)=NaN;
lon_u_bnds(isnan(lon_u_bnds))=[];
lat_u_bnds(isnan(lat_u_bnds))=[];
%now same for changes in mask_v
diff_mask_v=logical(mask_v-mask_v1);
lon_v_bnds=lon_v.*double(diff_mask_v);
lon_v_bnds(lon_v_bnds==0)=NaN;
lat_v_bnds=lat_v.*double(diff_mask_v);
lat_v_bnds(lat_v_bnds==0)=NaN;
lon_v_bnds(isnan(lon_v_bnds))=[];
lat_v_bnds(isnan(lat_v_bnds))=[];
bnd_coords_cat = [lon_u_bnds,lon_v_bnds;lat_u_bnds,lat_v_bnds]'; %make into 2 cols, many rows
And the result grabs all the coordinates at the edges of the boundary:
Now my answer goes a bit awry. If I plot the above vector as points plot(bnd_coords_cat(:,1),bnd_coords_cat(:,2),'kx' I get the above image, which is fine. However, if I join the line, as in: plot(bnd_coords_cat(:,1),bnd_coords_cat(:,2),'-' then the line jumps around, as the points aren't sorted. When I do the sort (using sort and pdist2) to sort by closest points, Matlab sometimes chooses odd points... nevertheless I figured I'd include this code as an appendix, and optional extra. Someone may know a better way to sort the output vectorbnds_coords_cat:
% now attempt to sort
[~,I]=sort([lon_u_bnds,lon_v_bnds]);
bnd_coords_inc1 = bnd_coords_cat(I,1);
bnd_coords_inc2 = bnd_coords_cat(I,2);
bnd_coords = [bnd_coords_inc1,bnd_coords_inc2];
bnd_coords_dist = pdist2(bnd_coords,bnd_coords);
bnd_coords_sort = nan(1,size(bnd_coords,1));
bnd_coords_sort(1)=1;
for ii=2:size(bnd_coords,1)
bnd_coords_dist(:,bnd_coords_sort(ii-1)) = Inf; %don't go backwards?
[~,closest_idx] = min(bnd_coords_dist(bnd_coords_sort(ii-1),:));
bnd_coords_sort(ii)=closest_idx;
end
bnd_coords_final(:,1)=bnd_coords(bnd_coords_sort,1);
bnd_coords_final(:,2)=bnd_coords(bnd_coords_sort,2);
Note that the pdist2 method was suggested by a colleague and also from this SO answer, Sort coordinates points in matlab. This is the final result:
To be honest, plotting without the line is fine. So as far as I'm concerned this is close enough to be answered!
I have a vector of values that I want to plot as brightness on a circle through the radius of it (I.e. If it was 0 3 1 5 I'd want a circle that was dark at the centre, then a bright ring around it, then a slightly darker ring, then a brighter ring).
To do this I've attempted to rotate my radial vector (E) around the y axis, as such
[X,Y,Z] = cylinder(E);
h = surf(X,Y,Z),
However I'm clearly not doing it right, as this appears to be rotating my curve around the x axis. I've tried just swapping X and Y, but it still rotates it around the x axis. Any help would be greatly appreciated.
One way would be to rotate your vector and create a surface. The Z data of the surface (your rotated vector) will be color coded according to the colormap you choose, if you display the surface from the top you get your circles at the different brightness.
If you are really only interested from the "top view" of this surface, then no need to create a full surface, a simple pcolor will do the job.
example:
%% // input data (and assumptions)
E=[0 3 1 5 2 7];
nBrightness = 10 ; %// number of brightness levels
r = (0:numel(E)) ; %// radius step=1 by default for consecutive circles
%// otherwise define different thickness for each circle
So if I use stairs([E 0]) you get your different brightness levels:
I had to add a last 0 to the vector to "close" the last level, we'll have to do that again in the solution below.
Now to rotate/replicate that around Y, color code the height, and look at it from the top:
%% // replicate profile around axis
ntt = 50 ; %// define how many angular division for the plot
theta = linspace(0,2*pi,ntt) ; %// create all the angular divisions
[rr,tt]=meshgrid(r,theta) ; %// generate a grid
z = repmat( [E 0] , ntt , 1 ) ; %// replicate our "E" vector to match the grid
[xx,yy,zz] = pol2cart(tt,rr,z) ; %// convert everything to cartesian coordinates
pcolor(xx,yy,zz) %// plot everything
colormap(gray(nBrightness)) %// make sure we use only "nBrightness" colors (Shades of gray)
caxis([0 nBrightness])
shading flat ; axis equal %// refine the view (axis ratio and "spokes" not visible) etc...
colorbar
axis off
will yield the following :
Note that your problem was not fully defined, I had to take assumptions on:
What radius each brightness circle should have ? (I made them all the same but you can modify that)
How many brightness levels you want ? (You can also modify that easily though).
Have you tried the rotate function?
direction = [0 1 0];
rotate(h,direction,90);
In this example a 90 degree rotation is performed around the y axis.
Using this library http://www.mathworks.com/matlabcentral/fileexchange/45952-circle-plotter
%http://www.mathworks.com/matlabcentral/fileexchange/45952-circle-plotter
x0 = 0;
y0 = 0;
colors = [0 3 1 5];
maxC = max(colors);
sz = numel(colors);
for i=fliplr(1:sz)
c = colors(i);
circles(x0,y0,i,'facecolor',[c/maxC c/maxC 0]) % http://au.mathworks.com/help/matlab/ref/colorspec.html
end
I am trying to make a plot with an intensity that varies over time:
[X,Y] = meshgrid(-30:.1:30);
figure;
colormap(bone);
for t = 0:0.1:2*pi
R = sqrt(X.^2 + Y.^2);
Z = cos(t)*abs(besselj(2,R));
surf(Z,'EdgeColor','None');
view(90,90);
axis([0 600 0 600 -0.5 0.5])
pause(0.1);
end
I want to look at this from the top, such that as the Z value changes, the color changes. The problem is that rather than having an absolute scale (black = -0.5, white = 0.5), the color scale is relative to the maximum and minimum values, such that the colors only change when the sign flips change. How can I set an absolute scale for the color map?
Thank you.
You have to use scaled colour mapping mode and set the limits of the scaling by using the caxis command.
Now the problem with your current code is that you call surf at each iteration of the loop, essentially destroying the current plot and generating a new plot each time. This will reset a lot of properties, including the caxis limits to auto. To overcome that, simply create your plot only once before the loop, then in the loop you only change the properties which are modified (the Z values in this case). This way everything else stays the same in the figure.
So you code becomes:
%% // Prepare and initialize the surface plot
[X,Y] = meshgrid(-30:.1:30);
R = sqrt(X.^2 + Y.^2) ; %// this doesn't need to be in the loop
Z = cos(0)*abs(besselj(2,R)) ; %// calculate initial value to create the surface
surfHandle = surf( Z , 'EdgeColor','None' ) ; %// create a first surface, and save the handle to the surface object
colormap(bone);
colorbar %// this is optional, just to make sure the colorbar does not vary
caxis([-5 5 ] ) ; %// this is what sets the color scaling to what you want
view(90,90);
axis([0 600 0 600 -0.5 0.5]) ; %// this doesn't need to be in the loop anymore
%% // Modify and update the surface plot
for t = 0:pi/100:2*pi
Z = cos(t)*abs(besselj(2,R));
set( surfHandle , 'ZData' , Z )
drawnow
pause(0.01);
end
Read coloring-mesh-and-surface-plots for more info on how surfaces can be colored.
If you just want white for values less than 0 and black for values greater than 0, you ca simply do:
surf(Z,sign(Z),'EdgeColor','None');
which uses the optional C argument to surf, telling Matlab to colour the plot depending on the values of C, not Z. sign(Z) is a matrix that has 1's where Z>0, 0's where Z=0, and -1's where Z<0.