What plotting software to use: 2D polar plot with unique data - matlab

I have my (example) data in the following format:
R_min R_max θ_min θ_min Zones
0 260 0 1.57 114
260 270 0 1.57 106
270 320 0 1.57 107
As you can see, I have "zones" (areas) that are created from R_min to R_max that sweep from theta_min to theta_max. Each row of data represents an area that I want to plot with a corresponding color based on the zone number. In this simple case, the data I show above would look like the following picture:
What plotting software should I use to accomplish this? I have been investigating the following options:
MATLAB. I am having trouble finding exactly what I need, but have found features like http://www.mathworks.com/help/symbolic/mupad_ref/plot-density.html?searchHighlight=plot%3A%3Adensity
Gnuplot. My issue with Gnuplot is the lack of documentation.
Are there other programs or a better way to compile my data to make my task-at-hand doable?
My real data set has thousands of rows of data and not nearly as simple as a quarter circle rainbow.

Here is one possible solution with gnuplot. That uses the circles plotting style to draw the overlapping wedges at the origin with a specified radius. That requires you to have your data sorted by descending maximum radius, and that you have no gaps.
Here is a possible script:
set xrange [0:350]
set yrange [0:350]
set size ratio -1
set style fill solid noborder
set palette defined (106 'blue', 107 'yellow', 114 'magenta')
set cbrange [106:114]
unset colorbox
plot 'test.txt' using (0):(0):2:($3*180/pi):($4*180/pi):5 with circles linecolor palette notitle
with the result (with 4.6.4):
Some more remarks:
The radius of the circles is given in units of the x-axis, but the y-axis isn't adapted accordingly. That's why you must set both xrange, yrange and even the ratio of the two axes with set size ratio -1.
Using the palette for coloring is one option, other options like using linecolor variable or linecolor rgb variable, are explained e.g. in gnuplot candlestick red and green fill.
On Unix systems, the sorting could also be done on-the-fly with e.g.
plot '< sort -r test.txt' ...

It's actually easy to do that with Matlab using simple trigonometry and the fill function:
% R_min R_max θ_min θ_min Zones
data = [
0 260 0 1.57 114
260 270 0 1.57 106
270 320 0 1.57 107];
% Define a color table, indexed by the "Zones" column
colors = {};
colors{114} = [1.0 0.0 0.5];
colors{106} = [0.7 0.0 1.0];
colors{107} = [1.0 1.0 0.0];
% Define the resolution of the plot (more points = more round)
nPoints = 100;
clf;
hold on;
for i = 1:size(data, 1)
% Extract the data from the i'th row. There's no need for this, you
% could access it directly below, but it makes the code more clean. :)
r_min = data(i,1);
r_max = data(i,2);
theta_min = data(i,3);
theta_max = data(i,4);
color = data(i, 5);
% First, get the sine and cosine between theta_min and theta_max
sin_theta = sin(linspace(theta_min, theta_max, nPoints));
cos_theta = cos(linspace(theta_min, theta_max, nPoints));
% Now, draw a semi-circle with radius = r_min and merge this
% semi-circle with another with radius = r_max, but reversed, so that
% it begins where the previous semi-circle ended.
x = [sin_theta * r_min sin_theta(end:-1:1) * r_max];
y = [cos_theta * r_min cos_theta(end:-1:1) * r_max];
% Draw the polygon.
fill(x,y, colors{color}, 'EdgeColor', colors{color});
end
hold off;
axis equal;
grid;
maxRadius = max(data(:,2));
axis([-maxRadius maxRadius -maxRadius maxRadius]);
Result:

Related

How do I set a custom colourbar for unequal data ranges in matlab?

I have some shelf sea data and need to plot values equal to NaN in one colour (for the land), values less than 2.7 in another colour, and values above 2.7 in a third colour. I've tried creating a custom colourbar with three colours (blue for 0, aqua for 0.001 to 2.7 and yellow for everything else), however, it plots a colour bar that has 3 equal sections (blue for 0 - 2, aqua for 2 - 4 and yellow for 4 - 6). I'm very new to Matlab and unsure what I am doing wrong.
Below is my code. The first part (included for clarity) works fine.
to_plot = hu_atl;
mask = zeros(size(to_plot));
mask(hz_atl>200) = NaN; %open ocean
mask(hz_atl<=200) = 1; %shelf
mask(mask==1) = to_plot(mask==1); %(or pick other suitable value)
log = log10(mask);
pcolor(log10(mask')); %plot h/u3 on a log scale
latlim = [-90 90]; %set the latitude limits
lonlim = [100 180]; %set the longitude limits
shading interp; %hide the grid lines, without this, the map just looks black due to all the grid lines
c = colorbar;
c.Label.String = 'log 10(h/u^3)'; %This labels the colourbar
caxis([0 6]); %This sets the upper and lower limits of the colourbar
Below is the colourbar code, which plots the figure below.
crange = [ 0 0.0001;0.0001 2.7;2.7 inf] ;
cmap = jet(size(crange,1)) ;
%%Arrange the colors range
colors = zeros(size(log));
for i = 1:size(crange,1)
colors(log > crange(i,1) & log<=crange(i,2)) = crange(i,2);
end
colormap(cmap)
title('Global tidal mixing front positions based on TPXO9 tidal data')
xlabel('Longitude')
ylabel('Latitude')
set(gca,'XTick',[]) %hides tick numbers on x axis
set(gca,'YTick',[]) %hides tick numbers on y axis
Is there a way to change the ranges on the colourbar to match my requirements above?
Thanks in advance for any assistance!

plotting a surface plot in two colours depending on condition on z axis

z is 200*200 array and I have a surf plot using Matlab surf(x,y,z). I am trying to plot the surf plot so that when z<10 it will be blue and when z>10 it will be red. At the moment the surf plot is like this. Can someone please suggest a way to do this in Matlab?
One way to achieve that (there are several ways) is to use a custom colormap:
Build a colormap with only the color you want to appear in your graph, then just adjust the levels so the midpoint is for Z=10.
The first part of the code shows how to create your custom colormap and apply it:
Zt = 10 ; % threshold level for Z
z = peaks+Zt ; % dummy test data
% build a colormap with your 2 custom color
% [%Red %green %blue]
cmap = [0.79 0.22 0.81 ; % some kind of purple
0 0 1 ] ; % pure blue
surf(z) ; % plot the surface
colormap(cmap) ; % apply the custom colormap
hcb = colorbar ;
This produces a surface with your two chosen colors:
But wait! The separation is not exactly at the Z=10 level. No problem, if we adjust the boundaries of the colormap so your threshold level is bang in the middle, Matlab will take care of adjusting the coloring for us:
%% Now center the colormap boundaries around your threshold level
% get the minimum and maximum
zmax = ceil( max(max(z)) ) ;
zmin = floor( min(min(z)) ) ;
span = max( [zmax-Zt, Zt-zmin] ) ; % calculate the span each side of [Zt]
caxis([Zt-span , Zt+span]) ; % center the colormap around [Zt]
The last bit of code above allow to define an equal span around your chosen threshold level and take the content of the Z data into account. If you know in advance the limits of your data you don't need to do the calculations. On the example above I could also have simply use the last line with some hard coded values:
caxis([0 , 20]) ;
As long as the interval you specify for caxis is centered around your threshold level it will work.
Edit:
To control the labels of the colorbar, I usually set the Ticks and TickLabels after the colorbar (or axes) is created. For this you need the handle of the colorbar object.
Note that in the code above I modified the last line of the first code block. I changed colorbar, to hcb=colorbar;. This way we have the handle of the colorbar, which allow us to set any arbitrary tick and associated label.
The most straightforward way to get your result for this specific example is:
hcb.Ticks = [ 5 , 10 , 15 ] ;
hcb.TickLabels = {'<10','10','>10'} ;
However if you want a more generic solution which can work with any threshold Zt then you can use:
%% adjust colorbar labels
zl = caxis ; % get the limits of the color scale
Zstr = num2str(Zt) ; % get a string representing the threshold
hcb.Ticks = [ (Zt+zl(1))/2 , Zt , (zl(2)+Zt)/2 ] ;
hcb.TickLabels = { ['<' Zstr] , Zstr , ['>' Zstr] } ;
For your example, both options produce:
Adapted from MATLAB Answers
z = peaks+10; % sample data generated between 3.4 and 18.1
% Make colors be red above 10, and blue below 10.
redChannel = z > 10;
greenChannel = 0*z;
blueChannel = z < 10;
% Make the RGB image.
colors = double(cat(3, redChannel, greenChannel, blueChannel));
% Plot the surface with those colors.
surf(z, colors);
try this
surf(x,y,z)
map = [0.0 0.0 1.0
1.0 0.0 0.0];
colormap(map);
caxis([0 20]);

MATLAB/Octave plot markers above the line rather than on the line

I want to visualize the peaks of a function, and I want to have markers for it appear above the line they are associated with.
I fabricated a minimum example where I already have the peaks, the question is just how to visualize the markers correctly:
y = [0.1 0.3 10.0 1.0 0.5 0.1 24.0 0.6 0.1 0.2]
x = (1:length(y))
plot(x,y);
hold on;
peaks = [3 7];
plot(x(peaks), y(peaks), 'v', 'MarkerSize', 24);
print('-dpng', 'example.png', '-S640,480');
So, as a result, the markers appear centered on the line like this:
The result that I want could be achieved by carefully tuning a parameter OFFSET like this:
plot(x(peaks), y(peaks)+OFFSET, 'v', 'MarkerSize', 24);
As shown in the following figure, for this exact example OFFSET=2.56 works for the exported png, but with the interactive plot and exporting vector graphics, it's wrong again.
Can anyone recommend a way to get this result without having to manually doing trial/error?
Currently I am using Octave with gnuplot to export to latex+tikz, and it would be good if the solution would work there.
In my actual (more complicated) use case I am plotting multiple lines after each other into the same figure, and the y limits change, so the offsets can not just be calculated easily, as the markersize doesn't change with the y limits.
Edit: Additionally I am using a semilogx plot, so drawing lines inside the diagram in the x/y-Axis scales would look distorted.
One way to do this is with annotations, but there are some drawbacks (see below).
Annotations enable you to place various graphic objects into your figure. One very annoying thing about them is that they work in so-called normalized coordinates,
which span the whole figure window (not just the plot area) and go from [0,0] to [1,1], forcing you to convert to these coordinates first. I wrote a simple function to do this, provided your plot scale is linear (if you want logarithmic, you will have to modify this function):
## Convert from data coordinates to normalized figure coordinates.
function [xf yf] = figcoords(xa, ya)
axp = get(gca, "position");
lf = axp(1);
bf = axp(2);
rf = lf + axp(3);
tf = bf + axp(4);
xl = xlim();
yl = ylim();
la = xl(1);
ra = xl(2);
ba = yl(1);
ta = yl(2);
xf = lf + (xa-la).*(rf-lf)./(ra-la);
yf = bf + (ya-ba).*(tf-bf)./(ta-ba);
endfunction
With this out of your way, you can proceed to annotating the plot using the annotation function:
y = [0.1 0.3 10.0 1.0 0.5 0.1 24.0 0.6 0.1 0.2];
x = (1:length(y));
peaks = [3 7];
## Plot the data as you would normally
plot(x,y);
## Plot peak markers (no `hold on` needed)
[xp yp] = figcoords(peaks, y(peaks)); # Transform to figure coordinates
for coords = [xp; yp]
xpi = coords(1);
ypi = coords(2);
annotation("arrow", [xpi xpi], [ypi+eps ypi]);
endfor
Plot with annotated peaks
Here, we actually draw little arrows pointing from top onto the peaks.
As their height is very small, we only see the arrowheads.
The arguments to the annotation function are the x and y coordinates
of the endpoints of the arrow. Note that we added a small number (eps)
to the y-value of the starting point to make the arrow point downward.
If you want, you can tweak the appearance of the markers to make them more visually appealing:
y = [0.1 0.3 10.0 1.0 0.5 0.1 24.0 0.6 0.1 0.2];
x = (1:length(y));
peaks = [3 7];
coloridx = get(gca, "ColorOrderIndex")
peakcolor = get(gca, "ColorOrder")(coloridx,:); # Save current plot colour
plot(x,y);
## Plot peak markers
[xp yp] = figcoords(peaks, y(peaks));
for coords = [xp; yp]
xpi = coords(1);
ypi = coords(2);
annotation("arrow", [xpi xpi], [ypi+eps ypi], "headstyle", "plain",...
"color", peakcolor);
endfor
Plot with annotated peaks in the same color
Drawbacks
While this approach works fine regardless of the size of the markers or your plot, there are some drawbacks:
First, the annotations are fixed relative to the figure window, not the plot.
This is fine when you display the plot for the first time, but once you zoom
or pan, the alignment is lost: The markes stay in place while the plot moves.
If you don't need an interactive plot (eg, you just want to export it to image),
just be sure to set the plot limits before adding the annotations and you should
be fine.
Second, this method is very slow compared to plotting the points using the
plot function. On my computer, for example, when drawing a simple example with
seven annotated peaks, it takes about a second before the markers appear.
Plotting a signal with thousands of peaks is near impossible.
Concerning the Matlab part, you could draw the peak markers yourself. Somewhere along these lines (extending your example):
y = [0.1 0.3 10.0 1.0 0.5 0.1 24.0 0.6 0.1 0.2]
x = (1:length(y))
figure, plot(x,y);
leglengthx=0.2;
leglengthy=0.5;
hold on;
peaks = [3 7];
peaks_max=[10 24];
for ii=1:2
line([peaks(ii) peaks(ii)+leglengthx],[peaks_max(ii) peaks_max(ii)+leglengthy]);
line([peaks(ii) peaks(ii)-leglengthx],[peaks_max(ii) peaks_max(ii)+leglengthy]);
line([peaks(ii)-leglengthx peaks(ii)+leglengthx],[peaks_max(ii)+leglengthy peaks_max(ii)+leglengthy]);
end
plot(x(peaks), y(peaks), 'v', 'MarkerSize', 24);
I have added the maxima of the peaks, which should not be an issue to automatically extract and two variables that control the triangle size of the marker. And then its just drawing three lines for every peak.
I don't know how this will translate to Octave.
What about drawing the little triangles?
y = [0.1 0.3 10.0 1.0 0.5 0.1 24.0 0.6 0.1 0.2];
x = (1:length(y));
peaks = [3 7];
plot(x,y);
hold on; line([peaks(1) peaks(1)+0.2], [y(x==peaks(1)) y(x==peaks(1))+1], 'color','b')
hold on; line([peaks(1) peaks(1)-0.2], [y(x==peaks(1)) y(x==peaks(1))+1], 'color','b')
hold on; line([peaks(1)+0.2 peaks(1)-0.2], [y(x==peaks(1))+1 y(x==peaks(1))+1], 'color','b')
hold on; line([peaks(2) peaks(2)+0.2], [y(x==peaks(2)) y(x==peaks(2))+1], 'color','b')
hold on; line([peaks(2) peaks(2)-0.2], [y(x==peaks(2)) y(x==peaks(2))+1], 'color','b')
hold on; line([peaks(2)+0.2 peaks(2)-0.2], [y(x==peaks(2))+1 y(x==peaks(2))+1], 'color','b')
There can be a problem if the y-values of the peaks exists in other locations on the vector. If so, you can specify first or other matching specs for the find function.

Modify colorbar ticks and color range

This question is following on from a previous question about HSV color space.
Let's say I have two arrays A and B, where A are my data points (2D) of interest to be shown in the colorbar and B is an RGB image transformed from the HSV color space where: Hue is in the interval [0.25-1] (corresponding to normalized A values 0.25-1), Saturation = 1, Value in interval [0-1] (corresponding to some other values).
When displaying B with imshow, I want to create a matching colorbar with ticks that correspond to the value range from A.
First difficulty that I'm facing is that I want my Hue to be in the interval [0.25-1] and hence I only need a certain part of the hsv colorbar to be displayed.
Second difficulty is that I need to match the value range from A to the colorbar.
Example code:
A = rand(30,30)*0.4; % Values range from 0 - 0.4
X = rand(30,30)*100+100; % Values range from 100 - 200
A_n = A / (max(A(:))/0.75) + 0.25; % "Normalize", with range 0.25 - 1
X_n = X / max(X(:)); % Normalize, range 0 - 1
colorRGB = NaN([size(A),3]); % preallocate
for ii = 1:size(A,1)
for jj = 1:size(A,2)
colorRGB(ii,jj,:) = hsv2rgb([A_n(ii,jj),1,X_n(ii,jj)]); % turn into RGB
end
end
imshow(colorRGB), % display image
colormap hsv; cb = colorbar();
In the example you can see that the colourbar covers the whole hsv range and has ticks from 0 - 1.
What I want it to be is showing only the upper 75% of the hsv range with ticks from 0 to max(A(:))
The correct colorbar assuming that max(A(:)) = 0.35 should look like this:
(you can see that I just cropped it, but that should not be necessary either)
In order to do that you need 2 things. First crop the colorbar, bu setting its limits. Secondly, change the text in the labels of the colobar, but to make sure they are in the rigth places, you also need to set the positions of them manually. Hopefully the code makes sense:
cb = colorbar();
set(cb, 'ylim', [25 100])
set(cb, 'XTick', [25:15:100]) % modify values if you preffer
set(cb,'XTickLabel',strsplit(num2str([0.25:0.15:1])));

Find point of intersection between histfit and line - MATLAB

How can I find the exact y-coordinate of the red gaussian at x = 0.5 without using Data Cursor?
I want the blue line to end when it touches the gaussian. But I need to find the point of intersection between the gaussian of the histfit shown in red, and the blue line at 0.5. I can access the data points of the histfit plot as follows:
C = get(get(gca, 'Children'), 'YData');
C{1,1}
line([0.5 0.5],[0 max(C{1,1})],'Color','b');
However, the data points of the gaussian don't relate to this axes. Meaning, x-axis of C{1,1} is from 1 - 100 and not from 0.1 to 0.9.
Whats the easiest way to find the y-coordinate of the gaussian at 0.5 so that I can replace max(C{1,1}) by that?
Getting XData as well should give you the right x-values:
C = get(get(gca, 'Children'), 'XData');
Alternatively, the values of YData should be at regular intervals, even if not on the correct scale (since it originated from hist), so you could probably find the y-value corresponding to x=0.5 in the plot.
The point x=0.5 between 0.1 and 0.85 (approximately, from the plot) scales to the point x=53.33 between 1 and 100. If the y-value at x=53 isn't accurate enough for plotting, you can just interpolate the value between 53 and 54 and that should be enough.
Here is some code to that should do the job:
XPlotRange = [0.1 0.85];
XDataRange = [1 100];
XPlotToInterp = 0.5;
XDataToInterp = XDataRange(1) + (XPlotToInterp - XPlotRange(1))*diff(XDataRange)/diff(XDataRange);
XData1 = floor(XDataToInterp);
XData2 = ceil(XDataToInterp);
YInterp = interp1([XData1 XData2], [YData(XData1) YData(XData2)], XDataToInterp);
Here YInterp is the interpolated y-value for the corresponding x-value.