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.Set;
13  
14  import javax.naming.NamingException;
15  import javax.swing.JFrame;
16  import javax.swing.JPanel;
17  import javax.swing.SwingUtilities;
18  import javax.swing.event.EventListenerList;
19  
20  import org.djunits.unit.UNITS;
21  import org.djunits.value.vdouble.scalar.Acceleration;
22  import org.djunits.value.vdouble.scalar.DoubleScalar;
23  import org.djunits.value.vdouble.scalar.Duration;
24  import org.djunits.value.vdouble.scalar.Length;
25  import org.djunits.value.vdouble.scalar.Speed;
26  import org.djunits.value.vdouble.scalar.Time;
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.base.parameters.ParameterSet;
42  import org.opentrafficsim.base.parameters.Parameters;
43  import org.opentrafficsim.core.dsol.OTSModelInterface;
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.Network;
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.road.gtu.lane.LaneBasedIndividualGTU;
55  import org.opentrafficsim.road.gtu.lane.perception.headway.Headway;
56  import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTUSimple;
57  import org.opentrafficsim.road.gtu.lane.tactical.LaneBasedCFLCTacticalPlanner;
58  import org.opentrafficsim.road.gtu.lane.tactical.following.GTUFollowingModelOld;
59  import org.opentrafficsim.road.gtu.lane.tactical.following.IDMOld;
60  import org.opentrafficsim.road.gtu.lane.tactical.following.IDMPlusOld;
61  import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.Altruistic;
62  import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.Egoistic;
63  import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.LaneChangeModel;
64  import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.LaneMovementStep;
65  import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalPlanner;
66  import org.opentrafficsim.road.gtu.strategical.route.LaneBasedStrategicalRoutePlanner;
67  import org.opentrafficsim.road.network.factory.LaneFactory;
68  import org.opentrafficsim.road.network.lane.DirectedLanePosition;
69  import org.opentrafficsim.road.network.lane.Lane;
70  import org.opentrafficsim.road.network.lane.LaneType;
71  import org.opentrafficsim.simulationengine.SimpleSimulator;
72  
73  import nl.tudelft.simulation.dsol.SimRuntimeException;
74  import nl.tudelft.simulation.dsol.gui.swing.TablePanel;
75  import nl.tudelft.simulation.dsol.simtime.SimTimeDoubleUnit;
76  import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
77  
78  /**
79   * Create a plot that characterizes a lane change graph.
80   * <p>
81   * Copyright (c) 2013-2018 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: 2018-09-19 13:55:45 +0200 (Wed, 19 Sep 2018) $, @version $Revision: 4006 $, 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         setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
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 
149                 @Override
150                 public void run()
151                 {
152                     try
153                     {
154                         buildGUI(args);
155                     }
156                     catch (NamingException | NetworkException | SimRuntimeException | GTUException exception)
157                     {
158                         exception.printStackTrace();
159                     }
160                 }
161 
162             });
163         }
164         catch (InvocationTargetException | InterruptedException exception)
165         {
166             exception.printStackTrace();
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 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 low minimum headway to consider
257      * @param high 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(Length low, Length high, final Speed referenceSpeed, final Speed speedDifference,
272             final LaneChangeModel laneChangeModel, final boolean mergeRight) throws NamingException, NetworkException,
273             SimRuntimeException, GTUException, OTSGeometryException, ParameterException, OperationalPlanException
274     {
275         // Set up the network
276         Network network = new OTSNetwork("lane change graph network");
277         GTUType gtuType = CAR;
278         LaneType laneType = LaneType.TWO_WAY_LANE;
279         final Speed speedLimit = new Speed(120, KM_PER_HOUR);
280 
281         Lane[] lanes = LaneFactory.makeMultiLane(network, "Road with two lanes",
282                 new OTSNode(network, "From", new OTSPoint3D(LOWERBOUND.getSI(), 0, 0)),
283                 new OTSNode(network, "To", new OTSPoint3D(UPPERBOUND.getSI(), 0, 0)), null, 2, laneType, speedLimit, null);
284         // Create the reference vehicle
285         Set<DirectedLanePosition> initialLongitudinalPositions = new LinkedHashSet<>(1);
286         initialLongitudinalPositions
287                 .add(new DirectedLanePosition(lanes[mergeRight ? 0 : 1], new Length(0, METER), GTUDirectionality.DIR_PLUS));
288 
289         // The reference car only needs a simulator
290         // But that needs a model (which this class implements)
291         SimpleSimulator simpleSimulator = new SimpleSimulator(Time.ZERO, Duration.ZERO, new Duration(3600.0, SECOND), this);
292         this.carFollowingModel = new IDMPlusOld(new Acceleration(1, METER_PER_SECOND_2),
293                 new Acceleration(1.5, METER_PER_SECOND_2), new Length(2, METER), new Duration(1, SECOND), 1d);
294         this.carFollowingModel = new IDMOld(new Acceleration(1, METER_PER_SECOND_2), new Acceleration(1.5, METER_PER_SECOND_2),
295                 new Length(2, METER), new Duration(1, SECOND), 1d);
296 
297         Parameters parameters = new ParameterSet();
298         LaneBasedIndividualGTU referenceCar = new LaneBasedIndividualGTU("ReferenceCar", gtuType, new Length(4, METER),
299                 new Length(2, METER), new Speed(150, KM_PER_HOUR), Length.createSI(2.0), simpleSimulator, this.network);
300         LaneBasedStrategicalPlanner strategicalPlanner = new LaneBasedStrategicalRoutePlanner(
301                 new LaneBasedCFLCTacticalPlanner(this.carFollowingModel, laneChangeModel, referenceCar), referenceCar);
302         referenceCar.setParameters(parameters);
303         referenceCar.init(strategicalPlanner, initialLongitudinalPositions, referenceSpeed);
304         Collection<Headway> sameLaneGTUs = new LinkedHashSet<>();
305         sameLaneGTUs.add(
306                 new HeadwayGTUSimple(referenceCar.getId(), referenceCar.getGTUType(), Length.ZERO, referenceCar.getLength(),
307                         referenceCar.getWidth(), referenceCar.getSpeed(), referenceCar.getAcceleration(), null));
308         // TODO play with the speed limit
309         // TODO play with the preferredLaneRouteIncentive
310         LaneMovementStep lowResult = computeLaneChange(referenceCar, sameLaneGTUs, speedLimit, laneChangeModel, low, lanes[1],
311                 speedDifference, mergeRight);
312         LaneMovementStep highResult = computeLaneChange(referenceCar, sameLaneGTUs, speedLimit, laneChangeModel, high, lanes[1],
313                 speedDifference, mergeRight);
314         Length mid = null;
315         if (lowResult.getLaneChangeDirection() != highResult.getLaneChangeDirection())
316         {
317             // Use bisection to home in onto the decision point
318             final double delta = 0.1; // [m]
319             final int stepsNeeded = (int) Math.ceil(Math.log(DoubleScalar.minus(high, low).getSI() / delta) / Math.log(2));
320             for (int step = 0; step < stepsNeeded; step++)
321             {
322                 Length mutableMid = low.plus(high).divideBy(2);
323                 mid = mutableMid;
324                 LaneMovementStep midResult = computeLaneChange(referenceCar, sameLaneGTUs, speedLimit, laneChangeModel, mid,
325                         lanes[1], speedDifference, mergeRight);
326                 // System.out.println(String.format ("mid %.2fm: %s", mid.getSI(), midResult));
327                 if (midResult.getLaneChangeDirection() != lowResult.getLaneChangeDirection())
328                 {
329                     high = mid;
330                     highResult = midResult;
331                 }
332                 else
333                 {
334                     low = mid;
335                     lowResult = midResult;
336                 }
337             }
338         }
339         else
340         {
341             // System.out.println("Bisection failed");
342             computeLaneChange(referenceCar, sameLaneGTUs, speedLimit, laneChangeModel, low, lanes[1], speedDifference,
343                     mergeRight);
344         }
345         return mid;
346     }
347 
348     /**
349      * @param referenceCar LaneBasedIndifidualCar&lt;String&gt;; the reference GTU
350      * @param sameLaneGTUs Collection&lt;HeadwayGTU&gt;; the set of GTUs in the same lane as the <cite>referenceCar</cite>
351      * @param speedLimit Speed; the speed limit
352      * @param laneChangeModel LaneChangeModel; the lane change model
353      * @param otherCarPosition Length; the position of the other car
354      * @param otherCarLane Lane; the lane of the other car
355      * @param deltaV Speed; the speed difference
356      * @param mergeRight boolean; if true; merging direction is to the right; if false; merging direction is to the left
357      * @return LaneMovementStep
358      * @throws NamingException on ???
359      * @throws SimRuntimeException on ???
360      * @throws NetworkException on network inconsistency
361      * @throws GTUException on error during GTU construction
362      * @throws OTSGeometryException when the initial position is outside the lane's center line
363      * @throws ParameterException in case of a parameter problem.
364      * @throws OperationalPlanException x
365      */
366     private LaneMovementStep computeLaneChange(final LaneBasedIndividualGTU referenceCar,
367             final Collection<Headway> sameLaneGTUs, final Speed speedLimit, final LaneChangeModel laneChangeModel,
368             final Length otherCarPosition, final Lane otherCarLane, final Speed deltaV, final boolean mergeRight)
369             throws NamingException, NetworkException, SimRuntimeException, GTUException, OTSGeometryException,
370             ParameterException, OperationalPlanException
371     {
372         Set<DirectedLanePosition> initialLongitudinalPositions = new LinkedHashSet<>(1);
373         initialLongitudinalPositions.add(new DirectedLanePosition(otherCarLane, otherCarPosition, GTUDirectionality.DIR_PLUS));
374         Parameters parameters = new ParameterSet();
375         LaneBasedIndividualGTU otherCar =
376                 new LaneBasedIndividualGTU("otherCar", referenceCar.getGTUType(), new Length(4, METER), new Length(2, METER),
377                         new Speed(150, KM_PER_HOUR), Length.createSI(2.0), referenceCar.getSimulator(), this.network);
378         LaneBasedStrategicalPlanner strategicalPlanner = new LaneBasedStrategicalRoutePlanner(
379                 new LaneBasedCFLCTacticalPlanner(this.carFollowingModel, laneChangeModel, otherCar), otherCar);
380         otherCar.setParameters(parameters);
381         otherCar.init(strategicalPlanner, initialLongitudinalPositions, referenceCar.getSpeed().plus(deltaV));
382         Collection<Headway> preferredLaneGTUs = new LinkedHashSet<>();
383         Collection<Headway> nonPreferredLaneGTUs = new LinkedHashSet<>();
384         Length referenceCarPosition = referenceCar.position(
385                 referenceCar.positions(referenceCar.getReference()).keySet().iterator().next(), referenceCar.getReference());
386         Headway otherHeadwayGTU =
387                 new HeadwayGTUSimple(otherCar.getId(), otherCar.getGTUType(), otherCarPosition.minus(referenceCarPosition),
388                         otherCar.getLength(), otherCar.getWidth(), otherCar.getSpeed(), otherCar.getAcceleration(), null);
389         if (mergeRight)
390         {
391             preferredLaneGTUs.add(otherHeadwayGTU);
392         }
393         else
394         {
395             sameLaneGTUs.add(otherHeadwayGTU);
396         }
397         // System.out.println(referenceCar);
398         // System.out.println(otherCar);
399         LaneMovementStep result = laneChangeModel.computeLaneChangeAndAcceleration(referenceCar, sameLaneGTUs,
400                 mergeRight ? preferredLaneGTUs : null, mergeRight ? null : nonPreferredLaneGTUs, speedLimit,
401                 new Acceleration(0.3, METER_PER_SECOND_2), new Acceleration(0.1, METER_PER_SECOND_2),
402                 new Acceleration(-0.3, METER_PER_SECOND_2));
403         // System.out.println(result);
404         sameLaneGTUs.remove(otherHeadwayGTU);
405         otherCar.destroy();
406         return result;
407     }
408 
409     /**
410      * @param caption String; the caption for the chart
411      * @param speed double; the speed of the reference vehicle
412      * @return new JFreeChart
413      */
414     private JFreeChart createChart(final String caption, final double speed)
415     {
416         ChartFactory.setChartTheme(new StandardChartTheme("JFree/Shadow", false));
417         ChartData chartData = new ChartData();
418         JFreeChart chartPanel =
419                 ChartFactory.createXYLineChart(caption, "", "", chartData, PlotOrientation.VERTICAL, false, false, false);
420         NumberAxis xAxis = new NumberAxis("\u2192 " + "\u0394v (other car speed minus reference car speed) [km/h]");
421         xAxis.setAutoRangeIncludesZero(true);
422         double minimumDifference = -30;
423         xAxis.setRange(minimumDifference, minimumDifference + 60);
424         xAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
425         NumberAxis yAxis = new NumberAxis("\u2192 " + "gross headway (\u0394s) [m]");
426         yAxis.setAutoRangeIncludesZero(true);
427         yAxis.setRange(LOWERBOUND.getSI(), UPPERBOUND.getSI());
428         yAxis.setInverted(true);
429         chartPanel.getXYPlot().setDomainAxis(xAxis);
430         chartPanel.getXYPlot().setRangeAxis(yAxis);
431         final XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) chartPanel.getXYPlot().getRenderer();
432         renderer.setDefaultLinesVisible(true);
433         renderer.setDefaultShapesVisible(false);
434         renderer.setDefaultShape(new Line2D.Float(0, 0, 0, 0));
435         return chartPanel;
436     }
437 
438     /** {@inheritDoc} */
439     @Override
440     public void constructModel(final SimulatorInterface<Time, Duration, SimTimeDoubleUnit> simulator)
441             throws SimRuntimeException
442     {
443         // Do nothing
444     }
445 
446     /** {@inheritDoc} */
447     @Override
448     public final SimulatorInterface<Time, Duration, SimTimeDoubleUnit> getSimulator()
449     {
450         return null;
451     }
452 
453     /** {@inheritDoc} */
454     @Override
455     public final OTSNetwork getNetwork()
456     {
457         return this.network;
458     }
459 
460 }
461 
462 /** */
463 class ChartData implements XYDataset
464 {
465 
466     /** The X values. */
467     private ArrayList<ArrayList<Double>> xValues = new ArrayList<>();
468 
469     /** The Y values. */
470     private ArrayList<ArrayList<Double>> yValues = new ArrayList<>();
471 
472     /** The names of the series. */
473     private ArrayList<String> seriesKeys = new ArrayList<>();
474 
475     /** List of parties interested in changes of this ContourPlot. */
476     private transient EventListenerList listenerList = new EventListenerList();
477 
478     /** Not used internally. */
479     private DatasetGroup datasetGroup = null;
480 
481     /**
482      * Add storage for another series of XY values.
483      * @param seriesName String; the name of the new series
484      * @return int; the index to use to address the new series
485      */
486     public final int addSeries(final String seriesName)
487     {
488         this.xValues.add(new ArrayList<Double>());
489         this.yValues.add(new ArrayList<Double>());
490         this.seriesKeys.add(seriesName);
491         return this.xValues.size() - 1;
492     }
493 
494     /**
495      * Add an XY pair to the data.
496      * @param seriesKey int; key to the data series
497      * @param x double; x value of the pair
498      * @param y double; y value of the pair
499      */
500     public final void addXYPair(final int seriesKey, final double x, final double y)
501     {
502         this.xValues.get(seriesKey).add(x);
503         this.yValues.get(seriesKey).add(y);
504     }
505 
506     /** {@inheritDoc} */
507     @Override
508     public final int getSeriesCount()
509     {
510         return this.seriesKeys.size();
511     }
512 
513     /** {@inheritDoc} */
514     @Override
515     public final Comparable<?> getSeriesKey(final int series)
516     {
517         return this.seriesKeys.get(series);
518     }
519 
520     /** {@inheritDoc} */
521     @Override
522     public final int indexOf(@SuppressWarnings("rawtypes") final Comparable seriesKey)
523     {
524         return this.seriesKeys.indexOf(seriesKey);
525     }
526 
527     /** {@inheritDoc} */
528     @Override
529     public final void addChangeListener(final DatasetChangeListener listener)
530     {
531         this.listenerList.add(DatasetChangeListener.class, listener);
532     }
533 
534     /** {@inheritDoc} */
535     @Override
536     public final void removeChangeListener(final DatasetChangeListener listener)
537     {
538         this.listenerList.remove(DatasetChangeListener.class, listener);
539     }
540 
541     /** {@inheritDoc} */
542     @Override
543     public final DatasetGroup getGroup()
544     {
545         return this.datasetGroup;
546     }
547 
548     /** {@inheritDoc} */
549     @Override
550     public final void setGroup(final DatasetGroup group)
551     {
552         this.datasetGroup = group;
553     }
554 
555     /** {@inheritDoc} */
556     @Override
557     public final DomainOrder getDomainOrder()
558     {
559         return DomainOrder.ASCENDING;
560     }
561 
562     /** {@inheritDoc} */
563     @Override
564     public final int getItemCount(final int series)
565     {
566         return this.xValues.get(series).size();
567     }
568 
569     /** {@inheritDoc} */
570     @Override
571     public final Number getX(final int series, final int item)
572     {
573         return this.xValues.get(series).get(item);
574     }
575 
576     /** {@inheritDoc} */
577     @Override
578     public final double getXValue(final int series, final int item)
579     {
580         return this.xValues.get(series).get(item);
581     }
582 
583     /** {@inheritDoc} */
584     @Override
585     public final Number getY(final int series, final int item)
586     {
587         return this.yValues.get(series).get(item);
588     }
589 
590     /** {@inheritDoc} */
591     @Override
592     public final double getYValue(final int series, final int item)
593     {
594         return this.yValues.get(series).get(item);
595     }
596 
597 }