javajfreechart

How to rotate x-axis labels on JFreeChart xyPlot


I want to display the x-axis labels at a 45 degree angle, similar to this

enter image description here

The examples I've found all assume that getDomainAxis() returns a CategoryAxis, which has a setCategoryLabelPositions() method. But in an XY Plot, I get a ValueAxis. There's a method for displaying the labels vertical, but not rotating at an arbitrary angle.

Update

Looking at the code for ValueAxis.drawTickMarksAndLabels(), there does seem to be mention of an angle on the ValueTick object, but I'm not sure how to get access to that or where to set it


Solution

  • I had previously thought that ValueAxis tick label orientation was limited by setVerticalTickLabels(). In this discussion, you cite a forum topic that suggests a potential workaround.

    As a proof of concept, based on this example, the variation below illustrates the approach using a custom implementation of refreshTicksHorizontal(). Note:

    Rotated Axis labels

    import java.awt.Dimension;
    import java.awt.Graphics2D;
    import java.awt.font.TextAttribute;
    import java.awt.geom.Rectangle2D;
    import java.text.AttributedString;
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.TimeZone;
    import javax.swing.JFrame;
    import org.jfree.chart.ChartFactory;
    import org.jfree.chart.JFreeChart;
    import org.jfree.chart.axis.DateAxis;
    import org.jfree.chart.plot.XYPlot;
    import org.jfree.chart.ChartPanel;
    import org.jfree.chart.axis.DateTick;
    import org.jfree.chart.axis.Tick;
    import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
    import org.jfree.chart.ui.RectangleEdge;
    import org.jfree.data.time.Minute;
    import org.jfree.data.time.TimeSeries;
    import org.jfree.data.time.TimeSeriesCollection;
    import org.jfree.data.xy.XYDataset;
    
    /**
     * @see https://stackoverflow.com/q/76574565/230513
     * @see https://stackoverflow.com/q/70071299/230513
     * @see https://stackoverflow.com/a/12481509/230513
     * @see https://stackoverflow.com/a/12481509/230513
     */
    public class TimeTest {
    
        private static XYDataset createDataset() throws ParseException {
            SimpleDateFormat f = new SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy");
            TimeSeries series = new TimeSeries("Temperature");
            series.add(new Minute(f.parse("Sat Nov 20 12:10:00 PST 2021")), 72.5);
            series.add(new Minute(f.parse("Sat Nov 20 13:11:00 PST 2021")), 70.7);
            series.add(new Minute(f.parse("Sat Nov 20 14:12:00 PST 2021")), 70.1);
            return new TimeSeriesCollection(series);
        }
    
        private static JFreeChart createChart(final XYDataset dataset) {
            SimpleDateFormat f = new SimpleDateFormat("HH:mm");
            f.setTimeZone(TimeZone.getTimeZone("PST"));
            JFreeChart chart = ChartFactory.createTimeSeriesChart
    (            "Test", "Time", "Temperture °F", dataset, true, true, false);
            XYPlot plot = (XYPlot) chart.getPlot();
            XYLineAndShapeRenderer r = (XYLineAndShapeRenderer) plot.getRenderer();
            r.setDefaultShapesVisible(true);
            DateAxis domain = new DateAxis() {
                //@see https://www.jfree.org/forum/viewtopic.php?t=25189
                //todo paramterize angle
                @Override
                protected List refreshTicksHorizontal(Graphics2D g2, Rectangle2D dataArea, RectangleEdge edge) {
                    List ticks = super.refreshTicksHorizontal(g2, dataArea, edge);
                    List ret = new ArrayList();
                    for (Tick tick : (List<Tick>) ticks) {
                        if (tick instanceof DateTick dateTick) {
                            ret.add(new DateTick(dateTick.getDate(), dateTick.getText(),
                                dateTick.getTextAnchor(), dateTick.getRotationAnchor(), -45));
                        } else {
                            ret.add(tick);
                        }
                    }
                    return ret;
                }
            };
            AttributedString as = new AttributedString("TimeN");
            as.addAttribute(TextAttribute.SIZE, 16, 0, 5);
            as.addAttribute(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD, 0, 5);
            as.addAttribute(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUB, 4, 5);
            domain.setAttributedLabel(as);
            domain.setDateFormatOverride(f);
            domain.setVerticalTickLabels(true);
            plot.setDomainAxis(domain);
            return chart;
        }
    
        public static void main(String[] args) throws ParseException {
    
            JFrame f = new JFrame();
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            XYDataset dataset = createDataset();
            JFreeChart chart = createChart(dataset);
            ChartPanel chartPanel = new ChartPanel(chart) {
    
                @Override
                public Dimension getPreferredSize() {
                    return new Dimension(400, 250);
                }
            };
            f.add(chartPanel);
            f.pack();
            f.setLocationRelativeTo(null);
            f.setVisible(true);
        }
    }