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   import static org.opentrafficsim.core.gtu.GTUType.CAR;
8   
9   import java.awt.Component;
10  import java.awt.Container;
11  import java.awt.event.ActionEvent;
12  import java.awt.event.MouseListener;
13  import java.util.LinkedHashSet;
14  import java.util.Set;
15  
16  import javax.swing.JLabel;
17  
18  import org.djunits.unit.TimeUnit;
19  import org.djunits.unit.UNITS;
20  import org.djunits.value.vdouble.scalar.Acceleration;
21  import org.djunits.value.vdouble.scalar.Duration;
22  import org.djunits.value.vdouble.scalar.Length;
23  import org.djunits.value.vdouble.scalar.Speed;
24  import org.djunits.value.vdouble.scalar.Time;
25  import org.jfree.chart.ChartPanel;
26  import org.junit.Test;
27  import org.opentrafficsim.base.parameters.Parameters;
28  import org.opentrafficsim.core.compatibility.Compatible;
29  import org.opentrafficsim.core.dsol.OTSModelInterface;
30  import org.opentrafficsim.core.gtu.GTUDirectionality;
31  import org.opentrafficsim.core.gtu.GTUType;
32  import org.opentrafficsim.core.network.OTSNetwork;
33  import org.opentrafficsim.road.DefaultTestParameters;
34  import org.opentrafficsim.road.car.CarTest;
35  import org.opentrafficsim.road.gtu.lane.LaneBasedIndividualGTU;
36  import org.opentrafficsim.road.gtu.lane.tactical.LaneBasedCFLCTacticalPlanner;
37  import org.opentrafficsim.road.gtu.lane.tactical.following.FixedAccelerationModel;
38  import org.opentrafficsim.road.gtu.lane.tactical.following.GTUFollowingModelOld;
39  import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.Egoistic;
40  import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.LaneChangeModel;
41  import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalPlanner;
42  import org.opentrafficsim.road.gtu.strategical.route.LaneBasedStrategicalRoutePlanner;
43  import org.opentrafficsim.road.network.lane.DirectedLanePosition;
44  import org.opentrafficsim.road.network.lane.Lane;
45  import org.opentrafficsim.road.network.lane.LaneType;
46  import org.opentrafficsim.road.network.lane.object.sensor.SinkSensor;
47  import org.opentrafficsim.simulationengine.SimpleSimulator;
48  
49  import nl.tudelft.simulation.dsol.SimRuntimeException;
50  import nl.tudelft.simulation.dsol.simtime.SimTimeDoubleUnit;
51  import nl.tudelft.simulation.dsol.simulators.DEVSSimulatorInterface;
52  import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
53  
54  /**
55   * <p>
56   * Copyright (c) 2013-2018 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
57   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
58   * <p>
59   * $LastChangedDate: 2015-09-14 01:33:02 +0200 (Mon, 14 Sep 2015) $, @version $Revision: 1401 $, by $Author: averbraeck $,
60   * initial version Aug 25, 2014 <br>
61   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
62   */
63  public class FundamentalDiagramPlotTest implements OTSModelInterface, UNITS
64  {
65      /** */
66      private static final long serialVersionUID = 20150226L;
67  
68      /** The network. */
69      private OTSNetwork network = new OTSNetwork("network");
70  
71      /** the simulator. */
72      private DEVSSimulatorInterface.TimeDoubleUnit simulator;
73  
74      /**
75       * Test the FundamentalDiagram.
76       * @throws Exception when something goes wrong (should not happen)
77       */
78      @SuppressWarnings("static-method")
79      // XXX @Test
80      public final void fundamentalDiagramTest() throws Exception
81      {
82          this.simulator = new SimpleSimulator(new Time(0, TimeUnit.BASE_SECOND), new Duration(0, SECOND),
83                  new Duration(1800, SECOND), this);
84  
85          Duration aggregationTime = new Duration(30, SECOND);
86          Length position = new Length(123, METER);
87          Length carPosition = new Length(122.5, METER);
88          GTUType gtuType = CAR;
89          LaneType laneType = LaneType.TWO_WAY_LANE;
90          Lane lane = CarTest.makeLane(this.network, laneType, this.simulator);
91          FundamentalDiagram fd = new FundamentalDiagram("Fundamental Diagram", aggregationTime, lane, position,
92                  Compatible.EVERYTHING, this.simulator);
93          assertEquals("SeriesCount should match numberOfLanes", 1, fd.getSeriesCount());
94          assertEquals("Position should match the supplied position", position.getSI(), fd.getPosition().getSI(), 0.0001);
95          try
96          {
97              fd.getXValue(-1, 0);
98              fail("Bad series should have thrown an Error");
99          }
100         catch (Error e)
101         {
102             // Ignore
103         }
104         try
105         {
106             fd.getXValue(1, 0);
107             fail("Bad series should have thrown an Error");
108         }
109         catch (Error e)
110         {
111             // Ignore
112         }
113         double value = fd.getXValue(0, 0);
114         assertTrue("No data should result in NaN", java.lang.Double.isNaN(value));
115         value = fd.getX(0, 0).doubleValue();
116         assertTrue("No data should result in NaN", java.lang.Double.isNaN(value));
117         value = fd.getYValue(0, 0);
118         assertTrue("No data should result in NaN", java.lang.Double.isNaN(value));
119         value = fd.getY(0, 0).doubleValue();
120         assertTrue("No data should result in NaN", java.lang.Double.isNaN(value));
121         ActionEvent setXToSpeed = new ActionEvent(fd, 0, "Speed/Speed");
122         ActionEvent resetAxis = new ActionEvent(fd, 0, "Flow/Density");
123         Speed speed = new Speed(100, KM_PER_HOUR);
124         Time time = new Time(123, TimeUnit.BASE_SECOND);
125         Length length = new Length(5.0, METER);
126         Length width = new Length(2.0, METER);
127         Speed maxSpeed = new Speed(120, KM_PER_HOUR);
128         Set<DirectedLanePosition> initialLongitudinalPositions = new LinkedHashSet<>(1);
129         initialLongitudinalPositions.add(new DirectedLanePosition(lane, carPosition, GTUDirectionality.DIR_PLUS));
130 
131         // add a sink 100 meter before the end of the lane.
132         new SinkSensor(lane, new Length(lane.getLength().getSI() - 100, METER), this.simulator);
133 
134         this.simulator.runUpTo(time);
135         while (this.simulator.isRunning())
136         {
137             try
138             {
139                 Thread.sleep(1);
140             }
141             catch (InterruptedException ie)
142             {
143                 ie = null; // ignore
144             }
145         }
146         int bucket = (int) Math.floor(time.getSI() / aggregationTime.getSI());
147         LaneChangeModel laneChangeModel = new Egoistic();
148         GTUFollowingModelOld gtuFollowingModel =
149                 new FixedAccelerationModel(new Acceleration(0, METER_PER_SECOND_2), new Duration(1000, SECOND));
150         // Construct a car
151         Parameters parameters = DefaultTestParameters.create();
152         LaneBasedIndividualGTU gtu = new LaneBasedIndividualGTU("1", gtuType, length, width, maxSpeed, length.multiplyBy(0.5),
153                 this.simulator, this.network);
154         LaneBasedStrategicalPlanner strategicalPlanner = new LaneBasedStrategicalRoutePlanner(
155                 new LaneBasedCFLCTacticalPlanner(gtuFollowingModel, laneChangeModel, gtu), gtu);
156         gtu.setParameters(parameters);
157         gtu.init(strategicalPlanner, initialLongitudinalPositions, speed);
158         this.simulator.runUpTo(new Time(124, TimeUnit.BASE_SECOND));
159         while (this.simulator.isRunning())
160         {
161             try
162             {
163                 Thread.sleep(1);
164             }
165             catch (InterruptedException ie)
166             {
167                 ie = null; // ignore
168             }
169         }
170         for (int sample = 0; sample < 10; sample++)
171         {
172             boolean shouldHaveData = sample == bucket;
173             value = fd.getXValue(0, sample);
174             // System.out.println("value of sample " + sample + " is " + value);
175             if (shouldHaveData)
176             {
177                 double expectedDensity = 3600 / aggregationTime.getSI() / speed.getSI();
178                 // TODO THIS TEST FAILS!!!
179                 // assertEquals("Density should be " + expectedDensity, expectedDensity, value, 0.00001);
180             }
181             else
182             {
183                 assertTrue("Data should be NaN", java.lang.Double.isNaN(value));
184             }
185             value = fd.getX(0, sample).doubleValue();
186             if (shouldHaveData)
187             {
188                 double expectedDensity = 3600 / aggregationTime.getSI() / speed.getSI();
189                 assertEquals("Density should be " + expectedDensity, expectedDensity, value, 0.00001);
190             }
191             else
192             {
193                 assertTrue("Data should be NaN", java.lang.Double.isNaN(value));
194             }
195             shouldHaveData = sample <= bucket;
196             value = fd.getYValue(0, sample);
197             if (shouldHaveData)
198             {
199                 double expectedFlow = sample == bucket ? 3600 / aggregationTime.getSI() : 0;
200                 assertEquals("Flow should be " + expectedFlow, expectedFlow, value, 0.00001);
201             }
202             else
203             {
204                 assertTrue("Data should be NaN", java.lang.Double.isNaN(value));
205             }
206             value = fd.getY(0, sample).doubleValue();
207             if (shouldHaveData)
208             {
209                 double expectedFlow = sample == bucket ? 3600 / aggregationTime.getSI() : 0;
210                 assertEquals("Flow should be " + expectedFlow, expectedFlow, value, 0.00001);
211             }
212             else
213             {
214                 assertTrue("Data should be NaN", java.lang.Double.isNaN(value));
215             }
216             fd.actionPerformed(setXToSpeed);
217             value = fd.getYValue(0, sample);
218             if (shouldHaveData)
219             {
220                 double expectedSpeed = sample == bucket ? speed.getInUnit() : 0;
221                 assertEquals("Speed should be " + expectedSpeed, expectedSpeed, value, 0.00001);
222             }
223             else
224             {
225                 assertTrue("Data should be NaN", java.lang.Double.isNaN(value));
226             }
227             value = fd.getY(0, sample).doubleValue();
228             if (shouldHaveData)
229             {
230                 double expectedSpeed = sample == bucket ? speed.getInUnit() : 0;
231                 assertEquals("Speed should be " + expectedSpeed, expectedSpeed, value, 0.00001);
232             }
233             else
234             {
235                 assertTrue("Data should be NaN", java.lang.Double.isNaN(value));
236             }
237             fd.actionPerformed(resetAxis);
238         }
239         // Check that harmonic mean speed is computed
240         speed = new Speed(10, KM_PER_HOUR);
241         parameters = DefaultTestParameters.create();
242         LaneBasedIndividualGTU gtuX = new LaneBasedIndividualGTU("1234", gtuType, length, width, maxSpeed,
243                 length.multiplyBy(0.5), this.simulator, this.network);
244         strategicalPlanner = new LaneBasedStrategicalRoutePlanner(
245                 new LaneBasedCFLCTacticalPlanner(gtuFollowingModel, laneChangeModel, gtuX), gtuX);
246         gtuX.init(strategicalPlanner, initialLongitudinalPositions, speed);
247         gtuX.setParameters(parameters);
248         this.simulator.runUpTo(new Time(125, TimeUnit.BASE_SECOND));
249         while (this.simulator.isRunning())
250         {
251             try
252             {
253                 Thread.sleep(1);
254             }
255             catch (InterruptedException ie)
256             {
257                 ie = null; // ignore
258             }
259         }
260         fd.actionPerformed(setXToSpeed);
261         value = fd.getYValue(0, bucket);
262         double expected = 2d / (1d / 100 + 1d / 10);
263         // System.out.println("harmonic speed is " + value + ", expected is " + expected);
264         assertEquals("Harmonic mean of 10 and 100 is " + expected, expected, value, 0.0001);
265         // Test the actionPerformed method with various malformed ActionEvents.
266         try
267         {
268             fd.actionPerformed(new ActionEvent(fd, 0, "bla"));
269             fail("Bad ActionEvent should have thrown an Error");
270         }
271         catch (Error e)
272         {
273             // Ignore
274         }
275         try
276         {
277             fd.actionPerformed(new ActionEvent(fd, 0, "Speed/bla"));
278             fail("Bad ActionEvent should have thrown an Error");
279         }
280         catch (Error e)
281         {
282             // Ignore
283         }
284         try
285         {
286             fd.actionPerformed(new ActionEvent(fd, 0, "Flow/bla"));
287             fail("Bad ActionEvent should have thrown an Error");
288         }
289         catch (Error e)
290         {
291             // Ignore
292         }
293         try
294         {
295             fd.actionPerformed(new ActionEvent(fd, 0, "Density/bla"));
296             fail("Bad ActionEvent should have thrown an Error");
297         }
298         catch (Error e)
299         {
300             // Ignore
301         }
302         try
303         {
304             fd.actionPerformed(new ActionEvent(fd, 0, "bla/Speed"));
305             fail("Bad ActionEvent should have thrown an Error");
306         }
307         catch (Error e)
308         {
309             // Ignore
310         }
311         try
312         {
313             fd.actionPerformed(new ActionEvent(fd, 0, "bla/Flow"));
314             fail("Bad ActionEvent should have thrown an Error");
315         }
316         catch (Error e)
317         {
318             // Ignore
319         }
320         try
321         {
322             fd.actionPerformed(new ActionEvent(fd, 0, "bla/Density"));
323             fail("Bad ActionEvent should have thrown an Error");
324         }
325         catch (Error e)
326         {
327             // Ignore
328         }
329     }
330 
331     /**
332      * Test the updateHint method in the PointerHandler.
333      * @throws Exception when something goes wrong (should not happen)
334      */
335     @SuppressWarnings("static-method")
336     @Test
337     public final void testHints() throws Exception
338     {
339         this.simulator = new SimpleSimulator(new Time(0, TimeUnit.BASE_SECOND), new Duration(0, SECOND),
340                 new Duration(1800, SECOND), this);
341 
342         Duration aggregationTime = new Duration(30, SECOND);
343         Length position = new Length(123, METER);
344         LaneType laneType = LaneType.TWO_WAY_LANE;
345         FundamentalDiagram fd = new FundamentalDiagram("Fundamental Diagram", aggregationTime,
346                 CarTest.makeLane(this.network, laneType, this.simulator), position, Compatible.EVERYTHING, this.simulator);
347         // First get the panel that stores the result of updateHint (this is ugly)
348         JLabel hintPanel = null;
349         ChartPanel chartPanel = null;
350         for (Component c0 : fd.getComponents())
351         {
352             for (Component c1 : ((Container) c0).getComponents())
353             {
354                 if (c1 instanceof Container)
355                 {
356                     for (Component c2 : ((Container) c1).getComponents())
357                     {
358                         // System.out.println("c2 is " + c2);
359                         if (c2 instanceof Container)
360                         {
361                             for (Component c3 : ((Container) c2).getComponents())
362                             {
363                                 // System.out.println("c3 is " + c3);
364                                 if (c3 instanceof JLabel)
365                                 {
366                                     if (null == hintPanel)
367                                     {
368                                         hintPanel = (JLabel) c3;
369                                     }
370                                     else
371                                     {
372                                         fail("There should be only one JPanel in a FundamentalDiagram");
373                                     }
374                                 }
375                                 if (c3 instanceof ChartPanel)
376                                 {
377                                     if (null == chartPanel)
378                                     {
379                                         chartPanel = (ChartPanel) c3;
380                                     }
381                                     else
382                                     {
383                                         fail("There should be only one ChartPanel in a FundamentalDiagram");
384                                     }
385                                 }
386                             }
387                         }
388                     }
389                 }
390             }
391         }
392         if (null == hintPanel)
393         {
394             fail("Could not find a JLabel in FundamentalDiagram");
395         }
396         if (null == chartPanel)
397         {
398             fail("Could not find a ChartPanel in FundamentalDiagram");
399         }
400         assertEquals("Initially the text should be a single space", " ", hintPanel.getText());
401         PointerHandler ph = null;
402         for (MouseListener ml : chartPanel.getMouseListeners())
403         {
404             if (ml instanceof PointerHandler)
405             {
406                 if (null == ph)
407                 {
408                     ph = (PointerHandler) ml;
409                 }
410                 else
411                 {
412                     fail("There should be only one PointerHandler on the chartPanel");
413                 }
414             }
415         }
416         if (null == ph)
417         {
418             fail("Could not find the PointerHandler for the chartPanel");
419         }
420         ph.updateHint(1, 2);
421         // System.out.println("Hint text is now " + hintPanel.getText());
422         assertFalse("Hint should not be a single space", " ".equals(hintPanel.getText()));
423         ph.updateHint(java.lang.Double.NaN, java.lang.Double.NaN);
424         assertEquals("The text should again be a single space", " ", hintPanel.getText());
425     }
426 
427     /** {@inheritDoc} */
428     @Override
429     public void constructModel(final SimulatorInterface<Time, Duration, SimTimeDoubleUnit> arg0) throws SimRuntimeException
430     {
431         // Do nothing
432     }
433 
434     /** {@inheritDoc} */
435     @Override
436     public final SimulatorInterface<Time, Duration, SimTimeDoubleUnit> getSimulator()
437 
438     {
439         return this.simulator;
440     }
441 
442     /** {@inheritDoc} */
443     @Override
444     public final OTSNetwork getNetwork()
445     {
446         return this.network;
447     }
448 
449 }