2

In JFreechart I have an X axis with dates (and times).

How can I ask JFreechart to optimize them and make the most out of it?

Right now it contains more label than the space and all the labels gets converted into '...'.

enter image description here

It is totally fine if not all ticks will have labels, but I want as much as can be (if they fit and can be displayed fully).

How can I achieve this?

UPDATE1:

Here is the complete minimal source to reproduce the truncated labels. (also updated the screenshot). JFreechart does not handle the optimization by default:

import org.jfree.chart.ChartPanel;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.ui.ApplicationFrame;
import org.jfree.ui.RefineryUtilities;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.category.DefaultCategoryDataset;

public class LineChart_AWT extends ApplicationFrame {

    public LineChart_AWT( String applicationTitle , String chartTitle ) {
          super(applicationTitle);
          JFreeChart lineChart = ChartFactory.createLineChart(
             chartTitle,
             "Dates","Temperature",
             createDataset(),
             PlotOrientation.VERTICAL,
             true,true,false);

          CategoryPlot plot = (CategoryPlot) lineChart.getPlot();
          plot.getRangeAxis().setRange(25, 27);

          ChartPanel chartPanel = new ChartPanel( lineChart );
          chartPanel.setPreferredSize( new java.awt.Dimension( 560 , 367 ) );
          setContentPane( chartPanel );
       }

       private DefaultCategoryDataset createDataset( ) {
          DefaultCategoryDataset dataset = new DefaultCategoryDataset( );

          dataset.addValue( 26.44,"Temperature","2019-08-18 00:00");
          dataset.addValue( 26.2,"Temperature","2019-08-18 01:00");
          dataset.addValue( 25.93,"Temperature","2019-08-18 02:00");
          dataset.addValue( 25.71,"Temperature","2019-08-18 03:00");
          dataset.addValue( 25.54,"Temperature","2019-08-18 04:00");
          dataset.addValue( 25.42,"Temperature","2019-08-18 05:00");
          dataset.addValue( 25.25,"Temperature","2019-08-18 06:00");
          dataset.addValue( 25.19,"Temperature","2019-08-18 07:00");
          dataset.addValue( 25.25,"Temperature","2019-08-18 08:00");
          dataset.addValue( 25.36,"Temperature","2019-08-18 09:00");
          dataset.addValue( 25.52,"Temperature","2019-08-18 10:00");
          dataset.addValue( 25.86,"Temperature","2019-08-18 11:00");
          dataset.addValue( 26.51,"Temperature","2019-08-18 12:00");
          dataset.addValue( 26.82,"Temperature","2019-08-18 13:00");


          return dataset;
       }

       public static void main( String[ ] args ) {
          LineChart_AWT chart = new LineChart_AWT(
             "X-axis demo" ,
             "X-axis labels are truncated");

          chart.pack( );
          RefineryUtilities.centerFrameOnScreen( chart );
          chart.setVisible( true );
       }
    }

UPDATE2:

I prefer the 45° rotation for the X-axis labels as @trashgod recommended. However this approach is not working fine when more data comes into the picture:

enter image description here

Is it possible to set the maximal allowable labels count? I would set it to some default value like 5 or 6. Or, it is also fine to define a margin or padding for increasing readability if this would hide the surroundings.

Daniel
  • 2,318
  • 2
  • 22
  • 53
  • As the default settings handle this automatically, I'd guess your code is causing thism. Please [edit] your question to include a [mcve] that exhibits the problem you illustrate. – trashgod Aug 17 '19 at 16:32
  • @trashgod: it is not handled automatically - as seen on the screenshot. – Daniel Aug 18 '19 at 11:41
  • Your [mcve] illustrates the problem nicely; I've elaborated below. – trashgod Aug 18 '19 at 13:01

1 Answers1

4

Your updated example creates a CategoryDataset and uses the ChartFactory method, createLineChart(), to create a CategoryPlot. You can adjust the label positions for readability as shown here and below. Moreover,

it would be nice to have a vertical grid line only when the label is visible and hide all other grid lines.

plot.getDomainAxis().setCategoryLabelPositions(
    CategoryLabelPositions.UP_45);
plot.setDomainGridlinesVisible(true);
plot.setRangeGridlinesVisible(false);

enter image description here

More generally, create a TimeSeries and use the corresponding ChartFactory method, createTimeSeriesChart(). The resulting DateAxis will automatically adjust the labels when the enclosing chart is resized. In addition,

  • You can adjust the date format as shown here.

  • To establish the chart's initial size, override getPreferredSize(), as suggested here.

  • When using setRange(), query the underlying dataset, as shown below.

  • Construct and manipulate Swing GUI objects only on the event dispatch thread.

Adjustable

import java.awt.Dimension;
import java.awt.EventQueue;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.ui.ApplicationFrame;
import org.jfree.chart.plot.XYPlot;
import org.jfree.data.time.Day;
import org.jfree.data.time.Hour;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;

public class TempChart extends ApplicationFrame {

    public TempChart(String applicationTitle, String chartTitle) {
        super(applicationTitle);
        TimeSeries s = createSeries();
        JFreeChart chart = ChartFactory.createTimeSeriesChart(
            chartTitle, "Date", "Temperature", new TimeSeriesCollection(s));

        XYPlot plot = (XYPlot) chart.getPlot();
        plot.getRangeAxis().setRange(Math.floor(s.getMinY()), Math.ceil(s.getMaxY()));

        ChartPanel chartPanel = new ChartPanel(chart) {
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(560, 367);
            }
        };
        add(chartPanel);
    }

    private TimeSeries createSeries() {
        TimeSeries series = new TimeSeries("Temperature");

        series.add(new Hour(0, new Day()), 26.44);
        series.add(new Hour(1, new Day()), 26.2);
        series.add(new Hour(2, new Day()), 25.93);
        series.add(new Hour(3, new Day()), 25.71);
        series.add(new Hour(4, new Day()), 25.54);
        series.add(new Hour(5, new Day()), 25.42);
        series.add(new Hour(6, new Day()), 25.25);
        series.add(new Hour(7, new Day()), 25.19);
        series.add(new Hour(8, new Day()), 25.25);
        series.add(new Hour(9, new Day()), 25.36);
        series.add(new Hour(10, new Day()), 25.52);
        series.add(new Hour(11, new Day()), 25.86);
        series.add(new Hour(12, new Day()), 26.51);
        series.add(new Hour(13, new Day()), 26.82);

        return series;
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                TempChart chart = new TempChart(
                    "Temperature demo", "Time Axis labels adjust on resize");
                chart.pack();
                chart.setLocationRelativeTo(null);
                chart.setVisible(true);
            }
        });
    }
}
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • I'd prefer your 1st approach with the 45 degree labels. (this is a dynamically generated chart, and it can happen that the domain axis is not a DateAxis.) However when adding more values, labels are extremely close to each other, making it almost impossible to read them. Is it some easy way of handling this? Please check the update of my question I've created a new screenshot of the current problem. Thanks! – Daniel Aug 18 '19 at 19:50
  • @trashgood: also it would be nice to have a vertical grid line only when the label is visible and hide all other grid lines. – Daniel Aug 18 '19 at 20:50
  • If space is limited, you can make the labels vertical with either approach; more above. – trashgod Aug 19 '19 at 02:27
  • @trashgood: even when making them vertical does not helps, because they will be too close to each other :/ – Daniel Aug 19 '19 at 05:58
  • 1
    Also consider `SlidingCategoryDataset`. – trashgod Aug 19 '19 at 18:02
  • Thanks, I've checked SlidingCategoryDataset it hides the labels fine but also hides the chart data. – Daniel Aug 20 '19 at 11:57
  • Yes, sorry, just have accepted your nice suggestion :) – Daniel Aug 22 '19 at 15:20