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