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