java - Jfreechart 将文本/注释/标签放置在图表轮廓上的所需位置和线条上

标签 java text charts position jfreechart

我正在尝试重新创建一个类似于下图中的图表:

desired chart

目前,我设法得到了这个:

work in progress chart

如何添加缺少的元素,例如 setLabelLocation() 方法允许的边界之外的轴标签。另外如何在显示的位置上添加值标签?

对于最后一条绿色垂直线,该线似乎被虚线轮廓覆盖,这里我尝试在图表的右侧绘制辅助轴,但该线仍在背景中,有任何想法可以解决这个问题?

这是尝试的代码:

package javaapplication;

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Stroke;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.Locale;
import javax.swing.JFrame;
import javax.swing.JPanel;
import org.jfree.chart.ChartColor;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.annotations.XYLineAnnotation;
import org.jfree.chart.axis.AxisLabelLocation;
import org.jfree.chart.axis.AxisLocation;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.ValueMarker;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.chart.title.TextTitle;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.ui.HorizontalAlignment;
import org.jfree.ui.Layer;
import org.jfree.ui.RectangleEdge;
import org.jfree.ui.RectangleInsets;

public class JavaApplication { 
    public static void main(String[] args){
        PlotWindow pw = new PlotWindow();
        pw.setVisible(true);
    }
    
}

public class PlotWindow extends JFrame
{
    final double t_init = 0;
    final double step = 0.1;
    final double t_fin = 10;
    int lengthValues;
    double [] yValues;
    double [] xValues;

    //params
    double Fmax = 5;
    double Tau = 3;
    double alpha = 3;
    double deltaX;
    
            
    public PlotWindow()
    {
      super("Test JFreechart"); 
      this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      this.setLocationRelativeTo(null);
      
      // x y values
      this.lengthValues = (int) ((this.t_fin-t_init)/this.step) + 1;
      this.setTimeValues();
      this.computeDeltaX();
      this.setForceValues();
      
      
      JPanel jp = createChartPanel();
      jp.setPreferredSize(new java.awt.Dimension(546, 447));
      this.add(jp, BorderLayout.CENTER);

      this.pack();
      this.setPreferredSize(new Dimension(546, 447));
    }
    
    private void setTimeValues()
    {
        this.xValues = new double[this.lengthValues];
        for(int i = 0; i < this.lengthValues; ++i)
        {
          this.xValues[i] = this.t_init + i*this.step;    
        }
    }
   
    private void computeDeltaX()
    {
        this.deltaX = Math.sqrt(-this.Tau*Math.log(this.alpha/(1+this.alpha)));
    }
    
    private void setForceValues()
    {
        this.yValues = new double[lengthValues];
        for(int i = 0; i < lengthValues; ++i)
        {
          double A = this.Fmax*(1+1/this.alpha);
          double B = 1-Math.exp(-Math.pow(this.xValues[i]-this.deltaX, 2)/this.Tau);
          this.yValues[i] = A*B-this.Fmax/this.alpha;      
        }
    }
    
    private XYSeriesCollection createDataset() 
    {
        boolean autoSort = false;
        boolean allowDuplicateXValues = false;
        XYSeriesCollection dataset = new XYSeriesCollection();
        XYSeries series1 = new XYSeries("",autoSort,allowDuplicateXValues);
        
        for(int i = 0; i < lengthValues; ++i)
        {
            series1.add(this.xValues[i], this.yValues[i]);
        }
        dataset.addSeries(series1);
        return dataset;
    }
    
    
    private JPanel createChartPanel()
    {
        String chartTitle = "Wetting balance curve";
        String xAxisLabel = "";
        String yAxisLabel = "";
        //String yAxisLabel = "Fr(mN)";

        XYSeriesCollection dataset = createDataset();
                
        JFreeChart chart = ChartFactory.createXYLineChart(chartTitle,
                xAxisLabel, yAxisLabel, dataset,PlotOrientation.VERTICAL,false,true,false);

        XYPlot plot = chart.getXYPlot();
        
        
        //title
        TextTitle tt = new TextTitle();
        tt.setText("C:\\MENISCO ST60\\Mesures\\22-5912-100.PM1");
        tt.setPaint(Color.BLUE);
        tt.setFont( new java.awt.Font( "SansSerif", java.awt.Font.PLAIN, 10 ) );
        chart.setTitle(tt);
        //empty subtitle (offset for the title)
        TextTitle tts = new TextTitle();
        tts.setText(" ");
        tts.setPaint(Color.BLUE);
        tts.setFont( new java.awt.Font( "SansSerif", java.awt.Font.PLAIN, 6 ) );
        chart.addSubtitle(tts);

        // norm subtitle
        //TextTitle normtt = new TextTitle("          " + "Norme : J-STD-002E");
        TextTitle normtt = new TextTitle("    " + "Norme : J-STD-002E");
        normtt.setFont(new Font("SansSerif", Font.BOLD, 12));
        normtt.setPosition(RectangleEdge.BOTTOM);
        normtt.setPaint(Color.BLACK);
        normtt.setHorizontalAlignment(HorizontalAlignment.LEFT);
        chart.addSubtitle(normtt);
        
        // fmoy subtitle
        TextTitle fmoytt = new TextTitle("    " + "Force moyenne à 0.900 S: 0.25mN");
        fmoytt.setFont(new Font("SansSerif", Font.PLAIN, 10));
        fmoytt.setPosition(RectangleEdge.BOTTOM);
        fmoytt.setPaint(Color.BLUE);
        fmoytt.setHorizontalAlignment(HorizontalAlignment.LEFT);
        chart.addSubtitle(fmoytt);
              
        // axis
        //domain axis
        plot.getDomainAxis().setLowerMargin(0.0);
        plot.getDomainAxis().setUpperMargin(0.0);
        NumberAxis domain = (NumberAxis) plot.getDomainAxis();
        NumberFormat formatterd = DecimalFormat.getInstance();
        formatterd.setMinimumFractionDigits(0);
        domain.setNumberFormatOverride(formatterd);
        domain.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
        domain.setTickMarksVisible(false);
        plot.getDomainAxis().setAxisLineVisible(false);
        
        //range axis
        plot.getRangeAxis().setLabelAngle(Math.PI/2);
        plot.getRangeAxis().setLabelLocation(AxisLabelLocation.HIGH_END);
        NumberAxis range = (NumberAxis) plot.getRangeAxis();
        NumberFormat formatter = DecimalFormat.getInstance(Locale.ENGLISH);
        formatter.setMinimumFractionDigits(2);
        range.setNumberFormatOverride(formatter);
        plot.getRangeAxis().setAxisLineStroke(new BasicStroke(1.5f));
        plot.getRangeAxis().setAxisLinePaint(Color.BLUE);
        plot.getRangeAxis().setTickMarksVisible(true);
        plot.getRangeAxis().setTickMarkPaint(Color.BLACK);
        plot.getRangeAxis().setTickMarkStroke(new BasicStroke(1.5f));
        float lg = plot.getRangeAxis().getTickMarkOutsideLength();
        plot.getRangeAxis().setTickMarkInsideLength(lg);
        plot.getRangeAxis().setRange(-3, 6);

        // background;gridline;outline 
        plot.setAxisOffset(new RectangleInsets(0, 0, 0, 0));
        plot.setDomainGridlinePaint(Color.DARK_GRAY);
        plot.setRangeGridlinePaint(Color.DARK_GRAY);
        plot.setBackgroundPaint(Color.white);
        plot.setOutlineStroke(plot.getDomainGridlineStroke()); //dashed outline
        plot.setOutlinePaint(Color.DARK_GRAY);
        XYItemRenderer  renderer =  plot.getRenderer();
        renderer.setSeriesPaint(0, ChartColor.VERY_DARK_GREEN); // set green color to the xyline

        // vertical lines
        ValueMarker marker0 = new ValueMarker(0.2,Color.MAGENTA,new BasicStroke(1.5f));  // position is the value on the axis
        ValueMarker marker1 = new ValueMarker(1,Color.MAGENTA,new BasicStroke(1.5f));
        ValueMarker marker2 = new ValueMarker(4,Color.GREEN,new BasicStroke(1.5f));  // position is the value on the axis
        plot.addDomainMarker(marker0, Layer.FOREGROUND);
        plot.addDomainMarker(marker1,Layer.FOREGROUND);
        plot.addDomainMarker(marker2,Layer.FOREGROUND);

        //horizontal lines
        XYLineAnnotation line = new XYLineAnnotation(0, 4, 10, 4, new BasicStroke(2.0f), Color.green);
        plot.addAnnotation(line);
        XYLineAnnotation line0 = new XYLineAnnotation(0, 0, 10, 0, new BasicStroke(1.0f), Color.BLUE);
        plot.addAnnotation(line0);
       
       
        //dashed horizontal line
        float[] dash = {10.0f, 3.0f, 3.0f};
        Stroke dashed = new BasicStroke(1.0f, BasicStroke.CAP_BUTT,
                BasicStroke.JOIN_MITER, 10.0f, dash, 0.0f);
        XYLineAnnotation line1 = new XYLineAnnotation(0, -1, 10, -1, dashed, Color.MAGENTA);
        plot.addAnnotation(line1);
        
        //right side axis
        NumberAxis range2 = new NumberAxis(" ");
        range2.setAxisLinePaint(Color.GREEN);
        range2.setAxisLineStroke(new BasicStroke(1.5f));
        range2.setTickMarksVisible(false);
        range2.setTickLabelsVisible(false);
        plot.setRangeAxis(1, range2);
        plot.setRangeAxisLocation(1, AxisLocation.BOTTOM_OR_RIGHT);
                
        return new ChartPanel(chart);
    }
    
}

最佳答案

正如所讨论的here ,获得所需轴标记的一种方法是添加 SymbolAxis右侧,如图here及以下。此外,

  • 轴/图重叠可能反射(reflect)了 setAxisOffset()RectangleInsets 的使用,下面省略;相反,会显示第三个域标记。

  • 刻度线位置支持由 DataAxis 提供和DateTickMarkPosition

  • 不要用空格填充标题,而是使用 setPadding(),参见 here及以下。

  • 调整图表的整体大小,如图 here .

  • 在 Swing 上下文中, event dispatch thread 上构建和操作 GUI 对象。

ChartImage

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Stroke;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.Locale;
import javax.swing.JFrame;
import javax.swing.JPanel;
import org.jfree.chart.ChartColor;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.annotations.XYLineAnnotation;
import org.jfree.chart.axis.AxisLocation;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.SymbolAxis;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.ValueMarker;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.chart.title.TextTitle;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.chart.ui.HorizontalAlignment;
import org.jfree.chart.ui.Layer;
import org.jfree.chart.ui.RectangleEdge;

/**
 * @see https://stackoverflow.com/q/74592572/230513
 * @see https://github.com/jfree/jfreechart/discussions/327
 */
public class GitHub327 {
    
    public static void main(String[] args) {
        EventQueue.invokeLater(() -> {
            PlotWindow pw = new PlotWindow();
            pw.setLocationRelativeTo(null);
            pw.setVisible(true);
        });
    }
    
    private static class PlotWindow extends JFrame {
        
        final double t_init = 0;
        final double step = 0.1;
        final double t_fin = 10;
        int lengthValues;
        double[] yValues;
        double[] xValues;

        //params
        double Fmax = 5;
        double Tau = 3;
        double alpha = 3;
        double deltaX;
        
        public PlotWindow() {
            super("Test JFreechart");
            this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            this.setLocationRelativeTo(null);

            // x y values
            this.lengthValues = (int) ((this.t_fin - t_init) / this.step) + 1;
            this.setTimeValues();
            this.computeDeltaX();
            this.setForceValues();
            
            this.add(createChartPanel(), BorderLayout.CENTER);
            this.pack();
        }
        
        private void setTimeValues() {
            this.xValues = new double[this.lengthValues];
            for (int i = 0; i < this.lengthValues; ++i) {
                this.xValues[i] = this.t_init + i * this.step;
            }
        }
        
        private void computeDeltaX() {
            this.deltaX = Math.sqrt(-this.Tau * Math.log(this.alpha / (1 + this.alpha)));
        }
        
        private void setForceValues() {
            this.yValues = new double[lengthValues];
            for (int i = 0; i < lengthValues; ++i) {
                double A = this.Fmax * (1 + 1 / this.alpha);
                double B = 1 - Math.exp(-Math.pow(this.xValues[i] - this.deltaX, 2) / this.Tau);
                this.yValues[i] = A * B - this.Fmax / this.alpha;
            }
        }
        
        private XYSeriesCollection createDataset() {
            boolean autoSort = false;
            boolean allowDuplicateXValues = false;
            XYSeriesCollection dataset = new XYSeriesCollection();
            XYSeries series1 = new XYSeries("", autoSort, allowDuplicateXValues);
            
            for (int i = 0; i < lengthValues; ++i) {
                series1.add(this.xValues[i], this.yValues[i]);
            }
            dataset.addSeries(series1);
            var series2 = new XYSeries("S");
            dataset.addSeries(series2);
            return dataset;
        }
        
        private JPanel createChartPanel() {
            String chartTitle = "Wetting balance curve";
            String xAxisLabel = "X";
            String yAxisLabel = "Y";
            XYSeriesCollection dataset = createDataset();
            JFreeChart chart = ChartFactory.createXYLineChart(chartTitle,
                xAxisLabel, yAxisLabel, dataset, PlotOrientation.VERTICAL, false, true, false);
            XYPlot plot = chart.getXYPlot();

            //title
            TextTitle tt = new TextTitle();
            tt.setText("C:\\MENISCO ST60\\Mesures\\22-5912-100.PM1");
            tt.setPaint(Color.BLUE);
            tt.setFont(new Font("SansSerif", Font.PLAIN, 12));
            tt.setPadding(8, 8, 8, 8);
            chart.setTitle(tt);

            // norm subtitle
            TextTitle normtt = new TextTitle("Norme : J-STD-002E");
            normtt.setFont(new Font("SansSerif", Font.BOLD, 12));
            normtt.setPosition(RectangleEdge.BOTTOM);
            normtt.setPaint(Color.BLACK);
            normtt.setHorizontalAlignment(HorizontalAlignment.LEFT);
            normtt.setPadding(0, 16, 8, 0);
            chart.addSubtitle(normtt);

            // fmoy subtitle
            TextTitle fmoytt = new TextTitle("Force moyenne à 0.900 S: 0.25mN");
            fmoytt.setFont(new Font("SansSerif", Font.PLAIN, 10));
            fmoytt.setPosition(RectangleEdge.BOTTOM);
            fmoytt.setPaint(Color.BLUE);
            fmoytt.setHorizontalAlignment(HorizontalAlignment.LEFT);
            fmoytt.setPadding(0, 16, 2, 0);
            chart.addSubtitle(fmoytt);

            // axis
            //domain axis
            plot.getDomainAxis().setLowerMargin(0.0);
            plot.getDomainAxis().setUpperMargin(0.0);
            NumberAxis domain = (NumberAxis) plot.getDomainAxis();
            NumberFormat formatterd = DecimalFormat.getInstance();
            formatterd.setMinimumFractionDigits(0);
            domain.setNumberFormatOverride(formatterd);
            domain.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
            domain.setTickMarksVisible(false);
            //domain.setAxisLineVisible(false);

            //range axis
            plot.getRangeAxis().setLabelAngle(Math.PI / 2);
            NumberAxis range = (NumberAxis) plot.getRangeAxis();
            NumberFormat formatter = DecimalFormat.getInstance(Locale.ENGLISH);
            formatter.setMinimumFractionDigits(2);
            range.setNumberFormatOverride(formatter);
            plot.getRangeAxis().setAxisLineStroke(new BasicStroke(1.5f));
            plot.getRangeAxis().setAxisLinePaint(Color.BLUE);
            plot.getRangeAxis().setTickMarksVisible(true);
            plot.getRangeAxis().setTickMarkPaint(Color.BLACK);
            plot.getRangeAxis().setTickMarkStroke(new BasicStroke(1.5f));
            float lg = plot.getRangeAxis().getTickMarkOutsideLength();
            plot.getRangeAxis().setTickMarkInsideLength(lg);
            plot.getRangeAxis().setRange(-3, 6);

            // background;gridline;outline 
            //plot.setAxisOffset(new RectangleInsets(0, 0, 0, 0));
            plot.setDomainGridlinePaint(Color.DARK_GRAY);
            plot.setRangeGridlinePaint(Color.DARK_GRAY);
            plot.setBackgroundPaint(Color.white);
            plot.setOutlineStroke(plot.getDomainGridlineStroke()); //dashed outline
            plot.setOutlinePaint(Color.DARK_GRAY);
            XYItemRenderer renderer = plot.getRenderer();
            renderer.setSeriesPaint(0, ChartColor.VERY_DARK_GREEN); // set green color to the xyline

            // vertical lines
            ValueMarker marker0 = new ValueMarker(0.2, Color.MAGENTA, new BasicStroke(1.5f));  // position is the value on the axis
            ValueMarker marker1 = new ValueMarker(1, Color.MAGENTA, new BasicStroke(1.5f));
            ValueMarker marker2 = new ValueMarker(4, Color.GREEN, new BasicStroke(1.5f));  // position is the value on the axis
            plot.addDomainMarker(marker0, Layer.FOREGROUND);
            plot.addDomainMarker(marker1, Layer.FOREGROUND);
            plot.addDomainMarker(marker2, Layer.FOREGROUND);
            
            var marker3 = new ValueMarker(10, Color.GREEN, new BasicStroke(1.5f));
            plot.addDomainMarker(marker3, Layer.FOREGROUND);

            //horizontal lines
            XYLineAnnotation line = new XYLineAnnotation(0, 4, 10, 4, new BasicStroke(2.0f), Color.green);
            plot.addAnnotation(line);
            XYLineAnnotation line0 = new XYLineAnnotation(0, 0, 10, 0, new BasicStroke(1.0f), Color.BLUE);
            plot.addAnnotation(line0);

            //dashed horizontal line
            float[] dash = {10.0f, 3.0f, 3.0f};
            Stroke dashed = new BasicStroke(1.0f, BasicStroke.CAP_BUTT,
                BasicStroke.JOIN_MITER, 10.0f, dash, 0.0f);
            XYLineAnnotation line1 = new XYLineAnnotation(0, -1, 10, -1, dashed, Color.MAGENTA);
            plot.addAnnotation(line1);

            //right side axis
            String[] syms = new String[]{"", "", "", "t(s)", "", "", "", "4.0", "", ""};
            var range2 = new SymbolAxis("", syms);
            range2.setGridBandsVisible(false);
            plot.setRangeAxis(1, range2);
            plot.setRangeAxisLocation(1, AxisLocation.BOTTOM_OR_RIGHT);
            plot.mapDatasetToRangeAxis(1, 0);
            
            return new ChartPanel(chart) {
                @Override
                public Dimension getPreferredSize() {
                    return new Dimension(640, 480);
                }
            };
        }
    }
}

关于java - Jfreechart 将文本/注释/标签放置在图表轮廓上的所需位置和线条上,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74592572/

相关文章:

swift - xcode 8 输入字段操作不显示事件下拉列表

python - 如何使用 plotly_express 绘制多折线图?

charts - 删除谷歌图表中的填充?

java - Executorservice worker 没有在单独的线程中运行

java - 如何在 Eclipse 中设置 JVM 启动参数?

text - VHDL - 包中的类型声明

java - 使用 Bloomberg API 绘制历史最新价格图表

java - 如何向可变参数添加参数?

已给定字符串中的 Java 占位符

javascript - 如何获取特定 div 中的文本,只避免嵌套 div 中的文本?