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  
54  
55  
56  
57  
58  
59  
60  
61  
62  public class ContourPlotTest
63  {
64      
65  
66  
67  
68  
69  
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  
84  
85  
86  
87  
88  
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 
104 
105 
106 
107 
108 
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 
124 
125 
126 
127 
128 
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 
144 
145 
146 
147 
148 
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 
164 
165 
166 
167 
168 
169 
170 
171 
172 
173 
174 
175 
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         
201         
202         String initialLowerTimeBoundString = ContourPlot.INITIALLOWERTIMEBOUND.toString();
203         String initialUpperTimeBoundString = ContourPlot.INITIALUPPERTIMEBOUND.toString();
204         
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                     
264                 }
265                 try
266                 {
267                     cp.getXValue(0, bins);
268                     fail("Should have thrown an Error");
269                 }
270                 catch (Error e)
271                 {
272                     
273                 }
274                 try
275                 {
276                     cp.yAxisBin(-1);
277                     fail("Should have thrown an Error");
278                 }
279                 catch (Error e)
280                 {
281                     
282                 }
283                 try
284                 {
285                     cp.yAxisBin(bins);
286                     fail("Should have thrown an Error");
287                 }
288                 catch (Error e)
289                 {
290                     
291                 }
292             }
293         }
294         
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             
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             
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             
321         }
322         try
323         {
324             cp.actionPerformed(new ActionEvent(cp, 0, "setDistanceGranularitIE 10")); 
325             fail("Should have thrown an Error");
326         }
327         catch (Error e)
328         {
329             
330         }
331         
332         final double useTimeGranularity = 30; 
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         
347         SequentialFixedAccelerationModel gtuFollowingModel =
348                 new SequentialFixedAccelerationModel(simulator.getSimulator());
349         
350         gtuFollowingModel.addStep(new FixedAccelerationModel(new DoubleScalar.Abs<AccelerationUnit>(0,
351                 AccelerationUnit.METER_PER_SECOND_2), new DoubleScalar.Rel<TimeUnit>(60, TimeUnit.SECOND)));
352         
353         gtuFollowingModel.addStep(new FixedAccelerationModel(new DoubleScalar.Abs<AccelerationUnit>(0,
354                 AccelerationUnit.METER_PER_SECOND_2), new DoubleScalar.Rel<TimeUnit>(600, TimeUnit.SECOND)));
355         
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         
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         
399         
400         
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             
417             
418             
419             
420             boolean hit = false;
421             if (x + useTimeGranularity >= car.getLastEvaluationTime().getSI()
422                     && x <= car.getNextEvaluationTime().getSI())
423             {
424                 
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             
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                     {
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         
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         
495         
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                         
507                         if (c2 instanceof Container)
508                         {
509                             for (Component c3 : ((Container) c2).getComponents())
510                             {
511                                 
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         
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 
577 
578 
579 
580 
581 
582 
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 
599 
600 
601 
602 
603 
604 
605 
606 class ContourPlotModel implements OTSModelInterface
607 {
608 
609     
610     private static final long serialVersionUID = 20150209L;
611 
612     
613     @Override
614     public void constructModel(SimulatorInterface<Abs<TimeUnit>, Rel<TimeUnit>, OTSSimTimeDouble> simulator)
615             throws SimRuntimeException, RemoteException
616     {
617     }
618 
619     
620     @Override
621     public SimulatorInterface<Abs<TimeUnit>, Rel<TimeUnit>, OTSSimTimeDouble> getSimulator() throws RemoteException
622     {
623         return null;
624     }
625 
626 }