View Javadoc
1   package org.opentrafficsim.graphs;
2   
3   import static org.junit.Assert.assertEquals;
4   import static org.junit.Assert.assertFalse;
5   import static org.junit.Assert.assertTrue;
6   import static org.junit.Assert.fail;
7   
8   import java.awt.Component;
9   import java.awt.Container;
10  import java.awt.event.ActionEvent;
11  import java.awt.event.MouseListener;
12  import java.rmi.RemoteException;
13  import java.util.ArrayList;
14  import java.util.List;
15  
16  import javax.naming.NamingException;
17  import javax.swing.JLabel;
18  import javax.swing.JOptionPane;
19  
20  import nl.tudelft.simulation.dsol.SimRuntimeException;
21  import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
22  
23  import org.jfree.chart.ChartPanel;
24  import org.jfree.data.DomainOrder;
25  import org.junit.Test;
26  import org.opentrafficsim.core.car.CarTest;
27  import org.opentrafficsim.core.car.LaneBasedIndividualCar;
28  import org.opentrafficsim.core.dsol.OTSDEVSSimulator;
29  import org.opentrafficsim.core.dsol.OTSModelInterface;
30  import org.opentrafficsim.core.dsol.OTSSimTimeDouble;
31  import org.opentrafficsim.core.gtu.GTUException;
32  import org.opentrafficsim.core.gtu.following.FixedAccelerationModel;
33  import org.opentrafficsim.core.gtu.following.SequentialFixedAccelerationModel;
34  import org.opentrafficsim.core.gtu.lane.changing.Egoistic;
35  import org.opentrafficsim.core.gtu.lane.changing.LaneChangeModel;
36  import org.opentrafficsim.core.network.NetworkException;
37  import org.opentrafficsim.core.network.factory.LaneFactory;
38  import org.opentrafficsim.core.network.geotools.NodeGeotools;
39  import org.opentrafficsim.core.network.lane.Lane;
40  import org.opentrafficsim.core.network.lane.LaneType;
41  import org.opentrafficsim.core.unit.AccelerationUnit;
42  import org.opentrafficsim.core.unit.LengthUnit;
43  import org.opentrafficsim.core.unit.SpeedUnit;
44  import org.opentrafficsim.core.unit.TimeUnit;
45  import org.opentrafficsim.core.value.vdouble.scalar.DoubleScalar;
46  import org.opentrafficsim.core.value.vdouble.scalar.DoubleScalar.Abs;
47  import org.opentrafficsim.core.value.vdouble.scalar.DoubleScalar.Rel;
48  import org.opentrafficsim.simulationengine.SimpleSimulator;
49  
50  import com.vividsolutions.jts.geom.Coordinate;
51  
52  /**
53   * Test the non-GUI part of the ContourPlot class.
54   * <p>
55   * Copyright (c) 2013-2014 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights
56   * reserved. <br>
57   * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
58   * <p>
59   * @version Aug 21, 2014 <br>
60   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
61   */
62  public class ContourPlotTest
63  {
64      /**
65       * Create a dummy path for the tests.
66       * @return List&lt;Lane&gt;; the dummy path
67       * @throws NetworkException
68       * @throws NamingException
69       * @throws RemoteException
70       */
71      private List<Lane> dummyPath() throws RemoteException, NamingException, NetworkException
72      {
73          ArrayList<Lane> result = new ArrayList<Lane>();
74          Lane[] lanes =
75                  LaneFactory.makeMultiLane("AtoB", new NodeGeotools.STR("A", new Coordinate(1234, 0, 0)),
76                          new NodeGeotools.STR("B", new Coordinate(12345, 0, 0)), null, 1, new LaneType<String>(
77                                  "lane type"), null);
78          result.add(lanes[0]);
79          return result;
80      }
81  
82      /**
83       * Test the AccelerationContourPlot.
84       * @throws NamingException
85       * @throws SimRuntimeException
86       * @throws NetworkException
87       * @throws RemoteException
88       * @throws GTUException
89       */
90      @SuppressWarnings("static-method")
91      @Test
92      public final void accelerationContourTest() throws RemoteException, NetworkException, SimRuntimeException,
93              NamingException, GTUException
94      {
95          List<Lane> path = dummyPath();
96          AccelerationContourPlot acp = new AccelerationContourPlot("Acceleration", path);
97          assertTrue("newly created AccelerationContourPlot should not be null", null != acp);
98          assertEquals("SeriesKey should be \"acceleration\"", "acceleration", acp.getSeriesKey(0));
99          standardContourTests(acp, path.get(0), Double.NaN, 0);
100     }
101 
102     /**
103      * Test the DensityContourPlot.
104      * @throws NamingException
105      * @throws SimRuntimeException
106      * @throws NetworkException
107      * @throws RemoteException
108      * @throws GTUException
109      */
110     @SuppressWarnings("static-method")
111     @Test
112     public final void densityContourTest() throws RemoteException, NetworkException, SimRuntimeException,
113             NamingException, GTUException
114     {
115         List<Lane> path = dummyPath();
116         DensityContourPlot dcp = new DensityContourPlot("Density", path);
117         assertTrue("newly created DensityContourPlot should not be null", null != dcp);
118         assertEquals("SeriesKey should be \"density\"", "density", dcp.getSeriesKey(0));
119         standardContourTests(dcp, path.get(0), 0, Double.NaN);
120     }
121 
122     /**
123      * Test the FlowContourPlot.
124      * @throws NamingException
125      * @throws SimRuntimeException
126      * @throws NetworkException
127      * @throws RemoteException
128      * @throws GTUException
129      */
130     @SuppressWarnings("static-method")
131     @Test
132     public final void flowContourTest() throws RemoteException, NetworkException, SimRuntimeException, NamingException,
133             GTUException
134     {
135         List<Lane> path = dummyPath();
136         FlowContourPlot fcp = new FlowContourPlot("Density", path);
137         assertTrue("newly created DensityContourPlot should not be null", null != fcp);
138         assertEquals("SeriesKey should be \"flow\"", "flow", fcp.getSeriesKey(0));
139         standardContourTests(fcp, path.get(0), 0, Double.NaN);
140     }
141 
142     /**
143      * Test the SpeedContourPlot.
144      * @throws NamingException
145      * @throws SimRuntimeException
146      * @throws NetworkException
147      * @throws RemoteException
148      * @throws GTUException
149      */
150     @SuppressWarnings("static-method")
151     @Test
152     public final void speedContourTest() throws RemoteException, NetworkException, SimRuntimeException,
153             NamingException, GTUException
154     {
155         List<Lane> path = dummyPath();
156         SpeedContourPlot scp = new SpeedContourPlot("Density", path);
157         assertTrue("newly created DensityContourPlot should not be null", null != scp);
158         assertEquals("SeriesKey should be \"speed\"", "speed", scp.getSeriesKey(0));
159         standardContourTests(scp, path.get(0), Double.NaN, 50);
160     }
161 
162     /**
163      * Test various properties of a ContourPlot that has no observed data added.
164      * @param cp ContourPlot; the ContourPlot to test
165      * @param lane TODO
166      * @param expectedZValue double; the value that getZ and getZValue should return for a valid item when no car has
167      *            passed
168      * @param expectedZValueWithTraffic double; the value that getZ and getZValue should return a valid item where a car
169      *            has travelled at constant speed of 50 km/h. Supply Double.NaN if the value varies but differs from the
170      *            value expected when no car has passed
171      * @throws NetworkException
172      * @throws RemoteException
173      * @throws NamingException
174      * @throws SimRuntimeException
175      * @throws GTUException
176      */
177     public static void standardContourTests(final ContourPlot cp, Lane lane, final double expectedZValue,
178             final double expectedZValueWithTraffic) throws NetworkException, RemoteException, SimRuntimeException,
179             NamingException, GTUException
180     {
181         assertEquals("seriesCount should be 1", 1, cp.getSeriesCount());
182         assertEquals("domainOrder should be ASCENDING", DomainOrder.ASCENDING, cp.getDomainOrder());
183         assertEquals("indexOf always returns 0", 0, cp.indexOf(0));
184         assertEquals("indexOf always returns 0", 0, cp.indexOf("abc"));
185         assertEquals("getGroup always returns null", null, cp.getGroup());
186         int xBins = cp.xAxisBins();
187         int yBins = cp.yAxisBins();
188         int expectedXBins =
189                 (int) Math.ceil((DoubleScalar.minus(ContourPlot.INITIALUPPERTIMEBOUND,
190                         ContourPlot.INITIALLOWERTIMEBOUND).getSI())
191                         / ContourPlot.STANDARDTIMEGRANULARITIES[ContourPlot.STANDARDINITIALTIMEGRANULARITYINDEX]);
192         assertEquals("Initial xBins should be " + expectedXBins, expectedXBins, xBins);
193         int expectedYBins =
194                 (int) Math
195                         .ceil(lane.getLength().getSI()
196                                 / ContourPlot.STANDARDDISTANCEGRANULARITIES[ContourPlot.STANDARDINITIALDISTANCEGRANULARITYINDEX]);
197         assertEquals("yBins should be " + expectedYBins, expectedYBins, yBins);
198         int bins = cp.getItemCount(0);
199         assertEquals("Total bin count is product of xBins * yBins", xBins * yBins, bins);
200         // Cache the String equivalents of minimumDistance and maximumDistance, INITIALLOWERTIMEBOUND and
201         // INITUALUPPERTIMEBOUND
202         String initialLowerTimeBoundString = ContourPlot.INITIALLOWERTIMEBOUND.toString();
203         String initialUpperTimeBoundString = ContourPlot.INITIALUPPERTIMEBOUND.toString();
204         // Vary the x granularity
205         for (double timeGranularity : ContourPlot.STANDARDTIMEGRANULARITIES)
206         {
207             cp.actionPerformed(new ActionEvent(cp, 0, "setTimeGranularity " + timeGranularity));
208             for (double distanceGranularity : ContourPlot.STANDARDDISTANCEGRANULARITIES)
209             {
210                 cp.actionPerformed(new ActionEvent(cp, 0, "setDistanceGranularity " + distanceGranularity));
211                 cp.reGraph();
212                 expectedXBins =
213                         (int) Math.ceil((DoubleScalar.minus(ContourPlot.INITIALUPPERTIMEBOUND,
214                                 ContourPlot.INITIALLOWERTIMEBOUND).getSI()) / timeGranularity);
215                 xBins = cp.xAxisBins();
216                 assertEquals("Modified xBins should be " + expectedXBins, expectedXBins, xBins);
217                 expectedYBins = (int) Math.ceil(lane.getLength().getSI() / distanceGranularity);
218                 yBins = cp.yAxisBins();
219                 assertEquals("Modified yBins should be " + expectedYBins, expectedYBins, yBins);
220                 bins = cp.getItemCount(0);
221                 assertEquals("Total bin count is product of xBins * yBins", xBins * yBins, bins);
222                 for (int item = 0; item < bins; item++)
223                 {
224                     double x = cp.getXValue(0, item);
225                     assertTrue("X should be >= " + initialLowerTimeBoundString,
226                             x >= ContourPlot.INITIALLOWERTIMEBOUND.getSI());
227                     assertTrue("X should be <= " + initialUpperTimeBoundString,
228                             x <= ContourPlot.INITIALUPPERTIMEBOUND.getSI());
229                     Number alternateX = cp.getX(0, item);
230                     assertEquals("getXValue and getX should return things that have the same value", x,
231                             alternateX.doubleValue(), 0.000001);
232                     double y = cp.getYValue(0, item);
233                     Number alternateY = cp.getY(0, item);
234                     assertEquals("getYValue and getY should return things that have the same value", y,
235                             alternateY.doubleValue(), 0.000001);
236                     double z = cp.getZValue(0, item);
237                     if (Double.isNaN(expectedZValue))
238                     {
239                         assertTrue("Z value should be NaN", Double.isNaN(z));
240                     }
241                     else
242                     {
243                         assertEquals("Z value should be " + expectedZValue, expectedZValue, z, 0.0001);
244                     }
245                     Number alternateZ = cp.getZ(0, item);
246                     if (Double.isNaN(expectedZValue))
247                     {
248                         assertTrue("Alternate Z value should be NaN", Double.isNaN(alternateZ.doubleValue()));
249                     }
250                     else
251                     {
252                         assertEquals("Alternate Z value should be " + expectedZValue, expectedZValue,
253                                 alternateZ.doubleValue(), 0.0000);
254                     }
255                 }
256                 try
257                 {
258                     cp.getXValue(0, -1);
259                     fail("Should have thrown an Error");
260                 }
261                 catch (Error e)
262                 {
263                     // Ignore
264                 }
265                 try
266                 {
267                     cp.getXValue(0, bins);
268                     fail("Should have thrown an Error");
269                 }
270                 catch (Error e)
271                 {
272                     // Ignore
273                 }
274                 try
275                 {
276                     cp.yAxisBin(-1);
277                     fail("Should have thrown an Error");
278                 }
279                 catch (Error e)
280                 {
281                     // Ignore
282                 }
283                 try
284                 {
285                     cp.yAxisBin(bins);
286                     fail("Should have thrown an Error");
287                 }
288                 catch (Error e)
289                 {
290                     // Ignore
291                 }
292             }
293         }
294         // Test some ActionEvents that ContourPlot can not handle
295         try
296         {
297             cp.actionPerformed(new ActionEvent(cp, 0, "blabla"));
298             fail("Should have thrown an Error");
299         }
300         catch (Error e)
301         {
302             // Ignore
303         }
304         try
305         {
306             cp.actionPerformed(new ActionEvent(cp, 0, "setDistanceGranularity -1"));
307             fail("Should have thrown an Error");
308         }
309         catch (Error e)
310         {
311             // ignore
312         }
313         try
314         {
315             cp.actionPerformed(new ActionEvent(cp, 0, "setDistanceGranularity abc"));
316             fail("Should have thrown an Error");
317         }
318         catch (Error e)
319         {
320             // ignore
321         }
322         try
323         {
324             cp.actionPerformed(new ActionEvent(cp, 0, "setDistanceGranularitIE 10")); // typo in the event name
325             fail("Should have thrown an Error");
326         }
327         catch (Error e)
328         {
329             // ignore
330         }
331         // Make the time granularity a bit more reasonable
332         final double useTimeGranularity = 30; // [s]
333         cp.actionPerformed(new ActionEvent(cp, 0, "setTimeGranularity " + useTimeGranularity));
334         final double useDistanceGranularity =
335                 ContourPlot.STANDARDDISTANCEGRANULARITIES[ContourPlot.STANDARDDISTANCEGRANULARITIES.length - 1];
336         cp.actionPerformed(new ActionEvent(cp, 0, "setDistanceGranularity " + useDistanceGranularity));
337         cp.reGraph();
338         bins = cp.getItemCount(0);
339         DoubleScalar.Abs<TimeUnit> initialTime = new DoubleScalar.Abs<TimeUnit>(0, TimeUnit.SECOND);
340         DoubleScalar.Rel<LengthUnit> initialPosition = new DoubleScalar.Rel<LengthUnit>(100, LengthUnit.METER);
341         DoubleScalar.Abs<SpeedUnit> initialSpeed = new DoubleScalar.Abs<SpeedUnit>(50, SpeedUnit.KM_PER_HOUR);
342         ContourPlotModel model = new ContourPlotModel();
343         SimpleSimulator simulator =
344                 new SimpleSimulator(initialTime, new DoubleScalar.Rel<TimeUnit>(0, TimeUnit.SECOND),
345                         new DoubleScalar.Rel<TimeUnit>(1800, TimeUnit.SECOND), model);
346         // Create a car running 50 km.h
347         SequentialFixedAccelerationModel gtuFollowingModel =
348                 new SequentialFixedAccelerationModel(simulator.getSimulator());
349         // Make the car run at constant speed for one minute
350         gtuFollowingModel.addStep(new FixedAccelerationModel(new DoubleScalar.Abs<AccelerationUnit>(0,
351                 AccelerationUnit.METER_PER_SECOND_2), new DoubleScalar.Rel<TimeUnit>(60, TimeUnit.SECOND)));
352         // Make the car run at constant speed for another minute
353         gtuFollowingModel.addStep(new FixedAccelerationModel(new DoubleScalar.Abs<AccelerationUnit>(0,
354                 AccelerationUnit.METER_PER_SECOND_2), new DoubleScalar.Rel<TimeUnit>(600, TimeUnit.SECOND)));
355         // Make the car run at constant speed for five more minutes
356         gtuFollowingModel.addStep(new FixedAccelerationModel(new DoubleScalar.Abs<AccelerationUnit>(0,
357                 AccelerationUnit.METER_PER_SECOND_2), new DoubleScalar.Rel<TimeUnit>(300, TimeUnit.SECOND)));
358         LaneChangeModel laneChangeModel = new Egoistic();
359         LaneBasedIndividualCar<Integer> car =
360                 CarTest.makeReferenceCar(0, lane, initialPosition, initialSpeed,
361                         (OTSDEVSSimulator) simulator.getSimulator(), gtuFollowingModel, laneChangeModel);
362         // Check that the initial data in the graph contains no trace of any car.
363         for (int item = 0; item < bins; item++)
364         {
365             double x = cp.getXValue(0, item);
366             assertTrue("X should be >= " + ContourPlot.INITIALLOWERTIMEBOUND,
367                     x >= ContourPlot.INITIALLOWERTIMEBOUND.getSI());
368             assertTrue("X should be <= " + ContourPlot.INITIALUPPERTIMEBOUND,
369                     x <= ContourPlot.INITIALUPPERTIMEBOUND.getSI());
370             Number alternateX = cp.getX(0, item);
371             assertEquals("getXValue and getX should return things that have the same value", x,
372                     alternateX.doubleValue(), 0.000001);
373             double y = cp.getYValue(0, item);
374             Number alternateY = cp.getY(0, item);
375             assertEquals("getYValue and getY should return things that have the same value", y,
376                     alternateY.doubleValue(), 0.000001);
377             double z = cp.getZValue(0, item);
378             if (Double.isNaN(expectedZValue))
379             {
380                 assertTrue("Z value should be NaN (got " + z + ")", Double.isNaN(z));
381             }
382             else
383             {
384                 assertEquals("Z value should be " + expectedZValue, expectedZValue, z, 0.0001);
385             }
386             Number alternateZ = cp.getZ(0, item);
387             if (Double.isNaN(expectedZValue))
388             {
389                 assertTrue("Alternate Z value should be NaN", Double.isNaN(alternateZ.doubleValue()));
390             }
391             else
392             {
393                 assertEquals("Alternate Z value should be " + expectedZValue, expectedZValue, alternateZ.doubleValue(),
394                         0.0000);
395             }
396         }
397         simulator.runUpTo(gtuFollowingModel.timeAfterCompletionOfStep(0));
398         // System.out.println("Car at start time " + car.getLastEvaluationTime() + " is at "
399         // + car.getPosition(car.getLastEvaluationTime()));
400         // System.out.println("At time " + simulator.getSimulator().getSimulatorTime().get() + " car is at " + car);
401         for (int item = 0; item < bins; item++)
402         {
403             double x = cp.getXValue(0, item);
404             assertTrue("X should be >= " + ContourPlot.INITIALLOWERTIMEBOUND,
405                     x >= ContourPlot.INITIALLOWERTIMEBOUND.getSI());
406             assertTrue("X should be <= " + ContourPlot.INITIALUPPERTIMEBOUND,
407                     x <= ContourPlot.INITIALUPPERTIMEBOUND.getSI());
408             Number alternateX = cp.getX(0, item);
409             assertEquals("getXValue and getX should return things that have the same value", x,
410                     alternateX.doubleValue(), 0.000001);
411             double y = cp.getYValue(0, item);
412             Number alternateY = cp.getY(0, item);
413             assertEquals("getYValue and getY should return things that have the same value", y,
414                     alternateY.doubleValue(), 0.000001);
415             double z = cp.getZValue(0, item);
416             // figure out if the car has traveled through this cell
417             // if (x >= 180)
418             // System.out.println(String.format("t=%.3f, x=%.3f z=%f, exp=%.3f, carLast=%s, carNext=%s", x, y, z,
419             // expectedZValue, car.getLastEvaluationTime().getSI(), car.getNextEvaluationTime().getSI()));
420             boolean hit = false;
421             if (x + useTimeGranularity >= car.getLastEvaluationTime().getSI()
422                     && x <= car.getNextEvaluationTime().getSI())
423             {
424                 // the car MAY have contributed to this cell
425                 DoubleScalar.Abs<TimeUnit> cellStartTime =
426                         new DoubleScalar.Abs<TimeUnit>(Math.max(car.getLastEvaluationTime().getSI(), x),
427                                 TimeUnit.SECOND);
428                 DoubleScalar.Abs<TimeUnit> cellEndTime =
429                         new DoubleScalar.Abs<TimeUnit>(Math.min(car.getNextEvaluationTime().getSI(), x
430                                 + useTimeGranularity), TimeUnit.SECOND);
431                 if (cellStartTime.getSI() < cellEndTime.getSI()
432                         && car.position(lane, car.getReference(), cellStartTime).getSI() <= y + useDistanceGranularity
433                         && car.position(lane, car.getReference(), cellEndTime).getSI() >= y)
434                 {
435                     hit = true;
436                 }
437             }
438             // System.out.println(String.format("hit=%s, t=%.3f, x=%.3f z=%f, exp=%.3f", hit, x, y, z, expectedZValue));
439             Number alternateZ = cp.getZ(0, item);
440             if (hit)
441             {
442                 if (!Double.isNaN(expectedZValueWithTraffic))
443                 {
444                     assertEquals("Z value should be " + expectedZValueWithTraffic, expectedZValueWithTraffic, z, 0.0001);
445                     assertEquals("Z value should be " + expectedZValueWithTraffic, expectedZValueWithTraffic,
446                             alternateZ.doubleValue(), 0.0001);
447                 }
448                 else
449                 {
450                     if (Double.isNaN(expectedZValue))
451                     {// FIXME looks wrong / PK
452                         assertFalse("Z value should not be NaN", Double.isNaN(z));
453                     }
454                 }
455             }
456             else
457             {
458                 if (Double.isNaN(expectedZValue))
459                 {
460                     assertTrue("Z value should be NaN", Double.isNaN(z));
461                 }
462                 else
463                 {
464                     assertEquals("Z value should be " + expectedZValue, expectedZValue, z, 0.0001);
465                 }
466                 if (Double.isNaN(expectedZValue))
467                 {
468                     assertTrue("Alternate Z value should be NaN", Double.isNaN(alternateZ.doubleValue()));
469                 }
470                 else
471                 {
472                     assertEquals("Alternate Z value should be " + expectedZValue, expectedZValue,
473                             alternateZ.doubleValue(), 0.0000);
474                 }
475             }
476         }
477         simulator.runUpTo(gtuFollowingModel.timeAfterCompletionOfStep(1));
478         // Check that the time range has expanded
479         xBins = cp.xAxisBins();
480         bins = cp.getItemCount(0);
481         double observedHighestTime = Double.MIN_VALUE;
482         for (int bin = 0; bin < bins; bin++)
483         {
484             double xValue = cp.getXValue(0, bin);
485             if (xValue > observedHighestTime)
486             {
487                 observedHighestTime = xValue;
488             }
489         }
490         DoubleScalar.Abs<TimeUnit> carEndTime = car.getNextEvaluationTime();
491         double expectedHighestTime = Math.floor((carEndTime.getSI() - 0.001) / useTimeGranularity) * useTimeGranularity;
492         assertEquals("Time range should run up to " + expectedHighestTime, expectedHighestTime, observedHighestTime,
493                 0.0001);
494         // Check the updateHint method in the PointerHandler
495         // First get the panel that stores the result of updateHint (this is ugly)
496         JLabel hintPanel = null;
497         ChartPanel chartPanel = null;
498         for (Component c0 : cp.getComponents())
499         {
500             for (Component c1 : ((Container) c0).getComponents())
501             {
502                 if (c1 instanceof Container)
503                 {
504                     for (Component c2 : ((Container) c1).getComponents())
505                     {
506                         // System.out.println("c2 is " + c2);
507                         if (c2 instanceof Container)
508                         {
509                             for (Component c3 : ((Container) c2).getComponents())
510                             {
511                                 // System.out.println("c3 is " + c3);
512                                 if (c3 instanceof JLabel)
513                                 {
514                                     if (null == hintPanel)
515                                     {
516                                         hintPanel = (JLabel) c3;
517                                     }
518                                     else
519                                     {
520                                         fail("There should be only one JPanel in a ContourPlot");
521                                     }
522                                 }
523                                 if (c3 instanceof ChartPanel)
524                                 {
525                                     if (null == chartPanel)
526                                     {
527                                         chartPanel = (ChartPanel) c3;
528                                     }
529                                     else
530                                     {
531                                         fail("There should be only one ChartPanel in a ContourPlot");
532                                     }
533                                 }
534                             }
535                         }
536                     }
537                 }
538             }
539         }
540         if (null == hintPanel)
541         {
542             fail("Could not find a JLabel in ContourPlot");
543         }
544         if (null == chartPanel)
545         {
546             fail("Could not find a ChartPanel in ContourPlot");
547         }
548         assertEquals("Initially the text should be a single space", " ", hintPanel.getText());
549         PointerHandler ph = null;
550         for (MouseListener ml : chartPanel.getMouseListeners())
551         {
552             if (ml instanceof PointerHandler)
553             {
554                 if (null == ph)
555                 {
556                     ph = (PointerHandler) ml;
557                 }
558                 else
559                 {
560                     fail("There should be only one PointerHandler on the chartPanel");
561                 }
562             }
563         }
564         if (null == ph)
565         {
566             fail("Could not find the PointerHandler for the chartPanel");
567         }
568         ph.updateHint(1, 2);
569         // System.out.println("Hint text is now " + hintPanel.getText());
570         assertFalse("Hint should not be a single space", " ".equals(hintPanel.getText()));
571         ph.updateHint(Double.NaN, Double.NaN);
572         assertEquals("The text should again be a single space", " ", hintPanel.getText());
573     }
574 
575     /**
576      * Run the DensityContourPlot stand-alone for profiling
577      * @param args
578      * @throws RemoteException
579      * @throws NetworkException
580      * @throws SimRuntimeException
581      * @throws NamingException
582      * @throws GTUException
583      */
584     public static void main(final String[] args) throws RemoteException, NetworkException, SimRuntimeException,
585             NamingException, GTUException
586     {
587         ContourPlotTest cpt = new ContourPlotTest();
588         System.out.println("Click the OK button");
589         JOptionPane.showMessageDialog(null, "ContourPlot", "Start experiment", JOptionPane.INFORMATION_MESSAGE);
590         System.out.println("Running ...");
591         cpt.densityContourTest();
592         System.out.println("Finished");
593     }
594 
595 }
596 
597 /**
598  * <p>
599  * Copyright (c) 2013-2014 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights
600  * reserved. <br>
601  * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
602  * <p>
603  * @version 9 feb. 2015 <br>
604  * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
605  */
606 class ContourPlotModel implements OTSModelInterface
607 {
608 
609     /** */
610     private static final long serialVersionUID = 20150209L;
611 
612     /** {@inheritDoc} */
613     @Override
614     public void constructModel(SimulatorInterface<Abs<TimeUnit>, Rel<TimeUnit>, OTSSimTimeDouble> simulator)
615             throws SimRuntimeException, RemoteException
616     {
617     }
618 
619     /** {@inheritDoc} */
620     @Override
621     public SimulatorInterface<Abs<TimeUnit>, Rel<TimeUnit>, OTSSimTimeDouble> getSimulator() throws RemoteException
622     {
623         return null;
624     }
625 
626 }