View Javadoc
1   package org.opentrafficsim.draw.graphs;
2   
3   import org.djunits.value.vdouble.scalar.Duration;
4   import org.djunits.value.vdouble.scalar.Length;
5   import org.djunits.value.vdouble.scalar.Time;
6   import org.jfree.chart.JFreeChart;
7   import org.jfree.chart.event.AxisChangeEvent;
8   import org.jfree.chart.event.AxisChangeListener;
9   import org.jfree.chart.plot.XYPlot;
10  import org.opentrafficsim.core.dsol.OtsSimulatorInterface;
11  
12  /**
13   * Plots with space-time. This class adds some zoom control, where a user can manually select a zoom range, or the plot
14   * automatically zooms over the entire space range, and either the entire or some most recent fixed period in time.
15   * <p>
16   * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
17   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
18   * </p>
19   * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
20   * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
21   * @author <a href="https://dittlab.tudelft.nl">Wouter Schakel</a>
22   */
23  public abstract class AbstractSpaceTimePlot extends AbstractBoundedPlot
24  {
25  
26      /** Initial end time of plot. */
27      private final Time initialEnd;
28  
29      /** Whether to update the axes. */
30      private boolean autoBoundAxes = true;
31  
32      /** Whether to disable auto bounds on the axes on any change on the axes. */
33      private boolean virtualAutoBounds = false;
34  
35      /** Fixed domain range. */
36      private Double fixedDomainRange = null;
37  
38      /**
39       * Constructor.
40       * @param caption String; caption
41       * @param updateInterval Duration; regular update interval (simulation time)
42       * @param simulator OtsSimulatorInterface; simulator
43       * @param delay Duration; amount of time that chart runs behind simulation to prevent gaps in the charted data
44       * @param initialEnd Time; initial end time of plots, will be expanded if simulation time exceeds it
45       */
46      public AbstractSpaceTimePlot(final String caption, final Duration updateInterval, final OtsSimulatorInterface simulator,
47              final Duration delay, final Time initialEnd)
48      {
49          super(simulator, caption, updateInterval, delay);
50          this.initialEnd = initialEnd;
51      }
52  
53      /** {@inheritDoc} */
54      @Override
55      protected void setChart(final JFreeChart chart)
56      {
57          super.setChart(chart);
58          XYPlot xyPlot = chart.getXYPlot();
59          setLowerRangeBound(0.0);
60          setUpperRangeBound(getEndLocation().si);
61          setLowerDomainBound(0.0);
62          setUpperDomainBound(this.initialEnd.si);
63          setAutoBounds(xyPlot);
64          // axis listeners to enable/disable auto zoom
65          xyPlot.getDomainAxis().addChangeListener(new AxisChangeListener()
66          {
67              /** {@inheritDoc} */
68              @SuppressWarnings("synthetic-access")
69              @Override
70              public void axisChanged(final AxisChangeEvent event)
71              {
72                  if (!AbstractSpaceTimePlot.this.virtualAutoBounds)
73                  {
74                      // the axis was changed, but not by a command from this class, auto bounds should be disabled
75                      AbstractSpaceTimePlot.this.autoBoundAxes = false;
76                  }
77              }
78          });
79      }
80  
81      /** {@inheritDoc} */
82      @Override
83      protected void update()
84      {
85          if (getUpdateTime() != null && this.initialEnd != null)
86          {
87              setUpperDomainBound(Math.max(getUpdateTime().si, this.initialEnd.si));
88          }
89          if (this.autoBoundAxes && getChart() != null) // null during construction
90          {
91              setAutoBounds(getChart().getXYPlot());
92          }
93          super.update();
94      }
95  
96      /**
97       * Update the fixed-ness of the domain range.
98       * @param fixed boolean; if true; the domain range will not update when new data becomes available; if false; the domain
99       *            range will update to show newly available data
100      */
101     public void updateFixedDomainRange(final boolean fixed)
102     {
103         this.fixedDomainRange = fixed ? getChart().getXYPlot().getDomainAxis().getRange().getLength() : null;
104         notifyPlotChange();
105     }
106 
107     /**
108      * Sets the auto bounds without deactivating auto bounds through the axis change listener. This is used to initialize the
109      * plot, and to update the plot when time is increased.
110      * @param plot XYPlot; plot with default zoom-all bounds set
111      */
112     private void setAutoBounds(final XYPlot plot)
113     {
114         // disables the axis change listener from registering a user input that is actually an update of bounds as the time
115         // increases
116         this.virtualAutoBounds = true;
117         if (this.fixedDomainRange != null && getUpdateTime().si > 0.0)
118         {
119             plot.getDomainAxis().setRange(Math.max(getUpdateTime().si - this.fixedDomainRange, 0.0), getUpdateTime().si);
120         }
121         else
122         {
123             super.setAutoBoundDomain(plot); // super to skip setting autoBoundAxes = true
124         }
125         super.setAutoBoundRange(plot); // super to skip setting autoBoundAxes = true
126         this.virtualAutoBounds = false;
127     }
128 
129     /** {@inheritDoc} This implementation overrides to enable it's own form of auto bounds. */
130     @Override
131     public final void setAutoBoundDomain(final XYPlot plot)
132     {
133         super.setAutoBoundDomain(plot);
134         this.autoBoundAxes = true;
135     }
136 
137     /** {@inheritDoc} This implementation overrides to enable it's own form of auto bounds. */
138     @Override
139     public final void setAutoBoundRange(final XYPlot plot)
140     {
141         super.setAutoBoundRange(plot);
142         this.autoBoundAxes = true;
143     }
144 
145     /**
146      * Returns the total path length.
147      * @return Length; total path length
148      */
149     protected abstract Length getEndLocation();
150 
151 }