How to generate a map of European countries coloured according to a vector of positive and negative values?

Hello all,
I am trying to get a map of the European countries (cou_nam) in which the color of each country is assigned according to a vector including 0, positive and negative values (data).
The approach I follow is based on both the Mapping Toolbox as well as the borders file exchange submission.
% country names
cou_nam = {'Belgium', 'Bulgaria', 'Czech Republic', 'Denmark', 'Germany','Estonia','Ireland',...
'Greece','Spain','France','Croatia','Italy', 'Cyprus','Latvia', 'Lithuania', 'Luxembourg',...
'Hungary', 'Malta', 'Netherlands','Austria', 'Poland', 'Portugal','Romania','Slovenia', 'Slovakia',...
'Finland', 'Sweden'};
numRegions = length(cou_nam);
cmap = colormap;
% Random vector of values (one for each country)
data = randi([-5,5],numRegions,1);
% Map
figure('Color','w')
xh = worldmap('Europe');
setm(xh,'MapProjection','miller')
tightmap
geoshow ('landareas.shp', 'FaceColor', 'white');
for i = 1:1:numRegions
bordersm(cou_nam{i},'facecolor',cmap(data(i),:)) % => problem with negative values
end
colorbar
The issue with this code is that the variable data stores both negative and positive values, so when called in the for loop gets back an error since only positive integers are accepted.
Also, if I insert only positive values, i.e.
data = randi([1,5],numRegions,1);
it seems that the colorbar doesn't conform with the range of the values in data.
I have seen that there are similar questions (here and here) in the Community, yet I have not managed to get a solution.
Thanks!

 Accepted Answer

This should work. Note that the placeholder data isn't even integer-valued.
% country names
cou_nam = {'Belgium', 'Bulgaria', 'Czech Republic', 'Denmark', 'Germany','Estonia','Ireland',...
'Greece','Spain','France','Croatia','Italy', 'Cyprus','Latvia', 'Lithuania', 'Luxembourg',...
'Hungary', 'Malta', 'Netherlands','Austria', 'Poland', 'Portugal','Romania','Slovenia', 'Slovakia',...
'Finland', 'Sweden'};
numRegions = length(cou_nam);
cmap = jet(256); % specify the colormap
% Random vector of values (one for each country)
datarange = [-5 5]; % get the actual range of the data
data = range(datarange)*rand(numRegions,1) + min(datarange); % i'm assuming this is a placeholder
coloridx = round(rescale(data,1,size(cmap,1))); % rescale the data to use as an index into cmap
% Map
figure('Color','w')
xh = worldmap('Europe');
setm(xh,'MapProjection','miller')
tightmap
geoshow ('landareas.shp', 'FaceColor', 'white');
for i = 1:1:numRegions
bordersm(cou_nam{i},'facecolor',cmap(coloridx(i),:))
end
colorbar
colormap(cmap) % specify the colormap
caxis(datarange) % set caxis to match datarange

3 Comments

That's perfect! Thanks!
To facilitate reproduction of the map in applications where the data vector stores specific values associated with the countries (and not a random variable), I modified the last line of your solution so that:
caxis([min(data) max(data)]) % set caxis to match datarange
Also, given that the data would have zeros (where the color of that country could be grey), I thought that adding an if statement in the for loop, such as:
if data(i) == 0
bordersm(cou_nam{i},'facecolor',[192 192 192]) % grey color of the country if data value = 0
else
bordersm(cou_nam{i},'facecolor',cmap(coloridx(i),:))
end
Can this change for zeros in grey color be also displayed in the colorbar?
Thanks again!
If your data is relatively centered about 0, then you can do that by just using a colormap that's gray in the middle.
% country names
cou_nam = {'Belgium', 'Bulgaria', 'Czech Republic', 'Denmark', 'Germany','Estonia','Ireland',...
'Greece','Spain','France','Croatia','Italy', 'Cyprus','Latvia', 'Lithuania', 'Luxembourg',...
'Hungary', 'Malta', 'Netherlands','Austria', 'Poland', 'Portugal','Romania','Slovenia', 'Slovakia',...
'Finland', 'Sweden'};
numRegions = length(cou_nam);
cmap = jet(31); % specify the colormap (with an odd length
cmap(ceil(size(cmap,1)/2),:) = [192 192 192]/255;
% Random vector of values (one for each country)
data = 10*rand(numRegions,1) - 5; % i'm assuming this is a placeholder
datarange = [-5 5]; % nominal data range needs to be symmetric
% rescale the data to use as an index into cmap
coloridx = round((data-min(datarange))/range(datarange) * (size(cmap,1)-1) + 1);
% Map
%figure('Color','w')
xh = worldmap('Europe');
setm(xh,'MapProjection','miller')
tightmap
geoshow('landareas.shp', 'FaceColor', 'white');
for i = 1:1:numRegions
bordersm(cou_nam{i},'facecolor',cmap(coloridx(i),:))
end
colorbar
colormap(cmap) % specify the colormap
caxis(datarange) % needs to match datarange
You will need to make sure that clim() uses the values from nominal range specified by datarange and not the actual extrema of the data. Remember that you're explicitly coloring objects instead of letting the axes apply a scaled colormap. You're basically doing the colormapping yourself, so the colorbar will need to correspond to it, otherwise the colorbar won't reflect the values they're supposed to.
Thank you very much for your contribution and detailed explanation @DGM!

Sign in to comment.

More Answers (0)

Products

Release

R2019b

Asked:

on 26 Apr 2022

Commented:

on 26 Apr 2022

Community Treasure Hunt

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

Start Hunting!