feiyu02
2024-06-28 d2d71a6bc8e445ee60b7be2667676138e277d676
src/main/kotlin/com/flightfeather/uav/common/chart/ChartUtil.kt
@@ -3,17 +3,21 @@
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
/**
 * 图表生成
@@ -22,40 +26,87 @@
 */
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)
@@ -65,7 +116,54 @@
    }
    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 {