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