View Javadoc
1   package org.opentrafficsim.demo.lanechange;
2   
3   import java.awt.BorderLayout;
4   import java.awt.Frame;
5   import java.awt.geom.Line2D;
6   import java.lang.reflect.InvocationTargetException;
7   import java.util.ArrayList;
8   import java.util.Collection;
9   import java.util.LinkedHashSet;
10  import java.util.List;
11  import java.util.Set;
12  
13  import javax.naming.NamingException;
14  import javax.swing.JFrame;
15  import javax.swing.JPanel;
16  import javax.swing.SwingUtilities;
17  import javax.swing.event.EventListenerList;
18  
19  import org.djunits.unit.util.UNITS;
20  import org.djunits.value.vdouble.scalar.Acceleration;
21  import org.djunits.value.vdouble.scalar.Direction;
22  import org.djunits.value.vdouble.scalar.Duration;
23  import org.djunits.value.vdouble.scalar.Length;
24  import org.djunits.value.vdouble.scalar.Speed;
25  import org.djunits.value.vdouble.scalar.Time;
26  import org.djunits.value.vdouble.scalar.base.DoubleScalar;
27  import org.jfree.chart.ChartFactory;
28  import org.jfree.chart.ChartPanel;
29  import org.jfree.chart.JFreeChart;
30  import org.jfree.chart.StandardChartTheme;
31  import org.jfree.chart.axis.NumberAxis;
32  import org.jfree.chart.event.PlotChangeEvent;
33  import org.jfree.chart.plot.Plot;
34  import org.jfree.chart.plot.PlotOrientation;
35  import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
36  import org.jfree.data.DomainOrder;
37  import org.jfree.data.general.DatasetChangeListener;
38  import org.jfree.data.general.DatasetGroup;
39  import org.jfree.data.xy.XYDataset;
40  import org.opentrafficsim.base.parameters.ParameterException;
41  import org.opentrafficsim.core.dsol.OTSModelInterface;
42  import org.opentrafficsim.core.dsol.OTSSimulator;
43  import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
44  import org.opentrafficsim.core.geometry.OTSGeometryException;
45  import org.opentrafficsim.core.geometry.OTSPoint3D;
46  import org.opentrafficsim.core.gtu.GTUDirectionality;
47  import org.opentrafficsim.core.gtu.GTUException;
48  import org.opentrafficsim.core.gtu.GTUType;
49  import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
50  import org.opentrafficsim.core.network.NetworkException;
51  import org.opentrafficsim.demo.DefaultsFactory;
52  import org.opentrafficsim.road.gtu.lane.LaneBasedIndividualGTU;
53  import org.opentrafficsim.road.gtu.lane.perception.headway.Headway;
54  import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTUSimple;
55  import org.opentrafficsim.road.gtu.lane.tactical.LaneBasedCFLCTacticalPlanner;
56  import org.opentrafficsim.road.gtu.lane.tactical.following.GTUFollowingModelOld;
57  import org.opentrafficsim.road.gtu.lane.tactical.following.IDMOld;
58  import org.opentrafficsim.road.gtu.lane.tactical.following.IDMPlusOld;
59  import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.Altruistic;
60  import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.Egoistic;
61  import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.LaneChangeModel;
62  import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.LaneMovementStep;
63  import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalPlanner;
64  import org.opentrafficsim.road.gtu.strategical.route.LaneBasedStrategicalRoutePlanner;
65  import org.opentrafficsim.road.network.OTSRoadNetwork;
66  import org.opentrafficsim.road.network.factory.LaneFactory;
67  import org.opentrafficsim.road.network.lane.DirectedLanePosition;
68  import org.opentrafficsim.road.network.lane.Lane;
69  import org.opentrafficsim.road.network.lane.LaneType;
70  import org.opentrafficsim.road.network.lane.OTSRoadNode;
71  
72  import nl.tudelft.simulation.dsol.SimRuntimeException;
73  import nl.tudelft.simulation.dsol.experiment.StreamInformation;
74  import nl.tudelft.simulation.dsol.model.inputparameters.InputParameterMap;
75  import nl.tudelft.simulation.dsol.statistics.StatisticsInterface;
76  import nl.tudelft.simulation.dsol.swing.gui.TablePanel;
77  
78  /**
79   * Create a plot that characterizes a lane change graph.
80   * <p>
81   * Copyright (c) 2013-2022 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
82   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
83   * <p>
84   * $LastChangedDate$, @version $Revision$, by $Author$, initial version 18 nov. 2014 <br>
85   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
86   */
87  public class LaneChangeGraph extends JFrame implements OTSModelInterface, UNITS
88  {
89      /** */
90      private static final long serialVersionUID = 20141118L;
91  
92      /** Standard speed values in km/h. */
93      static final double[] STANDARDSPEEDS = {30, 50, 80, 100, 120};
94  
95      /** The car following model. */
96      private GTUFollowingModelOld carFollowingModel;
97  
98      /** The graphs. */
99      private ChartPanel[][] charts;
100 
101     /** Start of two lane road. */
102     private static final Length LOWERBOUND = new Length(-500, METER);
103 
104     /** Position of reference vehicle on the two lane road. */
105     private static final Length MIDPOINT = new Length(0, METER);
106 
107     /** End of two lane road. */
108     private static final Length UPPERBOUND = new Length(500, METER);
109 
110     /** The JFrame with the lane change graphs. */
111     private static LaneChangeGraph lcs;
112 
113     /** The network. */
114     private OTSRoadNetwork network = new OTSRoadNetwork("network", true, getSimulator());
115 
116     /**
117      * Create a Lane Change Graph.
118      * @param title String; title text of the window
119      * @param mainPanel JPanel; panel that will (indirectly?) contain the charts
120      */
121     LaneChangeGraph(final String title, final JPanel mainPanel)
122     {
123         super(title);
124         setContentPane(mainPanel);
125         this.charts = new ChartPanel[2][STANDARDSPEEDS.length];
126     }
127 
128     /**
129      * Main entry point; now Swing thread safe (I hope).
130      * @param args String[]; the command line arguments (not used)
131      * @throws GTUException on error during GTU construction
132      * @throws SimRuntimeException on ???
133      * @throws NetworkException on network inconsistency
134      * @throws NamingException on ???
135      * @throws OTSGeometryException x
136      * @throws ParameterException in case of a parameter problem.
137      * @throws OperationalPlanException x
138      */
139     public static void main(final String[] args) throws NamingException, NetworkException, SimRuntimeException, GTUException,
140             OTSGeometryException, ParameterException, OperationalPlanException
141     {
142         try
143         {
144             SwingUtilities.invokeAndWait(new Runnable()
145             {
146                 @Override
147                 public void run()
148                 {
149                     try
150                     {
151                         buildGUI(args);
152                     }
153                     catch (NamingException | NetworkException | SimRuntimeException | GTUException exception)
154                     {
155                         exception.printStackTrace();
156                     }
157                 }
158 
159             });
160         }
161         catch (InvocationTargetException | InterruptedException exception)
162         {
163             exception.printStackTrace();
164         }
165 
166         for (int row = 0; row < lcs.charts.length; row++)
167         {
168             LaneChangeModel laneChangeModel = 0 == row ? new Egoistic() : new Altruistic();
169             for (int index = 0; index < STANDARDSPEEDS.length; index++)
170             {
171                 Speed speed = new Speed(STANDARDSPEEDS[index], KM_PER_HOUR);
172                 // System.out.println("speed " + speed);
173                 double startSpeedDifference = -30; // standardSpeeds[index];
174                 double endSpeedDifference = startSpeedDifference + 60; // 150;
175                 ChartData data = (ChartData) lcs.charts[row][index].getChart().getXYPlot().getDataset();
176                 int beginRightKey = data.addSeries("Begin of no lane change to right");
177                 int endRightKey = data.addSeries("End of no lane change to right");
178                 int beginLeftKey = data.addSeries("Begin of no lane change to left");
179                 int endLeftKey = data.addSeries("End of no lane change to left");
180                 for (double speedDifference = startSpeedDifference; speedDifference <= endSpeedDifference; speedDifference += 1)
181                 {
182                     Length criticalHeadway = lcs.findDecisionPoint(LaneChangeGraph.LOWERBOUND, MIDPOINT, speed,
183                             new Speed(speedDifference, KM_PER_HOUR), laneChangeModel, true);
184                     if (null != criticalHeadway)
185                     {
186                         data.addXYPair(beginRightKey, speedDifference, criticalHeadway.getInUnit(METER));
187                     }
188                     criticalHeadway = lcs.findDecisionPoint(MIDPOINT, LaneChangeGraph.UPPERBOUND, speed,
189                             new Speed(speedDifference, KM_PER_HOUR), laneChangeModel, true);
190                     if (null != criticalHeadway)
191                     {
192                         data.addXYPair(endRightKey, speedDifference, criticalHeadway.getInUnit(METER));
193                     }
194                     criticalHeadway = lcs.findDecisionPoint(LaneChangeGraph.LOWERBOUND, MIDPOINT, speed,
195                             new Speed(speedDifference, KM_PER_HOUR), laneChangeModel, false);
196                     if (null != criticalHeadway)
197                     {
198                         data.addXYPair(beginLeftKey, speedDifference, criticalHeadway.getInUnit(METER));
199                     }
200                     else
201                     {
202                         lcs.findDecisionPoint(LaneChangeGraph.LOWERBOUND, MIDPOINT, speed,
203                                 new Speed(speedDifference, KM_PER_HOUR), laneChangeModel, false);
204                     }
205                     criticalHeadway = lcs.findDecisionPoint(MIDPOINT, LaneChangeGraph.UPPERBOUND, speed,
206                             new Speed(speedDifference, KM_PER_HOUR), laneChangeModel, false);
207                     if (null != criticalHeadway)
208                     {
209                         data.addXYPair(endLeftKey, speedDifference, criticalHeadway.getInUnit(METER));
210                     }
211                     Plot plot = lcs.charts[row][index].getChart().getPlot();
212                     plot.notifyListeners(new PlotChangeEvent(plot));
213                 }
214             }
215         }
216 
217     }
218 
219     /**
220      * Then execution start point.
221      * @param args String[]; the command line arguments (not used)
222      * @throws NamingException on ???
223      * @throws NetworkException on network inconsistency
224      * @throws SimRuntimeException on ???
225      * @throws GTUException on error during GTU construction
226      */
227     public static void buildGUI(final String[] args) throws NamingException, NetworkException, SimRuntimeException, GTUException
228     {
229         JPanel mainPanel = new JPanel(new BorderLayout());
230         lcs = new LaneChangeGraph("Lane change graphs", mainPanel);
231         TablePanel chartsPanel = new TablePanel(STANDARDSPEEDS.length, 2);
232         mainPanel.add(chartsPanel, BorderLayout.CENTER);
233         for (int index = 0; index < STANDARDSPEEDS.length; index++)
234         {
235             lcs.charts[0][index] =
236                     new ChartPanel(lcs.createChart(String.format("Egoistic reference car at %.0fkm/h", STANDARDSPEEDS[index]),
237                             STANDARDSPEEDS[index]));
238             chartsPanel.setCell(lcs.charts[0][index], index, 0);
239         }
240         for (int index = 0; index < STANDARDSPEEDS.length; index++)
241         {
242             lcs.charts[1][index] =
243                     new ChartPanel(lcs.createChart(String.format("Altruistic reference car at %.0fkm/h", STANDARDSPEEDS[index]),
244                             STANDARDSPEEDS[index]));
245             chartsPanel.setCell(lcs.charts[1][index], index, 1);
246         }
247         lcs.pack();
248         lcs.setExtendedState(Frame.MAXIMIZED_BOTH);
249         lcs.setVisible(true);
250     }
251 
252     /**
253      * Find the headway at which the decision to merge right changes.
254      * @param minHeadway Length; minimum headway to consider
255      * @param maxHeadway Length; maximum headway to consider
256      * @param referenceSpeed Speed; speed of the reference car
257      * @param speedDifference Speed; speed of the other car minus speed of the reference car
258      * @param laneChangeModel LaneChangeModel; the lane change model to apply
259      * @param mergeRight boolean; if true; merge right is tested; if false; merge left is tested
260      * @return Length
261      * @throws NamingException on ???
262      * @throws NetworkException on network inconsistency
263      * @throws SimRuntimeException on ???
264      * @throws GTUException on error during GTU construction
265      * @throws OTSGeometryException x
266      * @throws ParameterException in case of a parameter problem.
267      * @throws OperationalPlanException x
268      */
269     private Length findDecisionPoint(final Length minHeadway, final Length maxHeadway, final Speed referenceSpeed,
270             final Speed speedDifference, final LaneChangeModel laneChangeModel, final boolean mergeRight)
271             throws NamingException, NetworkException, SimRuntimeException, GTUException, OTSGeometryException,
272             ParameterException, OperationalPlanException
273     {
274         Length high = maxHeadway;
275         Length low = minHeadway;
276 
277         // The reference car only needs a simulator
278         // But that needs a model (which this class implements)
279         OTSSimulator simulator = new OTSSimulator("LaneChangeGraph");
280         simulator.initialize(Time.ZERO, Duration.ZERO, Duration.instantiateSI(3600.0), this);
281 
282         // Set up the network
283         GTUType gtuType = this.network.getGtuType(GTUType.DEFAULTS.CAR);
284         LaneType laneType = this.network.getLaneType(LaneType.DEFAULTS.TWO_WAY_LANE);
285         final Speed speedLimit = new Speed(120, KM_PER_HOUR);
286 
287         Lane[] lanes = LaneFactory.makeMultiLane(this.network, "Road with two lanes",
288                 new OTSRoadNode(this.network, "From", new OTSPoint3D(LOWERBOUND.getSI(), 0, 0), Direction.ZERO),
289                 new OTSRoadNode(this.network, "To", new OTSPoint3D(UPPERBOUND.getSI(), 0, 0), Direction.ZERO), null, 2,
290                 laneType, speedLimit, simulator);
291 
292         // Create the reference vehicle
293         Set<DirectedLanePosition> initialLongitudinalPositions = new LinkedHashSet<>(1);
294         initialLongitudinalPositions
295                 .add(new DirectedLanePosition(lanes[mergeRight ? 0 : 1], new Length(0, METER), GTUDirectionality.DIR_PLUS));
296 
297         this.carFollowingModel = new IDMPlusOld(new Acceleration(1, METER_PER_SECOND_2),
298                 new Acceleration(1.5, METER_PER_SECOND_2), new Length(2, METER), new Duration(1, SECOND), 1d);
299         this.carFollowingModel = new IDMOld(new Acceleration(1, METER_PER_SECOND_2), new Acceleration(1.5, METER_PER_SECOND_2),
300                 new Length(2, METER), new Duration(1, SECOND), 1d);
301 
302         LaneBasedIndividualGTU referenceCar = new LaneBasedIndividualGTU("ReferenceCar", gtuType, new Length(4, METER),
303                 new Length(2, METER), new Speed(150, KM_PER_HOUR), Length.instantiateSI(2.0), simulator, this.network);
304         referenceCar.setParameters(DefaultsFactory.getDefaultParameters());
305         LaneBasedStrategicalPlanner strategicalPlanner = new LaneBasedStrategicalRoutePlanner(
306                 new LaneBasedCFLCTacticalPlanner(this.carFollowingModel, laneChangeModel, referenceCar), referenceCar);
307         referenceCar.init(strategicalPlanner, initialLongitudinalPositions, referenceSpeed);
308         Collection<Headway> sameLaneGTUs = new LinkedHashSet<>();
309         sameLaneGTUs.add(
310                 new HeadwayGTUSimple(referenceCar.getId(), referenceCar.getGTUType(), Length.ZERO, referenceCar.getLength(),
311                         referenceCar.getWidth(), referenceCar.getSpeed(), referenceCar.getAcceleration(), null));
312         // TODO play with the speed limit
313         // TODO play with the preferredLaneRouteIncentive
314         LaneMovementStep lowResult = computeLaneChange(referenceCar, sameLaneGTUs, speedLimit, laneChangeModel, low, lanes[1],
315                 speedDifference, mergeRight);
316         LaneMovementStep highResult = computeLaneChange(referenceCar, sameLaneGTUs, speedLimit, laneChangeModel, high, lanes[1],
317                 speedDifference, mergeRight);
318         Length mid = null;
319         if (lowResult.getLaneChangeDirection() != highResult.getLaneChangeDirection())
320         {
321             // Use bisection to home in onto the decision point
322             final double delta = 0.1; // [m]
323             final int stepsNeeded = (int) Math.ceil(Math.log(DoubleScalar.minus(high, low).getSI() / delta) / Math.log(2));
324             for (int step = 0; step < stepsNeeded; step++)
325             {
326                 Length mutableMid = low.plus(high).divide(2);
327                 mid = mutableMid;
328                 LaneMovementStep midResult = computeLaneChange(referenceCar, sameLaneGTUs, speedLimit, laneChangeModel, mid,
329                         lanes[1], speedDifference, mergeRight);
330                 // System.out.println(String.format ("mid %.2fm: %s", mid.getSI(), midResult));
331                 if (midResult.getLaneChangeDirection() != lowResult.getLaneChangeDirection())
332                 {
333                     high = mid;
334                     highResult = midResult;
335                 }
336                 else
337                 {
338                     low = mid;
339                     lowResult = midResult;
340                 }
341             }
342         }
343         else
344         {
345             // System.out.println("Bisection failed");
346             computeLaneChange(referenceCar, sameLaneGTUs, speedLimit, laneChangeModel, low, lanes[1], speedDifference,
347                     mergeRight);
348         }
349         return mid;
350     }
351 
352     /**
353      * @param referenceCar LaneBasedIndividualGTU; the reference GTU
354      * @param sameLaneGTUs Collection&lt;Headway&gt;; the set of GTUs in the same lane as the
355      *            &lt;cite&gt;referenceCar&lt;/cite&gt;
356      * @param speedLimit Speed; the speed limit
357      * @param laneChangeModel LaneChangeModel; the lane change model
358      * @param otherCarPosition Length; the position of the other car
359      * @param otherCarLane Lane; the lane of the other car
360      * @param deltaV Speed; the speed difference
361      * @param mergeRight boolean; if true; merging direction is to the right; if false; merging direction is to the left
362      * @return LaneMovementStep
363      * @throws NamingException on ???
364      * @throws SimRuntimeException on ???
365      * @throws NetworkException on network inconsistency
366      * @throws GTUException on error during GTU construction
367      * @throws OTSGeometryException when the initial position is outside the lane's center line
368      * @throws ParameterException in case of a parameter problem.
369      * @throws OperationalPlanException x
370      */
371     private LaneMovementStep computeLaneChange(final LaneBasedIndividualGTU referenceCar,
372             final Collection<Headway> sameLaneGTUs, final Speed speedLimit, final LaneChangeModel laneChangeModel,
373             final Length otherCarPosition, final Lane otherCarLane, final Speed deltaV, final boolean mergeRight)
374             throws NamingException, NetworkException, SimRuntimeException, GTUException, OTSGeometryException,
375             ParameterException, OperationalPlanException
376     {
377         Set<DirectedLanePosition> initialLongitudinalPositions = new LinkedHashSet<>(1);
378         initialLongitudinalPositions.add(new DirectedLanePosition(otherCarLane, otherCarPosition, GTUDirectionality.DIR_PLUS));
379         LaneBasedIndividualGTU otherCar =
380                 new LaneBasedIndividualGTU("otherCar", referenceCar.getGTUType(), new Length(4, METER), new Length(2, METER),
381                         new Speed(150, KM_PER_HOUR), Length.instantiateSI(2.0), referenceCar.getSimulator(), this.network);
382         otherCar.setParameters(DefaultsFactory.getDefaultParameters());
383         LaneBasedStrategicalPlanner strategicalPlanner = new LaneBasedStrategicalRoutePlanner(
384                 new LaneBasedCFLCTacticalPlanner(this.carFollowingModel, laneChangeModel, otherCar), otherCar);
385         otherCar.init(strategicalPlanner, initialLongitudinalPositions, referenceCar.getSpeed().plus(deltaV));
386         Collection<Headway> preferredLaneGTUs = new LinkedHashSet<>();
387         Collection<Headway> nonPreferredLaneGTUs = new LinkedHashSet<>();
388         Length referenceCarPosition = referenceCar.position(
389                 referenceCar.positions(referenceCar.getReference()).keySet().iterator().next(), referenceCar.getReference());
390         Headway otherHeadwayGTU =
391                 new HeadwayGTUSimple(otherCar.getId(), otherCar.getGTUType(), otherCarPosition.minus(referenceCarPosition),
392                         otherCar.getLength(), otherCar.getWidth(), otherCar.getSpeed(), otherCar.getAcceleration(), null);
393         if (mergeRight)
394         {
395             preferredLaneGTUs.add(otherHeadwayGTU);
396         }
397         else
398         {
399             sameLaneGTUs.add(otherHeadwayGTU);
400         }
401         // System.out.println(referenceCar);
402         // System.out.println(otherCar);
403         LaneMovementStep result = laneChangeModel.computeLaneChangeAndAcceleration(referenceCar, sameLaneGTUs,
404                 mergeRight ? preferredLaneGTUs : null, mergeRight ? null : nonPreferredLaneGTUs, speedLimit,
405                 new Acceleration(0.3, METER_PER_SECOND_2), new Acceleration(0.1, METER_PER_SECOND_2),
406                 new Acceleration(-0.3, METER_PER_SECOND_2));
407         // System.out.println(result);
408         sameLaneGTUs.remove(otherHeadwayGTU);
409         otherCar.destroy();
410         return result;
411     }
412 
413     /**
414      * @param caption String; the caption for the chart
415      * @param speed double; the speed of the reference vehicle
416      * @return new JFreeChart
417      */
418     private JFreeChart createChart(final String caption, final double speed)
419     {
420         ChartFactory.setChartTheme(new StandardChartTheme("JFree/Shadow", false));
421         ChartData chartData = new ChartData();
422         JFreeChart chartPanel =
423                 ChartFactory.createXYLineChart(caption, "", "", chartData, PlotOrientation.VERTICAL, false, false, false);
424         NumberAxis xAxis = new NumberAxis("\u2192 " + "\u0394v (other car speed minus reference car speed) [km/h]");
425         xAxis.setAutoRangeIncludesZero(true);
426         double minimumDifference = -30;
427         xAxis.setRange(minimumDifference, minimumDifference + 60);
428         xAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
429         NumberAxis yAxis = new NumberAxis("\u2192 " + "gross headway (\u0394s) [m]");
430         yAxis.setAutoRangeIncludesZero(true);
431         yAxis.setRange(LOWERBOUND.getSI(), UPPERBOUND.getSI());
432         yAxis.setInverted(true);
433         chartPanel.getXYPlot().setDomainAxis(xAxis);
434         chartPanel.getXYPlot().setRangeAxis(yAxis);
435         final XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) chartPanel.getXYPlot().getRenderer();
436         renderer.setDefaultLinesVisible(true);
437         renderer.setDefaultShapesVisible(false);
438         renderer.setDefaultShape(new Line2D.Float(0, 0, 0, 0));
439         return chartPanel;
440     }
441 
442     /** {@inheritDoc} */
443     @Override
444     public void constructModel()
445     {
446         // Do nothing
447     }
448 
449     /** {@inheritDoc} */
450     @Override
451     public final OTSRoadNetwork getNetwork()
452     {
453         return this.network;
454     }
455 
456     /** {@inheritDoc} */
457     @Override
458     public OTSSimulatorInterface getSimulator()
459     {
460         return null;
461     }
462 
463     /** {@inheritDoc} */
464     @Override
465     public InputParameterMap getInputParameterMap()
466     {
467         return null;
468     }
469 
470     /** {@inheritDoc} */
471     @Override
472     public String getShortName()
473     {
474         return null;
475     }
476 
477     /** {@inheritDoc} */
478     @Override
479     public String getDescription()
480     {
481         return null;
482     }
483 
484     /** {@inheritDoc} */
485     @Override
486     public List<StatisticsInterface<Duration>> getOutputStatistics()
487     {
488         return null;
489     }
490 
491     /** {@inheritDoc} */
492     @Override
493     public void setStreamInformation(final StreamInformation streamInformation)
494     {
495         //
496     }
497 
498     /** {@inheritDoc} */
499     @Override
500     public StreamInformation getStreamInformation()
501     {
502         return null;
503     }
504 
505 }
506 
507 /** */
508 class ChartData implements XYDataset
509 {
510 
511     /** The X values. */
512     private ArrayList<ArrayList<Double>> xValues = new ArrayList<>();
513 
514     /** The Y values. */
515     private ArrayList<ArrayList<Double>> yValues = new ArrayList<>();
516 
517     /** The names of the series. */
518     private ArrayList<String> seriesKeys = new ArrayList<>();
519 
520     /** List of parties interested in changes of this ContourPlot. */
521     private transient EventListenerList listenerList = new EventListenerList();
522 
523     /** Not used internally. */
524     private DatasetGroup datasetGroup = null;
525 
526     /**
527      * Add storage for another series of XY values.
528      * @param seriesName String; the name of the new series
529      * @return int; the index to use to address the new series
530      */
531     public final int addSeries(final String seriesName)
532     {
533         this.xValues.add(new ArrayList<Double>());
534         this.yValues.add(new ArrayList<Double>());
535         this.seriesKeys.add(seriesName);
536         return this.xValues.size() - 1;
537     }
538 
539     /**
540      * Add an XY pair to the data.
541      * @param seriesKey int; key to the data series
542      * @param x double; x value of the pair
543      * @param y double; y value of the pair
544      */
545     public final void addXYPair(final int seriesKey, final double x, final double y)
546     {
547         this.xValues.get(seriesKey).add(x);
548         this.yValues.get(seriesKey).add(y);
549     }
550 
551     /** {@inheritDoc} */
552     @Override
553     public final int getSeriesCount()
554     {
555         return this.seriesKeys.size();
556     }
557 
558     /** {@inheritDoc} */
559     @Override
560     public final Comparable<?> getSeriesKey(final int series)
561     {
562         return this.seriesKeys.get(series);
563     }
564 
565     /** {@inheritDoc} */
566     @Override
567     public final int indexOf(@SuppressWarnings("rawtypes") final Comparable seriesKey)
568     {
569         return this.seriesKeys.indexOf(seriesKey);
570     }
571 
572     /** {@inheritDoc} */
573     @Override
574     public final void addChangeListener(final DatasetChangeListener listener)
575     {
576         this.listenerList.add(DatasetChangeListener.class, listener);
577     }
578 
579     /** {@inheritDoc} */
580     @Override
581     public final void removeChangeListener(final DatasetChangeListener listener)
582     {
583         this.listenerList.remove(DatasetChangeListener.class, listener);
584     }
585 
586     /** {@inheritDoc} */
587     @Override
588     public final DatasetGroup getGroup()
589     {
590         return this.datasetGroup;
591     }
592 
593     /** {@inheritDoc} */
594     @Override
595     public final void setGroup(final DatasetGroup group)
596     {
597         this.datasetGroup = group;
598     }
599 
600     /** {@inheritDoc} */
601     @Override
602     public final DomainOrder getDomainOrder()
603     {
604         return DomainOrder.ASCENDING;
605     }
606 
607     /** {@inheritDoc} */
608     @Override
609     public final int getItemCount(final int series)
610     {
611         return this.xValues.get(series).size();
612     }
613 
614     /** {@inheritDoc} */
615     @Override
616     public final Number getX(final int series, final int item)
617     {
618         return this.xValues.get(series).get(item);
619     }
620 
621     /** {@inheritDoc} */
622     @Override
623     public final double getXValue(final int series, final int item)
624     {
625         return this.xValues.get(series).get(item);
626     }
627 
628     /** {@inheritDoc} */
629     @Override
630     public final Number getY(final int series, final int item)
631     {
632         return this.yValues.get(series).get(item);
633     }
634 
635     /** {@inheritDoc} */
636     @Override
637     public final double getYValue(final int series, final int item)
638     {
639         return this.yValues.get(series).get(item);
640     }
641 
642 }