Move Longitude axis above scatterplot

5 views (last 30 days)
Below is my matlab code that plots locations in a region of interest:
axis = geoaxes('basemap','satellite');
S = struct(axis);
hold on
lat = normrnd(29,0.2,[1,50]);
lon = normrnd(-90,0.2,[1,50]);
geoscatter(axis,lat,lon,30,'.')
The lat/lon data is dummy data for reproducibility purposes. I want to put the Longitude axis above the scatterplot because I will eventually put a colorbar right below the figure. When using the plot function, I understand the following line can put the X-axis on top of a plot:
set(gca,'XAxisLocation','top','YAxisLocation','left')
However, I can't find the same capabilitiy for the geoscatter function.
Any suggestions on how I can accomplish the my goal?
  4 Comments
Debbie Green
Debbie Green on 3 Jun 2021
Edited: Debbie Green on 3 Jun 2021
@dpb, thanks very much for referring me to the Yair's hidden properties identifier. I changed my code to save the Lon/Lat axes into a structure S that lists the public and undocumented properties (which I can also obtain from Yair's function getundoc).
Unfortunately, I can't find any property that will enable me to put the Longitude axis above the scatterplot. If I do find a workaround, I'll update this post. Exploring other options right now.
dpb
dpb on 3 Jun 2021
Was afraid of that...why Mathworks has developed such a penchant recently for these opaque/dark graphics objects that hide useful/needed properties on the basis apparently of "Mother knows best!" is maddening and wastes untold numbers of users' hours more productively spent doing the research/productive work rather than fighting the toolset.
I'd suggest to submit enhancement request for the feature (and any others you run into along the way).

Sign in to comment.

Accepted Answer

Adam Danz
Adam Danz on 3 Jun 2021
Edited: Adam Danz on 3 Jun 2021
I looked for something analogous to XAxisLocation in geoaxes but couldn't find anything.
Here's a workaround until this feature becomes available. Scroll down to see a second version!
  • It applies text objects to the top of the geoaxes.
  • The text objects are copies of the actual longitude tick labels and properties.
  • Importantly, they text tick labels will update if there are changes to the axes such as user-interaction or changing axis limits.
  • Since the text objects copy the tick label properties, you can still set the tick and axis properties and the text objects will be updated.
  • You'll notice a flicker when panning and zooming. This is the time between removing the old text objs and assigning new ones.
% Create geoaxes
ax = geoaxes('basemap','satellite');
% % % % % % % % % % % % % % % %
% %
% Do all your plotting first %
% %
% % % % % % % % % % % % % % % %
%% Assign listener to create and update
% text labels that act as xtick labels on
% the top axes.
box(ax,'on') % so that tick marks appear on top
ax.UserData.UpperTicks = [];
ax.LongitudeAxis.Color = 'none'; % to make ticks & labels invisible
ax.UserData.Listener = addlistener(ax,'MarkedClean', ...
@(ax,~)updateGeoTicks(ax,[]));
% Initialize the upper "ticklabels" (ie text objects)
updateGeoTicks(ax) % if needed
function updateGeoTicks(ax, ~)
% Responds to MarkedClean event.
% Updates text objects acting as xtick labels on upper axes.
TickValues = ax.LongitudeAxis.TickValues;
nTicks = numel(TickValues);
latlim = geolimits(ax);
textYpos = latlim(2) + range(latlim)*.001;
% Delete old ticklables (text objs) and assign new ones.
% We can't merely update existing text objs since the number
% of ticks may change.
delete(ax.UserData.UpperTicks); % keep here rather than at beggining of fcn.
ax.UserData.UpperTicks = gobjects(nTicks,1);
for i = 1:nTicks
ax.UserData.UpperTicks(i) = text(ax, textYpos, TickValues(i),...
ax.LongitudeAxis.TickLabels{i},...
'HorizontalAlignment','center','VerticalAlignment','Bottom', ...
'Interpreter', ax.LongitudeAxis.TickLabelInterpreter, ...
'Rotation', ax.LongitudeAxis.TickLabelRotation, ...
'FontName', ax.FontName, ...
'FontSize', ax.FontSize, ...
'FontWeight', ax.FontWeight, ...
'FontAngle', ax.FontAngle,...
'Color', ax.LatitudeAxis.Color);
end
end
Include the longitude axis label
This solution above does not copy the longitude axis label. This version below reduces the axis height to 93% of the original height to make room for the longitude axis label at the top of the axes. It then copies the longitude axis label using the same logic above. The position of the label will update when the axis limits change and the properties are controlled by the axis properties so changes to the axis label using standard Matlab commands will also update the text object acting as the label.
% Create geoaxes
ax = geoaxes('basemap','satellite');
% % % % % % % % % % % % % % % %
% %
% Do all your plotting first %
% %
% % % % % % % % % % % % % % % %
%% Assign listener to create and update
% text labels that act as xtick labels on
% the top axes.
box(ax,'on') % so that tick marks appear on top
ax.LongitudeAxis.Color = 'none'; % to make ticks & labels invisible
ax.UserData.UpperTicks = [];
ax.UserData.LonLable = text(ax, nan, nan, '', ...
'HorizontalAlignment', 'Center','VerticalAlignment','bottom');
ax.UserData.Listener = addlistener(ax,'MarkedClean', ...
@(ax,~)updateGeoTicks(ax,[]));
% Initialize the upper "ticklabels" (ie text objects)
updateGeoTicks(ax) % if needed
% Make room for longitude axis label on top
ax.Position(4) = ax.Position(4)*.93;
function updateGeoTicks(ax, ~)
% Responds to MarkedClean event.
% Updates text objects acting as xtick labels on upper axes.
TickValues = ax.LongitudeAxis.TickValues;
nTicks = numel(TickValues);
[latlim, lonlim] = geolimits(ax);
latBuffer = range(latlim)*.001;
textYpos = latlim(2) + latBuffer;
% Delete old ticklables (text objs) and assign new ones.
% We can't merely update existing text objs since the number
% of ticks may change.
delete(ax.UserData.UpperTicks); % keep here rather than at beggining of fcn.
ax.UserData.UpperTicks = gobjects(nTicks,1);
for i = 1:nTicks
ax.UserData.UpperTicks(i) = text(ax, textYpos, TickValues(i),...
ax.LongitudeAxis.TickLabels{i},...
'HorizontalAlignment','center','VerticalAlignment','Bottom', ...
'Interpreter', ax.LongitudeAxis.TickLabelInterpreter, ...
'Rotation', ax.LongitudeAxis.TickLabelRotation, ...
'FontName', ax.FontName, ...
'FontSize', ax.FontSize, ...
'FontWeight', ax.FontWeight, ...
'FontAngle', ax.FontAngle,...
'Color', ax.LatitudeAxis.Color);
end
% update axis label
labelPos = [sum(ax.UserData.UpperTicks(1).Extent([1,3]))+latBuffer, ... % lat
lonlim(2)-range(lonlim)/2, 0]; % lon
set(ax.UserData.LonLable, 'Position', labelPos, ...
'String', ax.LongitudeLabel.String,...
'FontName', ax.LongitudeLabel.FontName, ...
'FontSize', ax.LongitudeLabel.FontSize, ...
'FontWeight', ax.LongitudeLabel.FontWeight, ...
'FontAngle', ax.LongitudeLabel.FontAngle, ...
'Color', ax.LatitudeLabel.Color);
end
  2 Comments
Debbie Green
Debbie Green on 3 Jun 2021
Edited: Debbie Green on 3 Jun 2021
@Adam Danz, that's incredible work. The last line in your post is definitely necessary to add space on the sides of the top axis. I'm still trying to modify the code to include the "Longitude" label; when I succeed, I'll update this post.
Edit: I added the "Longitude" lable by adding this line before the function:
hold on
ax.Subtitle.String = {'Longitude';' '};
hold off
Adam Danz
Adam Danz on 3 Jun 2021
I've updated my answer with a second version in the bottom half of the answer. It adds the longitudinal axis label to the listener function. Your subplot solution is simple and effective so if that works for you, keep it!

Sign in to comment.

More Answers (0)

Categories

Find more on Geographic Plots in Help Center and File Exchange

Products


Release

R2020b

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!