使用Java的JFreeChart创建折线图,并保存到word中
在一个项目中需要使用数据创建折线图,不过这个折线图比较丑。
先看效果
下面是代码。
<!-- JFreeChart 依赖 -->
<dependency>
<groupId>org.jfree</groupId>
<artifactId>jfreechart</artifactId>
<version>1.5.3</version>
</dependency>
<dependency>
<groupId>org.jfree</groupId>
<artifactId>jcommon</artifactId>
<version>1.0.24</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.4</version>
</dependency>
这是装数据的实体
package com.example.dome.domain.dto;
public class GnssDataByYueBao {
private String platformName;
private String showName;
//目标椭球平面坐标x
private Double dPlaneX;
//目标椭球平面坐标y
private Double dPlaneY;
//目标椭球平面坐标h
private Double dPlaneH;
//目标椭球平面坐标x方向差值
private Double dVariationPlaneX;
//目标椭球平面坐标y方向差值
private Double dVariationPlaneY;
//目标椭球平面坐标h方向差值
private Double dVariationPlaneH;
private String createTime;
public String getPlatformName() {
return platformName;
}
public void setPlatformName(String platformName) {
this.platformName = platformName;
}
public String getShowName() {
return showName;
}
public void setShowName(String showName) {
this.showName = showName;
}
public Double getdPlaneX() {
return dPlaneX;
}
public void setdPlaneX(Double dPlaneX) {
this.dPlaneX = dPlaneX;
}
public Double getdPlaneY() {
return dPlaneY;
}
public void setdPlaneY(Double dPlaneY) {
this.dPlaneY = dPlaneY;
}
public Double getdPlaneH() {
return dPlaneH;
}
public void setdPlaneH(Double dPlaneH) {
this.dPlaneH = dPlaneH;
}
public Double getdVariationPlaneX() {
return dVariationPlaneX;
}
public void setdVariationPlaneX(Double dVariationPlaneX) {
this.dVariationPlaneX = dVariationPlaneX;
}
public Double getdVariationPlaneY() {
return dVariationPlaneY;
}
public void setdVariationPlaneY(Double dVariationPlaneY) {
this.dVariationPlaneY = dVariationPlaneY;
}
public Double getdVariationPlaneH() {
return dVariationPlaneH;
}
public void setdVariationPlaneH(Double dVariationPlaneH) {
this.dVariationPlaneH = dVariationPlaneH;
}
public String getCreateTime() {
return createTime;
}
public void setCreateTime(String createTime) {
this.createTime = createTime;
}
}
业务逻辑代码
public void MonthlyReportExport(MonthlyReportParam monthlyReportParam) {
//先找下级单位
List<SysDept> lstDept = sysDeptMapper.getChildDept(monthlyReportParam.getDepId());
LinkedHashMap<String, Map<String, List<File>>> map = new LinkedHashMap<>();
// 计算总的循环次数
int totalLoops = 0;
for (SysDept sysDept : lstDept) {
List<PurviewPlatform> purviewPlatformList = purviewPlatformMapper.getPlatFormByDeptId(sysDept.getDeptId());
totalLoops += purviewPlatformList.size();
}
int currentLoop = 0;
// 创建以当前时间日期命名的文件夹
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
String folderName = now.format(formatter);
File dateFolder = new File(wordFileDownLoadPath, folderName);
if (!dateFolder.exists()) {
dateFolder.mkdirs();
}
for (SysDept sysDept : lstDept) {
Map<String, List<File>> platformImages = new HashMap<>();
//循环单位,找路段
List<PurviewPlatform> purviewPlatformList = purviewPlatformMapper.getPlatFormByDeptId(sysDept.getDeptId());
for (PurviewPlatform purviewPlatform : purviewPlatformList) {
// 每个平台有独立的图片列表
List<File> images = new ArrayList<>();
//循环平台找数据
monthlyReportParam.setDepId(sysDept.getDeptId());
monthlyReportParam.setPlatformId(purviewPlatform.getPlatformId());
//位移数据
List<GnssDataByYueBao> gnssDataByYueBaoList = iGnssDataService.getGnssDataByDepIdAndPlatformId(monthlyReportParam);
//倾角/加速度
List<DipAngleByYueBao> dipAngleByYueBaoList = iDipAngleDataService.getDipAngleDataByDepId(monthlyReportParam);
//深部位移
List<ClinometerByYueBao> clinometerDataByDepId = iClinometerDataService.getClinometerDataByDepId(monthlyReportParam);
//裂缝计
List<FissureByYueBao> fissureDataByDepId = iFissureDataService.getFissureDataByDepId(monthlyReportParam);
//浸润线
List<OsmometerByYueBao> osmometerByYueBaoList = iOsmometerDataService.getOsmometerDataByDepId(monthlyReportParam);
//雨量
List<RainByYueBao> rainByYueBaoList = iRainDataService.getRainDataByDepId(monthlyReportParam);
//应力计
List<StressByYueBao> stressByYueBaoList = iStressDataService.getStressDataByDepId(monthlyReportParam);
List<JFreeChart> jFreeChartList = createChartByData(
gnssDataByYueBaoList,
dipAngleByYueBaoList,
clinometerDataByDepId,
fissureDataByDepId,
osmometerByYueBaoList,
rainByYueBaoList,
stressByYueBaoList,
purviewPlatform.getPlatformName());
if (!jFreeChartList.isEmpty()) {
for (JFreeChart jFreeChart : jFreeChartList) {
this.setPieChartFont(jFreeChart);
File file = saveChartAsImage(dateFolder.getPath(), jFreeChart);
images.add(file);
}
}
platformImages.put(purviewPlatform.getPlatformName(), images);
//停50毫秒
try {
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
currentLoop++;
// 计算当前进度百分比
double progress = ((double) currentLoop / totalLoops) * 100;
// 确保进度不超过 100%
progress = Math.min(progress, 100);
smosTaskDevicedataReportMapper.updateProgress(monthlyReportParam.getId(),
new BigDecimal(progress).setScale(2, RoundingMode.HALF_UP).doubleValue());
System.out.println("当前进度: " + progress + "%");
}
map.put(sysDept.getDeptName(), platformImages);
}
//在wordTemplateFilePath 里面多生成一个文件夹,将文件保存到这个文件夹中
insertImagesIntoTemplate(map, wordFileDownLoadPath, monthlyReportParam.getFileName());
//删除全部图片
imagesDelete(dateFolder.getPath());
}
删除生成的图片
private void imagesDelete(String tempImgPath) {
//删除temp文件夹里面的所有文件
File tempFolder = new File(tempImgPath);
File[] files = tempFolder.listFiles();
if (files != null) {
for (File file : files) {
if (file.isFile()) {
file.delete();
}
}
}
if (tempFolder.exists()) {
tempFolder.delete();
}
}
设置字体
private void setPieChartFont(JFreeChart freeChart) {
XYPlot xyChart = (XYPlot) freeChart.getPlot();
xyChart.setNoDataMessageFont(new Font("宋体", Font.BOLD, 20));
TextTitle textTitle = freeChart.getTitle();
textTitle.setFont(new Font("宋体", Font.ITALIC, 25));
LegendTitle legendTitle = freeChart.getLegend();
legendTitle.setItemFont(new Font("宋体", Font.BOLD + Font.ITALIC, 10));
}
将生成的图片插入到word中
private void insertImagesIntoTemplate(LinkedHashMap<String, Map<String, List<File>>> platformImages, String wordTemplateFilePath, String fileName) {
try (XWPFDocument outputDoc = new XWPFDocument()) {
XWPFStyles styles = outputDoc.createStyles();
CTStyle ctStyle = CTStyle.Factory.newInstance();
ctStyle.setStyleId("Heading1");
CTString styleName = CTString.Factory.newInstance();
styleName.setVal("Heading 1");
ctStyle.setName(styleName);
// 设置样式类型为段落样式
ctStyle.setType(STStyleType.PARAGRAPH);
// 设置字体和字号
CTRPr rPr = ctStyle.addNewRPr();
CTFonts fonts = rPr.addNewRFonts();
fonts.setAscii("宋体");
fonts.setEastAsia("宋体");
fonts.setHAnsi("宋体");
rPr.addNewSz().setVal(BigInteger.valueOf(28)); // 字号为 14pt,1pt = 2 个单位
// 将样式添加到文档中
XWPFStyle heading1Style = new XWPFStyle(ctStyle);
styles.addStyle(heading1Style);
int i = 1;
for (Map.Entry<String, Map<String, List<File>>> stringMapEntry : platformImages.entrySet()) {
String key = stringMapEntry.getKey();
// 插入 key 到文档
XWPFParagraph keyPara = outputDoc.createParagraph();
keyPara.setStyle("Heading1");
XWPFRun keyRun = keyPara.createRun();
keyRun.setText(i + "、" + key);
keyRun.setFontFamily("宋体");
keyRun.setFontSize(14);
keyRun.setBold(true);
i++;
for (Map.Entry<String, List<File>> entry : stringMapEntry.getValue().entrySet()) {
String platformName = entry.getKey();
List<File> images = entry.getValue();
XWPFParagraph para = outputDoc.createParagraph();
XWPFRun run = para.createRun();
run.setText(platformName);
run.setFontFamily("宋体");
run.setFontSize(12);
run.setBold(true);
// 插入图片
for (File image : images) {
XWPFParagraph imageParagraph = outputDoc.createParagraph();
XWPFRun imageRun = imageParagraph.createRun();
int format = XWPFDocument.PICTURE_TYPE_PNG;
imageRun.addPicture(Files.newInputStream(image.toPath()), format, image.getName(), Units.toEMU(400), Units.toEMU(300));
Thread.sleep(5);
}
}
}
// 保存生成的文档
try (FileOutputStream out = new FileOutputStream(wordTemplateFilePath + "/" + fileName + ".docx")) {
outputDoc.write(out);
}
} catch (IOException e) {
e.printStackTrace();
} catch (InvalidFormatException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
创建数据集
/*
*根据数据不同创建不同数据集
*/
private List<JFreeChart> createChartByData(List<GnssDataByYueBao> gnssDataList, String platformName) {
List<JFreeChart> chartList = new ArrayList<>();
//生成表面位移的折线图
if (gnssDataList != null && !gnssDataList.isEmpty()) {
Set<Map.Entry<String, List<GnssDataByYueBao>>> entrySet = gnssDataList.stream().collect(groupingBy(GnssDataByYueBao::getShowName)).entrySet();
for (Map.Entry<String, List<GnssDataByYueBao>> stringListEntry : entrySet.stream().sorted(Map.Entry.comparingByKey()).collect(Collectors.toList())) {
TimeSeriesCollection gnssDataset = createGnssDataset(stringListEntry.getValue());
JFreeChart jFreeChart = createChart(stringListEntry.getKey(), gnssDataset, platformName);
chartList.add(jFreeChart);
}
}
return chartList;
}
创建折线图
/**
* 创建折线图
*
* @param chartTitle 图表标题
* @param dataset 数据集
* @param platformName
* @return 折线图对象
*/
private JFreeChart createChart(String chartTitle, TimeSeriesCollection dataset, String platformName) {
JFreeChart lineChart = ChartFactory.createTimeSeriesChart(
platformName + "_" + chartTitle,
"",
"",
dataset,
true,
true,
false
);
// 设置图例位置到图表上方
org.jfree.chart.title.LegendTitle legend = lineChart.getLegend();
if (legend != null) {
legend.setPosition(org.jfree.chart.ui.RectangleEdge.TOP);
legend.setHorizontalAlignment(org.jfree.chart.ui.HorizontalAlignment.CENTER);
}
// 设置 X 轴为日期轴
XYPlot plot = lineChart.getXYPlot();
DateAxis dateAxis = (DateAxis) plot.getDomainAxis();
dateAxis.setDateFormatOverride(new SimpleDateFormat(ChartConfig.DATE_FORMAT));
// 设置图表背景颜色为白色
plot.setBackgroundPaint(Color.WHITE);
// 设置纵轴
NumberAxis valueAxis = (NumberAxis) plot.getRangeAxis();
adjustValueAxisRange(valueAxis, dataset);
// 设置线条粗细
XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer();
BasicStroke stroke = new BasicStroke(ChartConfig.LINE_WIDTH, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
for (int i = 0; i < dataset.getSeriesCount(); i++) {
renderer.setSeriesStroke(i, stroke);
}
//设置折线图颜色
if (chartTitle.contains("表面位移")) {
renderer.setSeriesPaint(0, Color.green);
renderer.setSeriesPaint(1, Color.ORANGE);
renderer.setSeriesPaint(2, Color.blue);
}
if (chartTitle.contains("倾角")) {
renderer.setSeriesPaint(0, Color.green);
renderer.setSeriesPaint(1, Color.ORANGE);
renderer.setSeriesPaint(2, Color.blue);
renderer.setSeriesPaint(3, Color.GRAY);
renderer.setSeriesPaint(4, Color.CYAN);
renderer.setSeriesPaint(5, Color.magenta);
}
if (chartTitle.contains("深部位移")) {
renderer.setSeriesPaint(0, Color.green);
renderer.setSeriesPaint(1, Color.ORANGE);
renderer.setSeriesPaint(2, Color.blue);
renderer.setSeriesPaint(3, Color.magenta);
}
if (chartTitle.contains("裂缝")) {
renderer.setSeriesPaint(0, Color.green);
renderer.setSeriesPaint(1, Color.ORANGE);
renderer.setSeriesPaint(2, Color.blue);
renderer.setSeriesPaint(3, Color.darkGray);
renderer.setSeriesPaint(4, Color.cyan);
renderer.setSeriesPaint(5, Color.magenta);
renderer.setSeriesPaint(6, Color.green);
}
plot.setRenderer(renderer);
return lineChart;
}
/**
* 调整纵轴范围
*
* @param valueAxis 纵轴对象
* @param dataset 数据集
*/
private void adjustValueAxisRange(NumberAxis valueAxis, TimeSeriesCollection dataset) {
// 检查 dataset 是否为 null
if (dataset == null) {
return;
}
double minValue = Double.MAX_VALUE;
double maxValue = Double.MIN_VALUE;
for (int i = 0; i < dataset.getSeriesCount(); i++) {
TimeSeries series = dataset.getSeries(i);
// 检查 series 是否为 null
if (series == null) {
continue;
}
for (int j = 0; j < series.getItemCount(); j++) {
Number valueObj = series.getValue(j);
// 检查 valueObj 是否为 null
if (valueObj != null) {
double value = valueObj.doubleValue();
minValue = Math.min(minValue, value);
maxValue = Math.max(maxValue, value);
}
}
}
// 如果没有有效数据,不调整范围
if (minValue == Double.MAX_VALUE && maxValue == Double.MIN_VALUE) {
return;
}
minValue = minValue - 0.1;
maxValue = maxValue + 0.1;
valueAxis.setRange(minValue, maxValue);
}
/**
* 将图表保存为图片
*
* @param chart 图表对象
*/
private File saveChartAsImage(String folderPath, JFreeChart chart) {
File file = new File(folderPath + "/" + "随机数" + ".jpg");
try {
ChartUtils.saveChartAsJPEG(file, chart, ChartConfig.IMAGE_WIDTH, ChartConfig.IMAGE_HEIGHT);
} catch (IOException e) {
throw new RuntimeException(e);
}
return file;
}