View Javadoc
1   package org.opentrafficsim.draw.graphs.road;
2   
3   import java.awt.Color;
4   import java.util.List;
5   
6   import org.djunits.unit.DurationUnit;
7   import org.djunits.unit.LinearDensityUnit;
8   import org.djunits.value.StorageType;
9   import org.djunits.value.ValueException;
10  import org.djunits.value.vdouble.matrix.DurationMatrix;
11  import org.djunits.value.vdouble.scalar.Duration;
12  import org.djunits.value.vdouble.scalar.Length;
13  import org.djunits.value.vdouble.scalar.Time;
14  import org.djunits.value.vfloat.vector.FloatSpeedVector;
15  import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
16  import org.opentrafficsim.core.egtf.Converter;
17  import org.opentrafficsim.core.egtf.Quantity;
18  import org.opentrafficsim.draw.core.BoundsPaintScale;
19  import org.opentrafficsim.draw.graphs.AbstractContourPlot;
20  import org.opentrafficsim.draw.graphs.ContourDataSource;
21  import org.opentrafficsim.draw.graphs.ContourDataSource.ContourDataType;
22  import org.opentrafficsim.draw.graphs.GraphType;
23  import org.opentrafficsim.draw.graphs.GraphUtil;
24  import org.opentrafficsim.kpi.sampling.SamplingException;
25  import org.opentrafficsim.kpi.sampling.Trajectory;
26  import org.opentrafficsim.kpi.sampling.TrajectoryGroup;
27  import org.opentrafficsim.road.network.sampling.GtuData;
28  import org.opentrafficsim.road.network.sampling.data.ReferenceSpeed;
29  
30  /**
31   * Contour plot for delay.
32   * <p>
33   * <i>A note on the unit "/km"</i>. This unit is derived by measuring the total delay over a cell in space-time, which gives an
34   * SI value in [s]. With varying granularity, the value needs to be normalized to space-time. Hence, the value is divided by the
35   * length of the cell [m], and divided by the duration of the cell [s]. This gives a unit of [s/s/m] = [1/m]. This means that a
36   * traffic state represented by a value of D/km, gives a total amount of delay equal to D * x * t, where x * t is the size of
37   * the cell, and the resulting value is in the same unit as t. So if D = 50/km, then measuring this state over 2km and during 3
38   * hours gives 50 * 2 * 3 = 300h of delay.
39   * <p>
40   * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
41   * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
42   * <p>
43   * @version $Revision$, $LastChangedDate$, by $Author$, initial version 10 okt. 2018 <br>
44   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
45   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
46   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
47   */
48  public class ContourPlotDelay extends AbstractContourPlot<Duration>
49  {
50  
51      /** */
52      private static final long serialVersionUID = 20181010L;
53  
54      /** Quantity for the EGTF. */
55      private static final Quantity<Duration, DurationMatrix> QUANTITY = new Quantity<>("delay", new Converter<DurationMatrix>()
56      {
57          /** {@inheritDoc} */
58          @Override
59          public DurationMatrix convert(final double[][] filteredData)
60          {
61              try
62              {
63                  return new DurationMatrix(filteredData, DurationUnit.SI, StorageType.DENSE);
64              }
65              catch (ValueException exception)
66              {
67                  // should not happen as filtered data comes from the EGTF
68                  throw new RuntimeException("Unexpected exception while converting duration to output format.", exception);
69              }
70          }
71      });
72  
73      /** Contour data type. */
74      private static final ContourDataType<Duration, Duration> CONTOUR_DATA_TYPE = new ContourDataType<Duration, Duration>()
75      {
76          /** {@inheritDoc} */
77          @Override
78          public Duration identity()
79          {
80              return Duration.ZERO;
81          }
82  
83          /** {@inheritDoc} */
84          @Override
85          public Duration processSeries(final Duration intermediate, final List<TrajectoryGroup<?>> trajectories,
86                  final List<Length> xFrom, final List<Length> xTo, final Time tFrom, final Time tTo)
87          {
88              double sumActualTime = 0.0;
89              double sumRefTime = 0.0;
90              for (int i = 0; i < trajectories.size(); i++)
91              {
92                  TrajectoryGroup<?> trajectoryGroup = trajectories.get(i);
93                  for (Trajectory<?> trajectory : trajectoryGroup.getTrajectories())
94                  {
95                      if (GraphUtil.considerTrajectory(trajectory, tFrom, tTo))
96                      {
97                          trajectory = trajectory.subSet(xFrom.get(i), xTo.get(i), tFrom, tTo);
98                          try
99                          {
100                             FloatSpeedVector ref = trajectory.getExtendedData(ReferenceSpeed.INSTANCE);
101                             float[] v = trajectory.getV();
102                             float[] x = trajectory.getX();
103                             for (int j = 0; j < v.length - 1; j++)
104                             {
105                                 sumRefTime += (x[j + 1] - x[j]) / ref.get(j).si;
106                             }
107                         }
108                         catch (SamplingException | ValueException exception)
109                         {
110                             throw new RuntimeException("Unexpected exception while calculating delay.", exception);
111                         }
112                         sumActualTime += trajectory.getTotalDuration().si;
113                     }
114                 }
115             }
116             return Duration.createSI(intermediate.si + sumActualTime - sumRefTime);
117         }
118 
119         /** {@inheritDoc} */
120         @Override
121         public Duration finalize(final Duration intermediate)
122         {
123             return intermediate;
124         }
125 
126         /** {@inheritDoc} */
127         @SuppressWarnings("synthetic-access")
128         @Override
129         public Quantity<Duration, ?> getQuantity()
130         {
131             return QUANTITY;
132         }
133 
134     };
135 
136     /**
137      * Constructor.
138      * @param caption String; caption
139      * @param simulator OTSSimulatorInterface; simulator
140      * @param dataPool ContourDataSource&lt;GtuData&gt;; data pool
141      */
142     public ContourPlotDelay(final String caption, final OTSSimulatorInterface simulator,
143             final ContourDataSource<GtuData> dataPool)
144     {
145         super(caption, simulator, dataPool, createPaintScale(), new Duration(0.05, DurationUnit.SI), "%.1f/km",
146                 "delay %.1f /km");
147         dataPool.getSampler().registerExtendedDataType(ReferenceSpeed.INSTANCE);
148     }
149 
150     /**
151      * Creates a paint scale from red, via yellow to green.
152      * @return ContinuousColorPaintScale; paint scale
153      */
154     private static BoundsPaintScale createPaintScale()
155     {
156         double[] boundaries = { 0.0, 0.05, 0.2 };
157         Color[] colorValues = BoundsPaintScale.GREEN_RED;
158         return new BoundsPaintScale(boundaries, colorValues);
159     }
160 
161     /** {@inheritDoc} */
162     @Override
163     public GraphType getGraphType()
164     {
165         return GraphType.DELAY_CONTOUR;
166     }
167 
168     /** {@inheritDoc} */
169     @Override
170     protected double scale(final double si)
171     {
172         return LinearDensityUnit.PER_KILOMETER.getScale().fromStandardUnit(si);
173     }
174 
175     /** {@inheritDoc} */
176     @Override
177     protected double getValue(final int item, final double cellLength, final double cellSpan)
178     {
179         return getDataPool().get(item, CONTOUR_DATA_TYPE) / (cellLength * cellSpan);
180     }
181 
182     /** {@inheritDoc} */
183     @Override
184     protected ContourDataType<Duration, Duration> getContourDataType()
185     {
186         return CONTOUR_DATA_TYPE;
187     }
188 
189 }