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.model.inputparameters.InputParameterMap;
74  import nl.tudelft.simulation.dsol.model.outputstatistics.OutputStatistic;
75  import nl.tudelft.simulation.dsol.swing.gui.TablePanel;
76  
77  /**
78   * Create a plot that characterizes a lane change graph.
79   * <p>
80   * Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
81   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
82   * <p>
83   * $LastChangedDate: 2020-05-08 11:08:12 +0200 (Fri, 08 May 2020) $, @version $Revision: 6464 $, by $Author: pknoppers $,
84   * 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./../../../org/opentrafficsim/demo/lanechange/LaneChangeGraph.html#ChartData">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/LaneChangeGraph.html#ChartData">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 List<OutputStatistic<?>> getOutputStatistics()
473     {
474         return null;
475     }
476 
477     /** {@inheritDoc} */
478     @Override
479     public String getShortName()
480     {
481         return null;
482     }
483 
484     /** {@inheritDoc} */
485     @Override
486     public String getDescription()
487     {
488         return null;
489     }
490 
491 }
492 
493 /** */
494 class ChartData implements XYDataset
495 {
496 
497     /** The X values. */
498     private ArrayList<ArrayList<Double>> xValues = new ArrayList<>();
499 
500     /** The Y values. */
501     private ArrayList<ArrayList<Double>> yValues = new ArrayList<>();
502 
503     /** The names of the series. */
504     private ArrayList<String> seriesKeys = new ArrayList<>();
505 
506     /** List of parties interested in changes of this ContourPlot. */
507     private transient EventListenerList listenerList = new EventListenerList();
508 
509     /** Not used internally. */
510     private DatasetGroup datasetGroup = null;
511 
512     /**
513      * Add storage for another series of XY values.
514      * @param seriesName String; the name of the new series
515      * @return int; the index to use to address the new series
516      */
517     public final int addSeries(final String seriesName)
518     {
519         this.xValues.add(new ArrayList<Double>());
520         this.yValues.add(new ArrayList<Double>());
521         this.seriesKeys.add(seriesName);
522         return this.xValues.size() - 1;
523     }
524 
525     /**
526      * Add an XY pair to the data.
527      * @param seriesKey int; key to the data series
528      * @param x double; x value of the pair
529      * @param y double; y value of the pair
530      */
531     public final void addXYPair(final int seriesKey, final double x, final double y)
532     {
533         this.xValues.get(seriesKey).add(x);
534         this.yValues.get(seriesKey).add(y);
535     }
536 
537     /** {@inheritDoc} */
538     @Override
539     public final int getSeriesCount()
540     {
541         return this.seriesKeys.size();
542     }
543 
544     /** {@inheritDoc} */
545     @Override
546     public final Comparable<?> getSeriesKey(final int series)
547     {
548         return this.seriesKeys.get(series);
549     }
550 
551     /** {@inheritDoc} */
552     @Override
553     public final int indexOf(@SuppressWarnings("rawtypes") final Comparable seriesKey)
554     {
555         return this.seriesKeys.indexOf(seriesKey);
556     }
557 
558     /** {@inheritDoc} */
559     @Override
560     public final void addChangeListener(final DatasetChangeListener listener)
561     {
562         this.listenerList.add(DatasetChangeListener.class, listener);
563     }
564 
565     /** {@inheritDoc} */
566     @Override
567     public final void removeChangeListener(final DatasetChangeListener listener)
568     {
569         this.listenerList.remove(DatasetChangeListener.class, listener);
570     }
571 
572     /** {@inheritDoc} */
573     @Override
574     public final DatasetGroup getGroup()
575     {
576         return this.datasetGroup;
577     }
578 
579     /** {@inheritDoc} */
580     @Override
581     public final void setGroup(final DatasetGroup group)
582     {
583         this.datasetGroup = group;
584     }
585 
586     /** {@inheritDoc} */
587     @Override
588     public final DomainOrder getDomainOrder()
589     {
590         return DomainOrder.ASCENDING;
591     }
592 
593     /** {@inheritDoc} */
594     @Override
595     public final int getItemCount(final int series)
596     {
597         return this.xValues.get(series).size();
598     }
599 
600     /** {@inheritDoc} */
601     @Override
602     public final Number getX(final int series, final int item)
603     {
604         return this.xValues.get(series).get(item);
605     }
606 
607     /** {@inheritDoc} */
608     @Override
609     public final double getXValue(final int series, final int item)
610     {
611         return this.xValues.get(series).get(item);
612     }
613 
614     /** {@inheritDoc} */
615     @Override
616     public final Number getY(final int series, final int item)
617     {
618         return this.yValues.get(series).get(item);
619     }
620 
621     /** {@inheritDoc} */
622     @Override
623     public final double getYValue(final int series, final int item)
624     {
625         return this.yValues.get(series).get(item);
626     }
627 
628 }