Jfreechart поместите текст/аннотации/метки в нужное положение и линию по контуру диаграммы

Я пытаюсь воссоздать диаграмму, подобную той, что на следующем рисунке:

Jfreechart поместите текст/аннотации/метки в нужное положение и линию по контуру диаграммы

На данный момент мне удалось получить это:

Jfreechart поместите текст/аннотации/метки в нужное положение и линию по контуру диаграммы

Как добавить отсутствующие элементы, например метки осей, за пределы допустимых границ метода 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);
    }
    
}
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
0
156
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Как обсуждалось здесь , один из способов получить желаемую маркировку оси — добавить SymbolAxis справа, как показано здесь и ниже. Кроме того,

  • Перекрытие оси/графика может отражать использование RectangleInsets в setAxisOffset(), опущенное ниже; вместо этого отображается третий маркер домена.

  • Поддержка положения делений обеспечивается DataAxis и DateTickMarkPosition.

  • Вместо заполнения заголовков пробелами используйте setPadding(), как показано здесь и ниже.

  • Отрегулируйте общий размер диаграммы, как показано здесь.

  • В контексте Swing создавайте и манипулируйте объектами GUI только в потоке диспетчеризации событий.

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);
                }
            };
        }
    }
}

Другие вопросы по теме