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