Dav
Dav

Reputation: 65

Marker size unit proportionnal to the axis values

I have an X Y Z dataset. X and Y are the 2D coordinates and Z is the intensity.

I plot the data using the scatter function:

markerSize=100;
scatter(x,y,markerSize,z,'s','fill');

I use the options 's' and 'fill' to get filled squares.

My problem is the markerSize value corresponds to the area of the marker, and its unit is points (1/72 of one inch).

The marker size is constant, even if I resize the figure plot. So that the gap between the data points increases when I increase the figure size.

What I would like is a constant marker size which is a constant of the axis unit. For instance, the marker size should be 5x5 (5 in X axis and 5 in Y axis).

Thanks for your help.

Upvotes: 4

Views: 5976

Answers (5)

texnic
texnic

Reputation: 4098

The answer by ysakamoto doesn't work in Matlab 2014, where Mathworks changed figure handles from double to object. The following modification makes it work again:

function [ ] = setmarkersize( src, ~ )
% get position of the figure (pos = [x, y, width, height])
pos = get(src, 'Position');
% marker size
relativesize = 0.01;
% axes is not necessarily the only child of figure (e.g. colorbar may be first)
for i = 1:numel(src.Children)
    if strcmpi(src.Children(i).Type, 'axes')
        % make marker size depend on figure width
        src.Children(i).Children.SizeData = pos(3) * relativesize;
        break
    end
end
end

Also, when creating the figure it will not set the marker size to the correct value until resized. So call setmarkersize explicitly:

f = figure('ResizeFcn', @setmarkersize);
...
setmarkersize(f)

Upvotes: 1

Consider abandoning 'markers' and drawing directly on the axis using rectangles or circles. The 'rectangle' command with 'curvature' set to [1 1] is essentially an open circle. Units are in plot units, so each rectangle can be scaled:

rectangle('position', [rectCentX rectCentY widthInPlotUnits, heightInPlotUnits],'curvature',[1 1])

Upvotes: 0

Deve
Deve

Reputation: 4648

You will have to set the marker size manually according to the actual figure size on screen. Using axes property Position you can convert data units to relative figure units. In the next step this size can be converted to the absolute size in points on screen. With that information you can set the marker size accordingly. In the following code snippet I've set the x/y axis limits and width/height of the axis to identical values, because the square marker area can only be calculated reasonably if the marker width is equal to the marker height.

Set marker size relative to data units

% test data
x = [25*rand(1,10) 2.5];
y = [25*rand(1,10) 2.5];
z = [rand(1,10) 0.5];

% relative marker size in squared normalized figure units
marker_rel = 5;

%% Set relative marker size (approximately)
scatter(x,y,100,z,'s','fill');

% Set identical x/y limits and set axes height=widht, so that markers
% really represent squares in data units
xlim([0 25]);
ylim([0 25]);
set(gca, 'Units', 'Points');
axpos_pt = get(gca, 'Position');
axpos_pt = [axpos_pt(1) axpos_pt(2) min(axpos(3:4)) min(axpos(3:4))];
set(gca, 'Position', axpos_pt);

grid on;
grid minor;

% Set marker size relative to data units
markerSize = (marker_rel * axpos_pt(3) / diff(xlim))^2;
set(get(gca, 'children'), 'sizedata', markerSize);

As it turns out, the displayed marker size is slightly smaller than expected. Obviously, there's some bounding box of unknown (at least to me) size, see here.

An alternative approach is to plot rectangles "manually" as shown in the following code snippet (same test data is used). When the figure is resized, the rectangles are resized as well without any special callback function being needed.

Draw rectangles manually

%% Use rectangles (exact)
figure;
axes;
cmp = colormap;
z_range = max(z) - min(z);
line_style = 'none'; % set to '-' to make the edges visible
for k = 1:length(x)
    x_pos = x(k) - marker_rel/2;
    y_pos = y(k) - marker_rel/2;
    w = marker_rel;
    h = marker_rel;
    color = cmp( round(((z(k) - min(z))/z_range)*(length(cmp) - 1)) + 1, : );
        rectangle('Position', [x_pos y_pos w h], 'FaceColor', color,...
          'LineStyle', line_style);
end
grid on;
grid minor;

The above code produces the desired marker size:

Demonstration of rectangles with desired size and color grading

In general, these are no squares. They can (and will) only be squares if xlim = ylim and absolute height of axis = absolute width of axis. I have shown in my first code snippet how to achieve this.

Upvotes: 2

Dav
Dav

Reputation: 65

I found an answer on the Matlab central forum which does not use the useful dsxy2figxy function. Here is the link to it (link)

The code is the following:

x = rand(1,50);
y = rand(1,50);
s = 5; %Marker width in units of X
h = scatter(x,y); % Create a scatter plot and return a handle to the 'hggroup' object
%Obtain the axes size (in axpos) in Points
currentunits = get(gca,'Units');
set(gca, 'Units', 'Points');
axpos = get(gca,'Position');
set(gca, 'Units', currentunits);
markerWidth = s/diff(xlim)*axpos(3); % Calculate Marker width in points
set(h, 'SizeData', markerWidth^2)

Upvotes: 1

ysakamoto
ysakamoto

Reputation: 2532

You want to make the size of markers proportional to the figure size.

The size of markers is controlled by SizeData parameter of the scattergroup object. The size of figure is stored in Position parameter of the figure object. The difficult part is to interactively resize the marker when the figure size is changed. So you need to use ResizeFcn callback and call setmarkersize function that you define.

function [ ] = setmarkersize( src, evnt )

    % # get position of the figure (pos = [x, y, width, height]) 
    pos = get(src, 'Position'); 
    % # get the scattergroup object 
    h = get(get(src,'children'),'children'); 
    % # resize the marker
    relativesize = 0.5;
    set(h,'SizeData', pos(3)*relativesize); 

end
================================================
% # attach the callback to figure 
f = figure('ResizeFcn', @setmarkersize); 
h = scatter(x,y,markerSize,z,'s','fill');

Upvotes: 4

Related Questions