View Javadoc
1   package nl.tudelft.simulation.dsol.swing.charts.histogram;
2   
3   import java.awt.Color;
4   import java.awt.Graphics2D;
5   import java.awt.geom.Rectangle2D;
6   import java.text.DecimalFormatSymbols;
7   import java.text.NumberFormat;
8   
9   import org.jfree.chart.axis.AxisSpace;
10  import org.jfree.chart.axis.AxisState;
11  import org.jfree.chart.axis.NumberAxis;
12  import org.jfree.chart.plot.Plot;
13  import org.jfree.chart.plot.PlotRenderingInfo;
14  import org.jfree.chart.plot.XYPlot;
15  import org.jfree.chart.ui.RectangleEdge;
16  
17  /**
18   * The histogram domainAxis defines the x-Axis of a histogram.
19   * <p>
20   * copyright (c) 2002-2021 <a href="https://simulation.tudelft.nl">Delft University of Technology </a>, the Netherlands. <br>
21   * See for project information <a href="https://simulation.tudelft.nl"> www.simulation.tudelft.nl </a>.
22   * <p>
23   * Copyright (c) 2002-2023 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
24   * for project information <a href="https://simulation.tudelft.nl/" target="_blank"> https://simulation.tudelft.nl</a>. The DSOL
25   * project is distributed under a three-clause BSD-style license, which can be found at
26   * <a href="https://https://simulation.tudelft.nl/dsol/docs/latest/license.html" target="_blank">
27   * https://https://simulation.tudelft.nl/dsol/docs/latest/license.html</a>.
28   * </p>
29   * @author <a href="mailto:a.verbraeck@tudelft.nl"> Alexander Verbraeck </a> <br>
30   *         <a href="https://www.linkedin.com/in/peterhmjacobs"> Peter Jacobs </a>
31   * @since 1.5
32   */
33  public class HistogramDomainAxis extends NumberAxis
34  {
35      /** */
36      private static final long serialVersionUID = 1L;
37  
38      /** labels refers to the labels to be printed. */
39      protected String[] labels = null;
40  
41      /** maxLabelHeight refers to the maximum label heigth. */
42      protected double maxLabelHeight = -1;
43  
44      /**
45       * constructs a new HistogramDomainAxis.
46       * @param parent XYPlot; the plot to which this axis belongs
47       * @param label String; the label of the axis
48       * @param domain double[]; the domain
49       * @param numberOfBins int; the numberOfBins
50       */
51      public HistogramDomainAxis(final XYPlot parent, final String label, final double[] domain, final int numberOfBins)
52      {
53          super(label);
54          this.setAutoRange(false);
55          double binWidth = (domain[1] - domain[0]) / numberOfBins * 1.0;
56          this.setLowerBound(domain[0] - binWidth);
57          this.setUpperBound(domain[1] + binWidth);
58          this.setVerticalTickLabels(true);
59          // Let's copy the font etc from the y-axis
60          this.setLabelFont(parent.getRangeAxis().getLabelFont());
61          this.setTickLabelFont(parent.getRangeAxis().getTickLabelFont());
62          this.labels = this.createLabels(domain, numberOfBins);
63      }
64  
65      /** {@inheritDoc} */
66      @Override
67      public double valueToJava2D(final double value, final Rectangle2D dataArea, final RectangleEdge edge)
68      {
69          double ratio = (value - this.getLowerBound()) / (this.getUpperBound() - this.getLowerBound());
70          return dataArea.getX() + ratio * (dataArea.getWidth());
71      }
72  
73      /** {@inheritDoc} */
74      @Override
75      public double java2DToValue(final double value, final Rectangle2D dataArea, final RectangleEdge edge)
76      {
77          double ratio = (value - dataArea.getX()) / dataArea.getWidth();
78          return this.getLowerBound() + ratio * (this.getUpperBound() - this.getLowerBound());
79      }
80  
81      /** {@inheritDoc} */
82      @Override
83      public AxisSpace reserveSpace(final Graphics2D g2, final Plot dataPlot, final Rectangle2D dataArea,
84              final RectangleEdge edge, final AxisSpace axisSpace)
85      {
86          if (this.maxLabelHeight == -1)
87          {
88              g2.setFont(this.getTickLabelFont());
89              // If we have not yet measured the heigth of the labels
90              for (int i = 0; i < this.labels.length; i++)
91              {
92                  // we draw 90degrees rotated, so heigth = width
93                  double height = g2.getFont().getStringBounds(this.labels[i], g2.getFontRenderContext()).getWidth();
94                  if (height > this.maxLabelHeight)
95                  {
96                      this.maxLabelHeight = height + 3;
97                  }
98              }
99          }
100         AxisSpace result = new AxisSpace();
101         result.add(this.maxLabelHeight, RectangleEdge.BOTTOM);
102         return result;
103     }
104 
105     /**
106      * creates the labels for the axis
107      * @param domain double[]; the domain of the histogram
108      * @param numberOfBins int; the number of bins
109      * @return the result
110      */
111     private String[] createLabels(final double[] domain, final int numberOfBins)
112     {
113         String[] result = new String[numberOfBins + 2];
114         NumberFormat formatter = NumberFormat.getInstance();
115         formatter.setMaximumFractionDigits(2);
116         double binWidth = (domain[1] - domain[0]) / numberOfBins * 1.0;
117         double start = domain[0];
118         for (int i = 1; i < numberOfBins + 1; i++)
119         {
120             result[i] = formatter.format(start);
121             start = start + binWidth;
122         }
123         // Let's write infinity and -infinity
124         DecimalFormatSymbols symbols = new DecimalFormatSymbols();
125         result[0] = "-" + symbols.getInfinity();
126         result[numberOfBins + 1] = symbols.getInfinity();
127         return result;
128     }
129 
130     /** {@inheritDoc} */
131     @Override
132     public AxisState draw(final Graphics2D g2, final double cursor, final Rectangle2D plotArea, final Rectangle2D dataArea,
133             final RectangleEdge edge, final PlotRenderingInfo arg5)
134     {
135         g2.setColor(Color.BLACK);
136         g2.setFont(this.getTickLabelFont());
137         double labelWidth = g2.getFont().getStringBounds(this.labels[0], g2.getFontRenderContext()).getHeight();
138         double width = dataArea.getWidth() / (this.labels.length) * 1.0;
139         double x = dataArea.getX() + 0.5 * width;
140         double y = dataArea.getY() + dataArea.getHeight();
141         g2.translate(x, y);
142         g2.rotate(-Math.PI / 2.0);
143         double offset = 0.0;
144         for (int i = 0; i < this.labels.length; i++)
145         {
146             double labelHeight = g2.getFont().getStringBounds(this.labels[i], g2.getFontRenderContext()).getWidth() + 3;
147             g2.drawString(this.labels[i], Math.round(-labelHeight), Math.round(offset + 0.33 * labelWidth));
148             offset = offset + width;
149         }
150         g2.rotate(Math.PI / 2.0);
151         g2.translate(-x, -y);
152         return new AxisState();
153     }
154 }