| | |
| | | import org.jfree.chart.ChartFactory |
| | | import org.jfree.chart.ChartUtils |
| | | import org.jfree.chart.JFreeChart |
| | | import org.jfree.chart.labels.StandardCategoryItemLabelGenerator |
| | | import org.jfree.chart.axis.NumberAxis |
| | | import org.jfree.chart.axis.NumberTickUnit |
| | | import org.jfree.chart.labels.StandardCategorySeriesLabelGenerator |
| | | import org.jfree.chart.plot.CategoryPlot |
| | | import org.jfree.chart.plot.PlotOrientation |
| | | import org.jfree.chart.renderer.category.LineAndShapeRenderer |
| | | import org.jfree.chart.title.TextTitle |
| | | import org.jfree.data.category.DefaultCategoryDataset |
| | | import java.awt.BasicStroke |
| | | import java.awt.Color |
| | | import java.awt.Font |
| | | import java.awt.Paint |
| | | import java.io.ByteArrayOutputStream |
| | | import java.text.DecimalFormat |
| | | import kotlin.math.max |
| | | import kotlin.math.min |
| | | |
| | | /** |
| | | * 图表生成 |
| | |
| | | */ |
| | | object ChartUtil { |
| | | |
| | | data class ChartValue(val x: String?, val y: Number) |
| | | private const val FONT_NAME = "SimHei" |
| | | private val defaultColors = listOf( |
| | | Color(191, 0, 0), |
| | | Color(181, 125, 69), |
| | | Color(236, 204, 6), |
| | | Color(84, 151, 57), |
| | | Color(13, 221, 151), |
| | | Color(57, 75, 151), |
| | | Color(115, 51, 206), |
| | | ) |
| | | |
| | | fun line(title: String, dataset: DefaultCategoryDataset): JFreeChart { |
| | | val line = ChartFactory.createLineChart(title, "时间", "浓度", dataset) |
| | | // line.categoryPlot.domainAxis.labelFont = Font("宋体", Font.PLAIN, 12) |
| | | // line.categoryPlot.rangeAxis.labelFont = Font("宋体", Font.PLAIN, 12) |
| | | // 图表数据值坐标 |
| | | data class ChartValue(val x: String?, val y: Double) |
| | | // 图表数据集 |
| | | data class ChartDataset(val dataset: DefaultCategoryDataset, val minValue: Double, val maxValue: Double) |
| | | |
| | | /** |
| | | * 创建折线图 |
| | | */ |
| | | fun line(title: String, chartDatasetList: List<ChartDataset>): JFreeChart? { |
| | | if (chartDatasetList.isEmpty()) return null |
| | | |
| | | val line = ChartFactory.createLineChart( |
| | | title, "时间", "浓度(μg/m3)", |
| | | null, PlotOrientation.VERTICAL, true, false, false |
| | | ) |
| | | // val line = ChartFactory.createLineChart( |
| | | // title, "", "浓度(μg/m³)", |
| | | // null, PlotOrientation.VERTICAL, true, false, false |
| | | // ) |
| | | var minValue = chartDatasetList[0].minValue |
| | | var maxValue = chartDatasetList[0].maxValue |
| | | val datasetList = mutableListOf<DefaultCategoryDataset>() |
| | | chartDatasetList.forEach { |
| | | minValue = min(minValue, it.minValue) |
| | | maxValue = max(maxValue, it.maxValue) |
| | | datasetList.add(it.dataset) |
| | | } |
| | | val plot = line.categoryPlot |
| | | setLine(line) |
| | | setRenderer(plot, datasetList) |
| | | setX(plot) |
| | | setY(plot, "浓度(μg/m3)", minValue, maxValue) |
| | | return line |
| | | } |
| | | |
| | | fun lineToByteArray(title: String, dataset: DefaultCategoryDataset):ByteArray { |
| | | /** |
| | | * 新建折线图并转换为byte数组 |
| | | */ |
| | | fun lineToByteArray(title: String, dataset: List<ChartDataset>): ByteArray { |
| | | val chart = line(title, dataset) |
| | | val output = ByteArrayOutputStream() |
| | | ChartUtils.writeChartAsPNG(output, chart, 600, 400) |
| | | ChartUtils.writeChartAsJPEG(output, chart, 1080, 607) |
| | | val byteArray = output.toByteArray() |
| | | output.flush() |
| | | output.close() |
| | | return byteArray |
| | | } |
| | | |
| | | fun newDataset(dataList:List<ChartValue>, seriesName:String): DefaultCategoryDataset { |
| | | /** |
| | | * 新建图表数据集 |
| | | */ |
| | | fun newDataset(dataList: List<ChartValue>, seriesName: String): ChartDataset? { |
| | | if (dataList.isEmpty()) return null |
| | | val dataset = DefaultCategoryDataset() |
| | | var minValue = dataList[0].y |
| | | var maxValue = minValue |
| | | dataList.forEach { |
| | | dataset.setValue(it.y, seriesName, it.x) |
| | | minValue = min(minValue, it.y) |
| | | maxValue = max(maxValue, it.y) |
| | | } |
| | | return dataset |
| | | return ChartDataset(dataset, minValue, maxValue) |
| | | } |
| | | |
| | | /** |
| | | * 设置折线图样式 |
| | | */ |
| | | private fun setLine(chart: JFreeChart) { |
| | | chart.legend.itemFont = Font("SimHei", Font.PLAIN, 16) |
| | | chart.title.font = Font("SimHei", Font.BOLD, 20) |
| | | chart.legend.itemFont = Font(FONT_NAME, Font.PLAIN, 16) |
| | | chart.title.font = Font(FONT_NAME, Font.BOLD, 20) |
| | | chart.categoryPlot.apply { |
| | | backgroundPaint = Color(255, 255, 255) |
| | | rangeGridlinePaint = Color(200, 200, 200) |
| | |
| | | } |
| | | |
| | | private fun setRenderer(plot:CategoryPlot, datasetList:List<DefaultCategoryDataset>) { |
| | | val renderer = newBasicRenderer(paint = Color(191, 0, 0)) |
| | | datasetList.forEachIndexed { index, v -> |
| | | val renderer = newBasicRenderer(paint = defaultColors[index % defaultColors.size]) |
| | | plot.setDataset(index, v) |
| | | plot.setRenderer(index, renderer) |
| | | } |
| | | |
| | | } |
| | | |
| | | private fun setX(plot: CategoryPlot) { |
| | | plot.domainAxis.apply { |
| | | // 设置x轴每个刻度的字体 |
| | | tickLabelFont = Font(FONT_NAME, Font.BOLD, 16) |
| | | // 设置x轴标签的字体 |
| | | labelFont = Font(FONT_NAME, Font.BOLD, 20) |
| | | // 设置x轴轴线是否显示 |
| | | isAxisLineVisible = false |
| | | // 设置x轴刻度是否显示 |
| | | isTickMarksVisible = false |
| | | |
| | | upperMargin = .0 |
| | | lowerMargin = .0 |
| | | } |
| | | } |
| | | |
| | | private fun setY(plot: CategoryPlot, label:String, minValue: Double, maxValue: Double) { |
| | | val tickUnit = (maxValue - minValue) / 10 |
| | | val axis1 = NumberAxis().apply { |
| | | this.label = label |
| | | // 刻度展示格式化 |
| | | numberFormatOverride = DecimalFormat("0.0") |
| | | if (tickUnit != .0) { |
| | | // 取消自动分配间距 |
| | | isAutoTickUnitSelection = false |
| | | // 设置间隔距离 |
| | | setTickUnit(NumberTickUnit(tickUnit)) |
| | | // 设置显示范围 |
| | | setRange(minValue, maxValue) |
| | | } |
| | | // 设置y轴每个刻度的字体 |
| | | tickLabelFont = Font(FONT_NAME, Font.BOLD, 16) |
| | | // 设置y轴标签的字体 |
| | | labelFont = Font(FONT_NAME, Font.BOLD, 20) |
| | | // 设置y轴轴线不显示 |
| | | isAxisLineVisible = false |
| | | // 设置y轴刻度不显示 |
| | | isTickMarksVisible = false |
| | | } |
| | | plot.rangeAxis = axis1 |
| | | } |
| | | |
| | | private fun newBasicRenderer(series:Int = 0, paint:Paint): LineAndShapeRenderer { |