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