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