笔者之前学习过Matlab,说真的,Matlab用来仿真和绘图真的很方便了。无奈实验部分必须要用java建模,但是java绘图功能就有点弱,因此,笔者决定封装一个jar包,用来调用matlab进行绘图。
本文笔者将要介绍的是如何通过matlab中的deploytool工具,将matlab中常用的函数打包成为一个jar包,然后供java程序调用。
1.编写matlab脚本
笔者总结了常用的matlab绘图函数,具体如下:
1.1 plot
function drawPlotSimple(x, y)
%% 使用matlab函数plot()作图
plot(x, y);
end
function drawPlot(x, y, labels)
%% 使用matlab函数plot()作图
hold on;
grid on;
rows = size(x,1);
for i=1:rows
plot(x(i,:), y(i,:));
end
str = strsplit(labels);
legend(str);
end
1.2 雷达图
function drawRadar(data,lim,labels,legendlabels)
%data是要画图的数据(根据数据的个数来确定雷达图的轴数)
%lim是各指标画图上下限范围
%labels是坐标轴名称
%legendlabels每个数据的标注
labels = strsplit(labels);
legendlabels = strsplit(legendlabels);
hold on;
linestyles = {'k-','r-','b-','g-','c-','m-','y-'};
for i=1:size(data,1)
if i == 1
H = radar(data(i,:),lim,labels,linestyles{i},true);
else
H = radar(data(i,:),lim,labels,linestyles{i},false);
end
for j=1:length(H)-1
set(get(get(H{j},'Annotation'),'LegendInformation'),'IconDisplayStyle','off');
end
end
legend(legendlabels,'Interpreter','latex');
end
function [H] = radar(data,lim,labels,linestyle,flag)
H={};
n=length(data);%维度
adj_data=zeros(n,1);
point=zeros(n,2);
set(gca,'units','normal','pos',[0 0 1 1]);
axis off
axis equal
hold on
theta_last=pi/2;
for i=1:n
theta=2*pi/n*i+pi/2;
H{length(H) + 1} = plot([0,500*cos(theta)],[0,500*sin(theta)],'k-','linewidth',2);
for j=1:5
H{length(H) + 1} = plot([j*100*cos(theta_last),j*100*cos(theta)],[j*100*sin(theta_last),j*100*sin(theta)],'--','linewidth',0.75,'color',[0.5,0.5,0.5]);
end
theta_last=theta;
if data(i)<lim(i,1)
adj_data(i)=0;
elseif data(i)>lim(i,2)
adj_data(i)=500;
else
adj_data(i)=(data(i)-lim(i,1))/(lim(i,2)-lim(i,1))*500;
end
point(i,1:2)=[adj_data(i)*cos(theta);adj_data(i)*sin(theta)];
text_around(510*cos(theta),510*sin(theta),labels{i},theta);
end
for i=1:n
theta=2*pi/n*i+pi/2;
for j=1:5
text_around(j*100*cos(theta),j*100*sin(theta),num2str(lim(i,1)+(lim(i,2)-lim(i,1))/5*j),theta+pi/2,7);
end
end
if(flag == true)
H{length(H) + 1} = fill(point(:,1),point(:,2),[0.9 0.9 0.7]);
alpha(0.7);
end
H{length(H) + 1} = plot([point(:,1);point(1,1)],[point(:,2);point(1,2)],linestyle,'linewidth',1.5);%绘制
texts=findobj(gca,'Type','Text');
minx=-300;
maxx=300;
miny=-300;
maxy=300;
for i=1:length(texts)
rect=get(texts(i),'Extent');
x=rect(1);
y=rect(2);
dx=rect(3);
dy=rect(4);
if x<minx
minx=x;
elseif x+dx>maxx
maxx=x+dx;
end
if y<miny
miny=y;
elseif y+dy>maxy
maxy=y+dy;
end
end
axis([minx-50,maxx+50,miny-20,maxy+20]);
end
function text_around(x,y,txt,theta,fontsize)
if nargin==4
fontsize=10;
end
section=mod(theta+pi/12,2*pi);
if section>pi+pi/6
%上对齐
if section>1.5*pi+pi/6
%左对齐
text(x,y,txt,'Interpreter','latex','VerticalAlignment','cap','HorizontalAlignment','left','Fontsize',fontsize);
elseif section>1.5*pi
%中对齐
text(x,y,txt,'Interpreter','latex','VerticalAlignment','cap','HorizontalAlignment','center','Fontsize',fontsize);
else
%右对齐
text(x,y,txt,'Interpreter','latex','VerticalAlignment','cap','HorizontalAlignment','right','Fontsize',fontsize);
end
elseif section>pi
%中、右对齐
text(x,y,txt,'Interpreter','latex','VerticalAlignment','middle','HorizontalAlignment','right','Fontsize',fontsize);
elseif section>pi/6
%下对齐
if section>0.5*pi+pi/6
%右对齐
text(x,y,txt,'Interpreter','latex','VerticalAlignment','bottom','HorizontalAlignment','right','Fontsize',fontsize);
elseif section>0.5*pi
%中对齐
text(x,y,txt,'Interpreter','latex','VerticalAlignment','bottom','HorizontalAlignment','center','Fontsize',fontsize);
else
%左对齐
text(x,y,txt,'Interpreter','latex','VerticalAlignment','bottom','HorizontalAlignment','left','Fontsize',fontsize);
end
else
%中、左对齐
text(x,y,txt,'Interpreter','latex','VerticalAlignment','middle','HorizontalAlignment','left','Fontsize',fontsize);
end
end
1.3 甘特图
%% 绘制甘特图
function [] = printgante( a,legendStr )
%a中每一列的格式:蒸馏塔/管道 油罐 开始时间 结束时间 原油类型
w=0.5; %横条宽度
%color=['r','g','b','c','m','y','w']; %颜色
color={[0 0 0.5],[1 1 0],[1 0 0],[1 0 1],[0 1 1],[0 1 0],[0 0 1],...
[0.5 0.5 0.5],[0.8 0.6 0.1],[0 0.7 0.4],[0.5 0.6 0.9],[1,1,1]};
str = strsplit(legendStr);
num = length(str);%塔和罐的个数
maxLen = max(a(:,4));
left = 0;
right = ceil(maxLen/10)*10;
step = floor((right - left)/100)*10;
YLoc = 0:10/(num+1):10;
xlabel('T');
axis([left right 0 10]);
set(gca,'Box','on');
set(gca,'XTick',left:step:right); %设置刻度
set(gca,'YTick',YLoc); %设置刻度
set(gca,'YTickLabel',{'' , str{1:6} , ''}); %设置标记
for ii=1:size(a,1)
%计算矩形区域所在位置
x=a(ii,[3 3 4 4]);
y=a(ii,1)*10/(num+1)+[-w/2 w/2 w/2 -w/2];
%使用不同的颜色填充封闭的矩形区域
if a(ii,5)==1
p=patch('xdata',x,'ydata',y,'facecolor',color{1},'edgecolor','k');
elseif a(ii,5)==2
p=patch('xdata',x,'ydata',y,'facecolor',color{2},'edgecolor','k');
elseif a(ii,5)==3
p=patch('xdata',x,'ydata',y,'facecolor',color{3},'edgecolor','k');
elseif a(ii,5)==4
p=patch('xdata',x,'ydata',y,'facecolor',color{4},'edgecolor','k');
elseif a(ii,5)==5
p=patch('xdata',x,'ydata',y,'facecolor',color{5},'edgecolor','k');
elseif a(ii,5)==6
p=patch('xdata',x,'ydata',y,'facecolor',color{6},'edgecolor','k');
elseif a(ii,5)==7
p=patch('xdata',x,'ydata',y,'facecolor',color{7},'edgecolor','k');
elseif a(ii,5)==8
p=patch('xdata',x,'ydata',y,'facecolor',color{8},'edgecolor','k');
elseif a(ii,5)==9
p=patch('xdata',x,'ydata',y,'facecolor',color{9},'edgecolor','k');
elseif a(ii,5)==10
p=patch('xdata',x,'ydata',y,'facecolor',color{10},'edgecolor','k');
elseif a(ii,5)==11
p=patch('xdata',x,'ydata',y,'facecolor',color{11},'edgecolor','k');
elseif a(ii,5)==0
p=patch('xdata',x,'ydata',y,'facecolor',color{length(color)},'edgecolor','k');
end
%设置矩形区域的文字
text(a(ii,3)+0.5,a(ii,1)*10/(num+1),[num2str(a(ii,2))]);
end
end
2. 使用deploytool打包
在控制台输入deploytool -> Liberary Compiler -> java package -> EXPORTED:drawplot.m -> 修改类名 -> package,文件夹下面会多出刚才的工程,里面有我们要要用的jar包。
3. 编写java代码调用jar包
将上述生成好的jar包加入到build path中,同时将 javabuilder.jar (在matlab安装目录\toolbox\javabuilder\jar\jarbuilder.jar) 加入到java项目的build path中。编写函数,测试是否能够正常绘图。
3.1 测试plot函数
public void plotTest() {
try{
PlotUtils plotUtil = new PlotUtils();
double[] x = new double[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
double[] y = new double[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
MWNumericArray B = MWNumericArray.newSparse(x, MWClassID.DOUBLE);
MWNumericArray C = MWNumericArray.newSparse(y, MWClassID.DOUBLE);
plotUtil.drawPlotSimple(B, C);
plotUtil.waitForFigures();
MWArray.disposeArray(B);
MWArray.disposeArray(C);
} catch (Exception e) {
System.out.println("Matlab plot module error.");
System.exit(1);
}
}
public void plotTest2() {
try {
PlotUtils plotUtil = new PlotUtils();
double[][] x = new double[][] { { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 },
{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 } };
double[][] y = new double[][] { { 5, 6, 1, 4, 5, 6, 7, 8, 9, 10 }, { 1, 8, 3, 4, 5, 5, 7, 8, 3, 10 },
{ 9, 2, 3, 4, 10, 6, 7, 8, 9, 6 }, { 1, 2, 3, 10, 5, 6, 7, 8, 9, 5 } };
MWNumericArray B = MWNumericArray.newSparse(x, MWClassID.DOUBLE);
MWNumericArray C = MWNumericArray.newSparse(y, MWClassID.DOUBLE);
plotUtil.drawPlot(B, C, "line1 line2 line3 line4");
plotUtil.waitForFigures();
MWArray.disposeArray(B);
MWArray.disposeArray(C);
} catch (Exception e) {
System.out.println("Matlab plot module error.");
System.exit(1);
}
}
3.2 测试雷达图函数
public void plotRadar() {
try {
PlotUtils plotUtil = new PlotUtils();
double[][] data = new double[][] { { 1, 8, 5, 6 }, { 2, 5, 7, 1 }, { 5, 3, 9, 4 } };
double[][] lim = new double[][] { { 0, 10 }, { 0, 10 }, { 0, 10 }, { 0, 10 } };
String label = "metrics1 metrics2 metrics3 metrics4";
String legend = "data1 data2 data3";
MWNumericArray B = MWNumericArray.newSparse(data, MWClassID.DOUBLE);
MWNumericArray C = MWNumericArray.newSparse(lim, MWClassID.DOUBLE);
plotUtil.drawRadar(B, C, label, legend);
plotUtil.waitForFigures();
MWArray.disposeArray(B);
MWArray.disposeArray(C);
} catch (Exception e) {
System.out.println("Matlab plot module error.");
System.exit(1);
}
}
3.3 测试甘特图函数
public void ganteTest() {
try {
PlotUtils plotUtil = new PlotUtils();
String labels = "位置1 位置2 位置3 位置4 位置5 位置6";
// data的格式:
// 事件发生所在的位置 事件标注 事件开始时间 事件结束时间 事件类型(不同的颜色)
double[][] data= new double[][] { { 2.0, 2.0, 0.0, 47.9, 1.0 }, { 1.0, 5.0, 0.0, 100.0, 3.0 },
{ 3.0, 6.0, 0.0, 60.0, 5.0 }, { 4.0, 9.0, 0.0, 35.2, 7.0 }, { 6.0, 8.0, 0.0, 50.0, 6.0 },
{ 5.0, 4.0, 0.0, 8.0, 8.0 }, { 5.0, 11.0, 8.0, 25.5, 8.0 }, { 5.0, 0.0, 25.5, 35.2, 0.0 },
{ 4.0, 10.0, 35.2, 65.6, 8.0 }, { 5.0, 0.0, 35.2, 47.9, 0.0 }, { 2.0, 3.0, 47.9, 125.7, 2.0 },
{ 5.0, 0.0, 47.9, 60.0, 0.0 }, { 6.0, 8.0, 50.0, 86.0, 6.0 }, { 3.0, 7.0, 60.0, 128.0, 4.0 },
{ 5.0, 0.0, 60.0, 65.6, 0.0 }, { 4.0, 4.0, 65.6, 81.6, 8.0 }, { 5.0, 10.0, 65.6, 78.0, 4.0 },
{ 5.0, 0.0, 78.0, 81.6, 0.0 }, { 4.0, 11.0, 81.6, 104.0, 8.0 }, { 5.0, 4.0, 81.6, 112.9, 8.0 },
{ 6.0, 9.0, 86.0, 149.8, 11.0 }, { 1.0, 10.0, 100.0, 149.6, 4.0 },
{ 5.0, 11.0, 112.9, 129.9, 10.0 }, { 4.0, 4.0, 116.9, 156.9, 8.0 }, { 2.0, 8.0, 125.7, 179.6, 6.0 },
{ 5.0, 3.0, 129.9, 141.5, 4.0 }, { 3.0, 11.0, 133.9, 201.9, 10.0 }, { 5.0, 5.0, 141.5, 179.0, 8.0 },
{ 1.0, 3.0, 149.6, 196.0, 4.0 }, { 6.0, 2.0, 149.8, 169.5, 11.0 }, { 6.0, 6.0, 169.5, 182.8, 11.0 },
{ 5.0, 4.0, 179.0, 190.5, 8.0 }, { 2.0, 9.0, 179.6, 259.8, 11.0 }, { 6.0, 1.0, 182.8, 200.1, 11.0 },
{ 4.0, 5.0, 183.0, 231.0, 8.0 }, { 5.0, 8.0, 190.5, 214.5, 10.0 }, { 1.0, 4.0, 196.0, 242.0, 8.0 },
{ 6.0, 7.0, 200.1, 219.7, 11.0 }, { 5.0, 3.0, 214.5, 219.5, 8.0 }, { 3.0, 8.0, 218.5, 314.5, 10.0 },
{ 5.0, 11.0, 219.5, 244.5, 9.0 }, { 4.0, 3.0, 231.0, 239.0, 8.0 }, { 5.0, 10.0, 244.5, 252.5, 8.0 },
{ 4.0, 11.0, 248.5, 280.5, 9.0 }, { 5.0, 3.0, 252.5, 268.0, 9.0 }, { 1.0, 10.0, 256.5, 296.5, 8.0 },
{ 2.0, 2.0, 259.8, 304.1, 11.0 }, { 5.0, 4.0, 268.0, 282.5, 8.0 }, { 4.0, 3.0, 280.5, 300.3, 9.0 },
{ 5.0, 11.0, 282.5, 300.5, 9.0 }, { 1.0, 4.0, 296.5, 354.5, 8.0 }, { 5.0, 3.0, 300.5, 302.6, 10.0 },
{ 5.0, 5.0, 302.6, 327.6, 9.0 }, { 2.0, 6.0, 304.1, 334.0, 11.0 }, { 4.0, 11.0, 304.5, 333.3, 9.0 },
{ 3.0, 3.0, 314.5, 322.7, 10.0 }, { 5.0, 3.0, 327.6, 337.7, 8.0 }, { 4.0, 5.0, 333.3, 365.3, 9.0 },
{ 2.0, 1.0, 334.0, 372.9, 11.0 }, { 5.0, 8.0, 337.7, 372.7, 9.0 }, { 1.0, 3.0, 354.5, 386.7, 8.0 },
{ 2.0, 7.0, 372.9, 416.9, 11.0 }, { 4.0, 8.0, 376.7, 421.5, 9.0 }, { 5.0, 5.0, 372.7, 380.1, 9.0 },
{ 4.0, 5.0, 421.5, 436.3, 9.0 } };
int size = data.length;//事件个数
// java数组转换成matlab矩阵
MWNumericArray A = MWNumericArray.newSparse(data, MWClassID.DOUBLE);
plotUtil.printgante(A, labels);
plotUtil.waitForFigures();// 等待绘图完成
MWArray.disposeArray(A);
} catch (Exception e) {
System.out.println("Matlab plot module error.");
System.exit(1);
}
}
doc文件夹下还生成了html版的javadoc帮助文档,是不是很棒。
当输入参数为字符串数组时,可以通过传入字符串的方式传入,而不需要传入字符串数组,然后再在matlab程序中通过其strsplit(str)方法,将字符串分割为元胞数组(Matlab中的数据类型)。
最后,拿出笔者最喜欢的单例模式,对上述的方法进行封装。
package com.sim.ui;
import com.mathworks.toolbox.javabuilder.MWArray;
import com.mathworks.toolbox.javabuilder.MWClassID;
import com.mathworks.toolbox.javabuilder.MWException;
import com.mathworks.toolbox.javabuilder.MWNumericArray;
import plot.PlotUtils;
public class MatlabPlotHelper {
private static MatlabPlotHelper _instance;
private PlotUtils plotUtil = null;
private MatlabPlotHelper() {
try {
plotUtil = new PlotUtils();
} catch (MWException e) {
System.out.println("创建绘图对象失败");
}
}
public static MatlabPlotHelper getInstance() {
if (_instance == null) {
_instance = new MatlabPlotHelper();
}
return _instance;
}
/**
* 绘制雷达图
*
* @param data
* @param lim
* @param label
* @param legend
*/
public void radar(double[][] data, double[][] lim, String label, String legend) {
try {
MWNumericArray B = MWNumericArray.newSparse(data, MWClassID.DOUBLE);
MWNumericArray C = MWNumericArray.newSparse(lim, MWClassID.DOUBLE);
plotUtil.drawRadar(B, C, label, legend);
plotUtil.waitForFigures();
MWArray.disposeArray(B);
MWArray.disposeArray(C);
} catch (Exception e) {
System.out.println("Matlab plot module error.");
System.exit(1);
}
}
/**
* 普通绘图,绘制单条线
*
* @param x
* @param y
*/
public void plot(double[] x, double[] y) {
try {
MWNumericArray B = MWNumericArray.newSparse(x, MWClassID.DOUBLE);
MWNumericArray C = MWNumericArray.newSparse(y, MWClassID.DOUBLE);
plotUtil.drawPlotSimple(B, C);
plotUtil.waitForFigures();
MWArray.disposeArray(B);
MWArray.disposeArray(C);
} catch (Exception e) {
System.out.println("Matlab plot module error.");
System.exit(1);
}
}
/**
* 绘制多条线
*
* @param x
* @param y
*/
public void plot2(double[][] x, double[][] y, String labels) {
try {
MWNumericArray B = MWNumericArray.newSparse(x, MWClassID.DOUBLE);
MWNumericArray C = MWNumericArray.newSparse(y, MWClassID.DOUBLE);
plotUtil.drawPlot(B, C, labels);
plotUtil.waitForFigures();
MWArray.disposeArray(B);
MWArray.disposeArray(C);
} catch (Exception e) {
System.out.println("Matlab plot module error.");
System.exit(1);
}
}
/**
* 绘制甘特图
*
* @param labels
* @param data
*/
public void gante(String labels, double[][] data) {
try {
// java数组转换成matlab矩阵
MWNumericArray A = MWNumericArray.newSparse(data, MWClassID.DOUBLE);
plotUtil.printgante(A, labels);
plotUtil.waitForFigures();// 等待绘图完成
MWArray.disposeArray(A);
} catch (Exception e) {
System.out.println("Matlab plot module error.");
System.exit(1);
}
}
}
4. 错误及解决方案
4.1 缺少 javabuilder.jar
将生成的 xxx.jar 和 javabuilder.jar (在matlab安装目录\toolbox\javabuilder\jar\jarbuilder.jar) 加入到java项目的build path中。注意,若没有加入javabuilder.jar,在创建java对象时,则会出现下面的错误提示。
4.2 UnsatisfiedLinkError
Exception in thread "Scheduler" java.lang.UnsatisfiedLinkError: Failed to find the required library mclmcrrt9_2.dll on java.library.path.
This library is typically installed along with MATLAB or the MATLAB Runtime. Its absence may indicate an issue with that installation or
the current path configuration, or a mismatch with the architecture of the Java interpreter on the path.
MATLAB Runtime version this component is attempting to use: 9.2.
Java interpreter architecture: win32.
at com.mathworks.toolbox.javabuilder.internal.MCRConfiguration$ProxyLibraryDir.get(MCRConfiguration.java:324)
at com.mathworks.toolbox.javabuilder.internal.MCRConfiguration$ProxyLibraryDir.<clinit>(MCRConfiguration.java:334)
at com.mathworks.toolbox.javabuilder.internal.MCRConfiguration.getProxyLibraryDir(MCRConfiguration.java:339)
at com.mathworks.toolbox.javabuilder.internal.MCRConfiguration$MCRRoot.get(MCRConfiguration.java:64)
at com.mathworks.toolbox.javabuilder.internal.MCRConfiguration$MCRRoot.<clinit>(MCRConfiguration.java:76)
at com.mathworks.toolbox.javabuilder.internal.MCRConfiguration.getMCRRoot(MCRConfiguration.java:81)
at com.mathworks.toolbox.javabuilder.internal.MCRConfiguration$ModuleDir.<clinit>(MCRConfiguration.java:53)
at com.mathworks.toolbox.javabuilder.internal.MCRConfiguration.getModuleDir(MCRConfiguration.java:58)
at com.mathworks.toolbox.javabuilder.internal.MWMCR.<clinit>(MWMCR.java:1699)
at drawplot.DrawplotMCRFactory.newInstance(DrawplotMCRFactory.java:46)
at drawplot.DrawplotMCRFactory.newInstance(DrawplotMCRFactory.java:57)
at drawplot.PlotUtil.<init>(PlotUtil.java:82)
at com.sim.operation.Operation.plotSchedule(Operation.java:518)
at com.sim.twostep.SimulationScheduler.run(SimulationScheduler.java:877)
可能原因1:缺少MCR
MCR是干什么的?下面看一下大佬给出的回答:
利用MATLAB GUI编程所得的exe软件,想在其他电脑上使用时,如果对方电脑没有安装MATLAB,是不能使用的,因为对方电脑上没有MATLAB的编译器。但我们也没有必要为了运行GUI程序而去安装MATLAB,只需要安装MCR编译器就行。
>> mcrinstaller
ans =
'D:\MATLAB\R2017a\toolbox\compiler\deploy\win64\MCRInstaller.exe'
找到上面指定的MCRInstaller.exe所在的路径。如果您还没有安装Matlab,并且也不想安装,那么,您可以通过到Matlab官网找到MCR的安装程序,最小化安装Matlab的运行环境。需要jar包,或者您在操作过程中遇到了莫名其妙的问题,可以在评论区留言。
可能原因2:Java解释器的架构不匹配
笔者的Matlab的版本是2017a,64位,上述错误的解释:此库通常与matlab或matlab运行时一起安装。其缺失可能表明该安装存在问题,或当前路径配置,或者与路径上的Java解释器的架构不匹配。Matlab运行时版本:9.2。
问题分析:使用version -java可以看到,jdk的版本为1.7,为什么是1.7呢?笔者装的jdk命名是1.8,原来matlab自带了一个jdk,而2017a里面内置的jdk版本是1.7。注意,我们这里需要jdk的架构要和matlab的架构保持一致,即matlab如果是32位,那么jdk最好也是32位,matlab如果是64位,那么jdk最好也是64位。如果不呢?就会导致所谓的java解释器架构不匹配错误吧。从上面的错误不难看出,由于jdk架构为32位,而matlab版本为64位,因此,导致了错误。解决方案:可以通过设置系统环境变量的方式更改matlab的jdk配置,让matlab使用我们需要使用的jdk版本。即设置一个系统环境变量 MATLAB_JAVA : C:\Program Files\Java\jdk1.8.0_131\jre [ 自己安装的jdk中jre所在的位置 ]。
可能原因3:你欠eclipse一个重启
重启eclipse即可。
温馨提示,生成此jar所用的jdk版本为1.8,如果您的jdk版本不同,可能会在使用时遇到一些问题,欢迎评论区留言交流。