应粉丝要求写了一个多变量映射图绘制的工具函数,依旧全网唯一hiahiahia,一般图像只有一个变量需要映射到颜色,其他变量映射到点的大小啥的,但这两个变量之间的联系就没有那么直观,而且如果变量更多例如三个变量就比较难找到表现形式,但是,多变量映射图可以解决这些问题。
本篇先讲解双变量映射图表绘制再讲解三变量映射图表绘制。两个工具函数由于过长均放在文末。
示例图
双变量映射图表
输入输出说明
需要A,B两个完全相同大小的数值矩阵,通过
[CMapData,CMapHdl]=multiVarMap2(A,B)
的格式调用双变量映射图绘制工具函数,返回值为一个CMapData
RGB颜色数组,用于给图形对象赋予颜色,以及一个CMapHdl
图例。
基本使用
这里第一个例子是一个网格数据,multi2_3.mat
中存储着变量X1
,X2
,A
,B
,C
这里只用AB数据,分别是当前格点属于类A和B的概率,简单编写函数如下:
multiData=load('multi2_3.mat');
X1=multiData.X1;
X2=multiData.X2;
A=multiData.A;
B=multiData.B;
% 先绘制一个没有赋予颜色的图形
surfHdl=surf(X1,X2,zeros(size(X1)),'EdgeColor','none');
hold on;view(2);axis tight
% 调用工具函数生成图例和映射表
[CMapData,CMapHdl]=multiVarMap2(A,B);
set(surfHdl,'CData',CMapData);
颜色划分
上面每个变量颜色划分了四段,我们可以调整pieceNum
属性划分更多段颜色,例如分成8段:
multiData=load('multi2_3.mat');
X1=multiData.X1;
X2=multiData.X2;
A=multiData.A;
B=multiData.B;
% 先绘制一个没有赋予颜色的图形
surfHdl=surf(X1,X2,zeros(size(X1)),'EdgeColor','none');
hold on;view(2);axis tight
% 调用工具函数生成图例和映射表
[CMapData,CMapHdl]=multiVarMap2(A,B,'pieceNum',8);
set(surfHdl,'CData',CMapData);
其他配色
们可以调整colorList
属性调整配色,需要4x3大小的RGB数组,分别代表左下,右下,左上,右上的颜色,当然函数内置了12种默认配色,通过1-12的编号调用:
multiData=load('multi2_3.mat');
X1=multiData.X1;
X2=multiData.X2;
A=multiData.A;
B=multiData.B;
% 先绘制一个没有赋予颜色的图形
surfHdl=surf(X1,X2,zeros(size(X1)),'EdgeColor','none');
hold on;view(2);axis tight
% 调用工具函数生成图例和映射表
[CMapData,CMapHdl]=multiVarMap2(A,B,'pieceNum',8,'colorList',10);
set(surfHdl,'CData',CMapData);
全部配色展示:
图例移动
可以随便拖拽:
修改变量名和标题
multiData=load('multi2_3.mat');
X1=multiData.X1;
X2=multiData.X2;
A=multiData.A;
B=multiData.B;
% 先绘制一个没有赋予颜色的图形
surfHdl=surf(X1,X2,zeros(size(X1)),'EdgeColor','none');
hold on;view(2);axis tight
% 调用工具函数生成图例和映射表
[CMapData,CMapHdl]=multiVarMap2(A,B,'pieceNum',8,'colorList',10,'title','XXXXX','varName',{'AAAAA','BBBBB'});
set(surfHdl,'CData',CMapData);
三维绘图
不管二维还是三维图例都可以添加
multiData=load('multi2_3.mat');
X1=multiData.X1;
X2=multiData.X2;
A=multiData.A;
B=multiData.B;
% 先绘制一个没有赋予颜色的图形
surfHdl=surf(X1,X2,max(A,B),'EdgeColor','none');
hold on;view(3);axis tight
% 调用工具函数生成图例和映射表
[CMapData,CMapHdl]=multiVarMap2(A,B,'colorList',10,'pieceNum',8);
set(surfHdl,'CData',CMapData);
示例2地图绘制
本文展示mapping toolbox绘制的图双变量映射,因此需要提前安装mapping toolbox工具箱,当然其他地图绘制工具箱绘制的图用本文的双变量映射也差不多,以下内容懒得收集数据,于是随便生成了一些数据:
axm=usamap("conus");
states=shaperead("usastatelo.shp",'UseGeoCoords',true);
% 俩州离太远画不开,不要
for i=length(states):-1:1
if states(i).Name=="Alaska"||states(i).Name=="Hawaii"
states(i)=[];
end
end
faceColors=makesymbolspec('Polygon',{'INDEX',[1 numel(states)],'FaceColor',...
polcmap(numel(states))});
usaMap=geoshow(states, 'DisplayType','polygon','SymbolSpec', faceColors);
% 不想去收集数据了,随便生成点随机数
A=rand(numel(states),1);
B=rand(numel(states),1);
% 调用工具函数生成图例和映射表
[CMapData,CMapHdl]=multiVarMap2(A,B,'colorList',11,'pieceNum',8);
% 循环修改每个州颜色
for i=1:length(usaMap.Children)
usaMap.Children(i).FaceColor=[CMapData(i,1,1),CMapData(i,1,2),CMapData(i,1,3)];
end
示例3气泡图绘制
x=1:20;
y=rand(1,20);
sz=rand(1,20);
BC=bubblechart(x,y,sz);
% 不想去收集数据了,随便生成点随机数
A=rand(1,20);
B=rand(1,20);
% 调用工具函数生成图例和映射表
[CMapData,CMapHdl]=multiVarMap2(A,B,'colorList',10,'pieceNum',8);
BC.CData=[CMapData(:,:,1)',CMapData(:,:,2)',CMapData(:,:,3)'];
三变量映射图表
使用方式和双变量映射区别不大:
multiData=load('multi2_3.mat');
X1=multiData.X1;
X2=multiData.X2;
A=multiData.A;
B=multiData.B;
C=multiData.C;
% 先绘制一个没有赋予颜色的图形
surfHdl=surf(X1,X2,zeros(size(X1)),'EdgeColor','none');
hold on;view(2);axis tight
% 调用工具函数生成图例和映射表
[CMapData,CMapHdl]=multiVarMap3(A,B,C,'colorList',1,'pieceNum',8);
set(surfHdl,'CData',CMapData);
其他配色
只准备了俩配色,以下内容因为画完后背景色和图例文字色相同因此修改了图例文本颜色:
multiData=load('multi2_3.mat');
X1=multiData.X1;
X2=multiData.X2;
A=multiData.A;
B=multiData.B;
C=multiData.C;
% 先绘制一个没有赋予颜色的图形
surfHdl=surf(X1,X2,zeros(size(X1)),'EdgeColor','none');
hold on;view(2);axis tight
% 调用工具函数生成图例和映射表
[CMapData,CMapHdl]=multiVarMap3(A,B,C,'colorList',2,'pieceNum',8);
set(surfHdl,'CData',CMapData);
CMapHdl.XColor=[1,1,1];
CMapHdl.YColor=[1,1,1];
CMapHdl.ZColor=[1,1,1];
CMapHdl.Title.Color=[1,1,1];
图例移动
可以随便拖拽、旋转:
示例2地图绘制
随机生成颜色太乱,因此第二个图把所有数据都从大到小排列,生成可一组不太乱的随机数据。
axm=usamap("conus");
states=shaperead("usastatelo.shp",'UseGeoCoords',true);
% 俩州离太远画不开,不要
for i=length(states):-1:1
if states(i).Name=="Alaska"||states(i).Name=="Hawaii"
states(i)=[];
end
end
faceColors=makesymbolspec('Polygon',{'INDEX',[1 numel(states)],'FaceColor',...
polcmap(numel(states))});
usaMap=geoshow(states, 'DisplayType','polygon','SymbolSpec', faceColors);
% 不想去收集数据了,随便生成点随机数
A=sort(rand(numel(states),1));
B=sort(rand(numel(states),1));
C=sort(rand(numel(states),1));
% A=rand(numel(states),1);
% B=rand(numel(states),1);
% C=rand(numel(states),1);
% 调用工具函数生成图例和映射表
[CMapData,CMapHdl]=multiVarMap3(A,B,C,'colorList',1,'pieceNum',8);
% 循环修改每个州颜色
for i=1:length(usaMap.Children)
usaMap.Children(i).FaceColor=[CMapData(i,1,1),CMapData(i,1,2),CMapData(i,1,3)];
end
示例3气泡图绘制
x=1:20;
y=rand(1,20);
sz=rand(1,20);
BC=bubblechart(x,y,sz);
% 不想去收集数据了,随便生成点随机数
A=rand(1,20);
B=rand(1,20);
C=rand(1,20);
% 调用工具函数生成图例和映射表
[CMapData,CMapHdl]=multiVarMap3(A,B,C,'colorList',1,'pieceNum',8);
BC.CData=[CMapData(:,:,1)',CMapData(:,:,2)',CMapData(:,:,3)'];
工具函数完整代码
双变量
function [CMapData,CMapHdl]=multiVarMap2(A,B,varargin)
% @author : slandarer
% gzh : slandarer随笔
help multiVarMap2
% colorList应为4x3大小RGB数组
% [R1,G1,B1;R2,G2,B2;R3,B3,G3;R4,G4,B4]
% 分别对应图例左下,右下,左上,右上的颜色
% 或数值编号1-12
% =========================================================================
% 基本属性提取
obj.arginList={'colorList','pieceNum','varName','title'};
obj.colorList=1;
obj.pieceNum=4;
obj.varName={'var1','var2'};
obj.title='ColorMap';
for i=1:2:(length(varargin)-1)
tid=ismember(obj.arginList,varargin{i});
if any(tid)
obj.(obj.arginList{tid})=varargin{i+1};
end
end
% =========================================================================
% 基础配色表
baseAtla{1}=[233,233,233;201, 90, 90;100,173,191; 86, 64, 71]./255;
baseAtla{2}=[249,244,248;139,182,224;235,130,144;122,123,171]./255;
baseAtla{3}=[243,243,243;140,227,176;231,164,209;124,143,176]./255;
baseAtla{4}=[233,233,233;201,180, 90;154,115,176;129, 76, 51]./255;
baseAtla{5}=[222,222,222; 0,110,175;205, 0, 31; 74, 33, 76]./255;
baseAtla{6}=[233,233,233;108,132,182;116,175,129; 37, 90, 91]./255;
baseAtla{7}=[233,233,233; 89,201,201;191,100,173; 56, 71,149]./255;
baseAtla{8}=[243,243,243; 79,158,195;243,180, 0; 40, 40, 40]./255;
baseAtla{9}=[233,231,242; 78,174,209;223, 78,167; 37,19,139]./255;
baseAtla{10}=[227,228,223;127,135,124; 57,120,164; 20, 72, 83]./255;
baseAtla{11}=[243,232,156;248,161,127;207,102,148; 92, 82,166]./255;
baseAtla{12}=[248,225,152;181,167,124;108,119,149; 65, 92,170]./255;
if numel(obj.colorList)==1,atla=baseAtla{obj.colorList};end
% =========================================================================
% 数据处理及图例色卡数据构建
% nX1=X1(:);nX2=X2(:);
CMapData=getCMap(A,B,atla,obj.pieceNum);
function CMap=getCMap(A,B,atla,n)
nA=(A(:)-min(A(:)))./(max(A(:))-min(A(:)));
nB=(B(:)-min(B(:)))./(max(B(:))-min(B(:)));
nA=(ceil(nA.*n)-1)./(n-1);nB=(ceil(nB.*n)-1)./(n-1);
nA(nA<0)=0;nB(nB<0)=0;
colorAB=nA.*nB.*atla(4,:)+...
nA.*(1-nB).*atla(2,:)+...
(1-nA).*nB.*atla(3,:)+...
(1-nA).*(1-nB).*atla(1,:);
CMap=zeros([size(A),3]);
CMap(:,:,1)=reshape(colorAB(:,1),size(A));
CMap(:,:,2)=reshape(colorAB(:,2),size(A));
CMap(:,:,3)=reshape(colorAB(:,3),size(A));
end
% =========================================================================
% 坐标区域初定位及修饰
ax1=gca;fig=ax1.Parent;
ax1Pos=ax1.Position;
ax2=axes('Parent',fig,'Position',[ax1Pos(1)+3/4*ax1Pos(3),...
ax1Pos(2)+6/9*ax1Pos(4),ax1Pos(3)/4,ax1Pos(4)*2.5/9]);
ax1.UserData=1;
ax2.UserData=2;
ax2.XLim=[min(A(:)),max(A(:))];
ax2.YLim=[min(B(:)),max(B(:))];
ax2.TickDir='out';ax2.TickLength=[0.02,0.025];
ax2.PlotBoxAspectRatio=[1,1,1];
ax2.Layer='top';
ax2.LineWidth=1;
ax2.FontName='Arial';
ax2.FontSize=9;
ax2.Title.String=obj.title;
ax2.Title.FontSize=12;
ax2.XLabel.String=obj.varName{1};
ax2.XLabel.FontSize=11;
ax2.YLabel.String=obj.varName{2};
ax2.YLabel.FontSize=11;
ax2.Toolbar.Visible='off';
hold(ax2,'on');view(2);
% =========================================================================
% 绘制右上角图例
[AA,BB]=meshgrid(linspace(0,1,obj.pieceNum),...
linspace(0,1,obj.pieceNum));
tCMap=getCMap(AA,BB,atla,obj.pieceNum);
legendCMap=zeros([size(AA)+1,3]);
legendCMap(1:end-1,1:end-1,1)=tCMap(:,:,1);
legendCMap(1:end-1,1:end-1,2)=tCMap(:,:,2);
legendCMap(1:end-1,1:end-1,3)=tCMap(:,:,3);
[XX,YY]=meshgrid(linspace(ax2.XLim(1),ax2.XLim(2),obj.pieceNum+1),...
linspace(ax2.YLim(1),ax2.YLim(2),obj.pieceNum+1));
surf(ax2,XX,YY,zeros(size(XX)),'CData',legendCMap,'EdgeColor','none','FaceColor','flat');
% =========================================================================
% 图例拖动回调
set(fig,'WindowButtonDownFcn',@BtnDownFunc, ...
'WindowButtonUpFcn',@BtnUpFunc, ...
'WindowButtonMotionFcn',@BtnMotionFunc)
selectedAx=false;
% 选择图例时的回调
function BtnDownFunc(~,~)
taxes=get(fig,'currentAxes');
if taxes.UserData==2
selectedAx=true;
end
end
% 松开图例时的回调
function BtnUpFunc(~,~)
selectedAx=false;
set(fig,'currentAxes',ax1)
end
% 选择图例并拖动时的回调
function BtnMotionFunc(~,~)
if selectedAx
tpos=get(fig,'CurrentPoint');
figPos=fig.Position;
normPos=tpos./figPos(3:4);
ax2.Position(1:2)=-ax2.Position(3:4)./2+normPos;
end
end
set(fig,'currentAxes',ax1)
CMapHdl=ax2;
end
三变量
function [CMapData,CMapHdl]=multiVarMap3(A,B,C,varargin)
% @author : slandarer
% gzh : slandarer随笔
help multiVarMap3
% colorList应为8x3大小RGB数组
% [R1,G1,B1;R2,G2,B2;R3,B3,G3;R4,G4,B4,...]
% =========================================================================
% 基本属性提取
obj.arginList={'colorList','pieceNum','varName','title'};
obj.colorList=1;
obj.pieceNum=6;
obj.varName={'var1','var2','var3'};
obj.title='ColorMap';
for i=1:2:(length(varargin)-1)
tid=ismember(obj.arginList,varargin{i});
if any(tid)
obj.(obj.arginList{tid})=varargin{i+1};
end
end
% =========================================================================
% 基础配色表
% [0;X;Y;Z;XY;YZ;XZ;XYZ]
baseAtla{1}=[222,222,222;201,90,90;116,175,129;108,132,182;
243,180,0;37,90,91;74,33,76;40 40 40]./255;
baseAtla{2}=[0 0 0;1 0 0;0 1 0;0 0 1;1 1 0;0 1 1;1 0 1;1 1 1];
if numel(obj.colorList)==1,atla=baseAtla{obj.colorList};end
% =========================================================================
% 数据处理及图例色卡数据构建
% nX1=X1(:);nX2=X2(:);
CMapData=getCMap3(A,B,C,atla,obj.pieceNum);
function CMap=getCMap2(A,B,atla,n)
nA=(A(:)-min(A(:)))./(max(A(:))-min(A(:)));
nB=(B(:)-min(B(:)))./(max(B(:))-min(B(:)));
nA=(ceil(nA.*n)-1)./(n-1);
nB=(ceil(nB.*n)-1)./(n-1);
nA(nA<0)=0;nB(nB<0)=0;
colorAB=nA.*nB.*atla(4,:)+...
nA.*(1-nB).*atla(2,:)+...
(1-nA).*nB.*atla(3,:)+...
(1-nA).*(1-nB).*atla(1,:);
CMap=zeros([size(A),3]);
CMap(:,:,1)=reshape(colorAB(:,1),size(A));
CMap(:,:,2)=reshape(colorAB(:,2),size(A));
CMap(:,:,3)=reshape(colorAB(:,3),size(A));
end
function CMap=getCMap3(A,B,C,atla,n)
nA=(A(:)-min(A(:)))./(max(A(:))-min(A(:)));
nB=(B(:)-min(B(:)))./(max(B(:))-min(B(:)));
nC=(C(:)-min(C(:)))./(max(C(:))-min(C(:)));
nA=(ceil(nA.*n)-1)./(n-1);
nB=(ceil(nB.*n)-1)./(n-1);
nC=(ceil(nC.*n)-1)./(n-1);
nA(nA<0)=0;nB(nB<0)=0;nC(nC<0)=0;
colorAB=atla(1,:).*(1-nA).*(1-nB).*(1-nC)+...
atla(2,:).*(nA).*(1-nB).*(1-nC)+...
atla(3,:).*(1-nA).*(nB).*(1-nC)+...
atla(4,:).*(1-nA).*(1-nB).*(nC)+...
atla(5,:).*(nA).*(nB).*(1-nC)+...
atla(6,:).*(1-nA).*(nB).*(nC)+...
atla(7,:).*(nA).*(1-nB).*(nC)+...
atla(7,:).*(nA).*(nB).*(nC);
CMap=zeros([size(A),3]);
CMap(:,:,1)=reshape(colorAB(:,1),size(A));
CMap(:,:,2)=reshape(colorAB(:,2),size(A));
CMap(:,:,3)=reshape(colorAB(:,3),size(A));
end
% =========================================================================
% 坐标区域初定位及修饰
ax1=gca;fig=ax1.Parent;
ax1Pos=ax1.Position;
ax2=axes('Parent',fig,'Position',[ax1Pos(1)+6/9*ax1Pos(3),...
ax1Pos(2)+6/9*ax1Pos(4),ax1Pos(3)*2.5/9,ax1Pos(4)*2.5/9]);
ax1.UserData=1;
ax2.UserData=2;
ax2.XLim=[min(A(:)),max(A(:))];
ax2.YLim=[min(B(:)),max(B(:))];
ax2.ZLim=[min(C(:)),max(C(:))];
ax2.TickDir='out';ax2.TickLength=[0.02,0.025];
ax2.PlotBoxAspectRatio=[1,1,1];
ax2.Layer='top';
ax2.LineWidth=1;
ax2.FontName='Arial';
ax2.FontSize=9;
ax2.Title.String=obj.title;
ax2.Title.FontSize=12;
ax2.XLabel.String=obj.varName{1};
ax2.XLabel.FontSize=11;
ax2.YLabel.String=obj.varName{2};
ax2.YLabel.FontSize=11;
ax2.ZLabel.String=obj.varName{3};
ax2.ZLabel.FontSize=11;
ax2.Toolbar.Visible='off';
hold(ax2,'on');view(3);
% =========================================================================
% 绘制右上角图例
[AA,BB]=meshgrid(linspace(0,1,obj.pieceNum),...
linspace(0,1,obj.pieceNum));
function newMap=expMap(oriMap)
newMap=zeros([size(oriMap,1)+1,size(oriMap,2)+1,3]);
newMap(1:end-1,1:end-1,1)=oriMap(:,:,1);
newMap(1:end-1,1:end-1,2)=oriMap(:,:,2);
newMap(1:end-1,1:end-1,3)=oriMap(:,:,3);
end
[XX,YY]=meshgrid(linspace(ax2.XLim(1),ax2.XLim(2),obj.pieceNum+1),linspace(ax2.YLim(1),ax2.YLim(2),obj.pieceNum+1));
tCMap=getCMap2(AA,BB,atla([1,2,3,5],:),obj.pieceNum);tCMap=expMap(tCMap);
surf(ax2,XX,YY,ones(size(XX)).*ax2.ZLim(1),'CData',tCMap,'EdgeColor','none','FaceColor','flat');
tCMap=getCMap2(AA,BB,atla([4,7,6,8],:),obj.pieceNum);tCMap=expMap(tCMap);
surf(ax2,XX,YY,ones(size(XX)).*ax2.ZLim(2),'CData',tCMap,'EdgeColor','none','FaceColor','flat');
% -------------------------------------------------------------------------
[XX,ZZ]=meshgrid(linspace(ax2.XLim(1),ax2.XLim(2),obj.pieceNum+1),linspace(ax2.ZLim(1),ax2.ZLim(2),obj.pieceNum+1));
tCMap=getCMap2(AA,BB,atla([1,2,4,7],:),obj.pieceNum);tCMap=expMap(tCMap);
surf(ax2,XX,ones(size(XX)).*ax2.YLim(1),ZZ,'CData',tCMap,'EdgeColor','none','FaceColor','flat');
tCMap=getCMap2(AA,BB,atla([3,5,6,8],:),obj.pieceNum);tCMap=expMap(tCMap);
surf(ax2,XX,ones(size(XX)).*ax2.YLim(2),ZZ,'CData',tCMap,'EdgeColor','none','FaceColor','flat');
% -------------------------------------------------------------------------
[YY,ZZ]=meshgrid(linspace(ax2.YLim(1),ax2.YLim(2),obj.pieceNum+1),linspace(ax2.ZLim(1),ax2.ZLim(2),obj.pieceNum+1));
tCMap=getCMap2(AA,BB,atla([1,3,4,6],:),obj.pieceNum);tCMap=expMap(tCMap);
surf(ax2,ones(size(YY)).*ax2.XLim(1),YY,ZZ,'CData',tCMap,'EdgeColor','none','FaceColor','flat');
tCMap=getCMap2(AA,BB,atla([2,5,7,8],:),obj.pieceNum);tCMap=expMap(tCMap);
surf(ax2,ones(size(YY)).*ax2.XLim(2),YY,ZZ,'CData',tCMap,'EdgeColor','none','FaceColor','flat');
% 图例拖动回调
set(fig,'WindowButtonDownFcn',@BtnDownFunc, ...
'WindowButtonUpFcn',@BtnUpFunc, ...
'WindowButtonMotionFcn',@BtnMotionFunc, ...
'KeyPressFcn',@keyFunc)
selectedAx=false;
% 选择图例时的回调
function BtnDownFunc(~,~)
taxes=get(fig,'currentAxes');
if taxes.UserData==2
selectedAx=true;
end
end
% 松开图例时的回调
function BtnUpFunc(~,~)
selectedAx=false;
set(fig,'currentAxes',ax1)
end
% 选择图例并拖动时的回调
function BtnMotionFunc(~,~)
if selectedAx
tpos=get(fig,'CurrentPoint');
figPos=fig.Position;
normPos=tpos./figPos(3:4);
ax2.Position(1:2)=-ax2.Position(3:4)./2+normPos;
end
end
set(fig,'currentAxes',ax1)
CMapHdl=ax2;
end
完
如果有用请留个赞
叭~
未经允许本代码请勿作商业用途,引用的话可以引用我file exchange上的链接,可使用如下格式:
Zhaoxu Liu (2022). bivariate/ternary choropleth map,多变量映射图表 (https://www.mathworks.com/matlabcentral/fileexchange/117165-bivariate-ternary-choropleth-map), MATLAB Central File Exchange. 检索来源 2022/9/5.
若转载请保留以上file exchange链接及本文链接!!!
全部m文件及素材包获取:
链接:https://pan.baidu.com/s/1u57qx5g_eBuNc3ILHvb6IQ?pwd=slan
提取码:slan