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