View Javadoc
1   package org.opentrafficsim.road.od;
2   
3   import org.djunits.unit.FrequencyUnit;
4   import org.djunits.unit.TimeUnit;
5   import org.djunits.value.ValueRuntimeException;
6   import org.djunits.value.vdouble.scalar.Frequency;
7   import org.djunits.value.vdouble.scalar.Time;
8   import org.djunits.value.vdouble.vector.FrequencyVector;
9   import org.djunits.value.vdouble.vector.TimeVector;
10  
11  /**
12   * Interpolation of demand.
13   * <p>
14   * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
15   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
16   * </p>
17   * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
18   * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
19   * @author <a href="https://dittlab.tudelft.nl">Wouter Schakel</a>
20   */
21  public enum Interpolation
22  {
23  
24      /** Stepwise interpolation of demand. */
25      STEPWISE
26      {
27          /** {@inheritDoc} */
28          @Override
29          Frequency interpolate(final Frequency frequency0, final Time time0, final Frequency frequency1, final Time time1,
30                  final Time time)
31          {
32              return frequency0;
33          }
34  
35          /** {@inheritDoc} */
36          @Override
37          int integrate(final Frequency frequency0, final Time time0, final Frequency frequency1, final Time time1)
38          {
39              return (int) (frequency0.getInUnit(FrequencyUnit.PER_HOUR)
40                      * (time1.getInUnit(TimeUnit.BASE_HOUR) - time0.getInUnit(TimeUnit.BASE_HOUR)));
41          }
42      },
43  
44      /** Linear interpolation of demand. */
45      LINEAR
46      {
47          /** {@inheritDoc} */
48          @Override
49          Frequency interpolate(final Frequency frequency0, final Time time0, final Frequency frequency1, final Time time1,
50                  final Time time)
51          {
52              return Frequency.interpolate(frequency0, frequency1, (time.si - time0.si) / (time1.si - time0.si));
53          }
54  
55          /** {@inheritDoc} */
56          @Override
57          int integrate(final Frequency frequency0, final Time time0, final Frequency frequency1, final Time time1)
58          {
59              return (int) (0.5 * (frequency0.getInUnit(FrequencyUnit.PER_HOUR) + frequency1.getInUnit(FrequencyUnit.PER_HOUR))
60                      * (time1.getInUnit(TimeUnit.BASE_HOUR) - time0.getInUnit(TimeUnit.BASE_HOUR)));
61          }
62      };
63  
64      /**
65       * Interpolate between given frequencies.
66       * @param frequency0 Frequency; frequency at {@code time0}
67       * @param time0 Time; time of {@code frequency0} (&le; {@code time})
68       * @param frequency1 Frequency; frequency at {@code time1}
69       * @param time1 Time; time of {@code frequency1} (&gt; {@code time})
70       * @param time Time; {@code time0} &le; {@code time} &lt; {@code time1}
71       * @return interpolated frequency
72       */
73      abstract Frequency interpolate(Frequency frequency0, Time time0, Frequency frequency1, Time time1, Time time);
74  
75      /**
76       * Integrates to the number of trips in given period.
77       * @param frequency0 Frequency; frequency at {@code time0}
78       * @param time0 Time; time of {@code frequency0} (&le; {@code time})
79       * @param frequency1 Frequency; frequency at {@code time1}
80       * @param time1 Time; time of {@code frequency1} (&gt; {@code time})
81       * @return number of trips in given period
82       */
83      abstract int integrate(Frequency frequency0, Time time0, Frequency frequency1, Time time1);
84  
85      /**
86       * @return whether this is step-wise interpolation
87       */
88      public boolean isStepWise()
89      {
90          return this.equals(STEPWISE);
91      }
92  
93      /**
94       * @return whether this is linear interpolation
95       */
96      public boolean isLinear()
97      {
98          return this.equals(LINEAR);
99      }
100 
101     /**
102      * Returns interpolated value from array at given time. If time is outside of the vector range, 0 is returned.
103      * @param time Time; time to determine the frequency at
104      * @param demandVector FrequencyVector; demand vector
105      * @param timeVector TimeVector; time vector
106      * @param sliceStart boolean; whether the time is at the start of an arbitrary time slice
107      * @return interpolated value from array at given time, or 0 when time is outside of range
108      */
109     public final Frequency interpolateVector(final Time time, final FrequencyVector demandVector, final TimeVector timeVector,
110             final boolean sliceStart)
111     {
112         try
113         {
114             // empty data or before start or after end, return 0
115             // case 1: t < t(0)
116             // case 2: sliceEnd & t == t(0), i.e. end of no-demand time before time array
117             // case 3: sliceStart & t == t(end), i.e. start of no-demand time after time array
118             // case 4: t > t(end)
119             if (timeVector.size() == 0 || (sliceStart ? time.lt(timeVector.get(0)) : time.le(timeVector.get(0))) || (sliceStart
120                     ? time.ge(timeVector.get(timeVector.size() - 1)) : time.gt(timeVector.get(timeVector.size() - 1))))
121             {
122                 return new Frequency(0.0, FrequencyUnit.PER_HOUR); // Frequency.ZERO give "Hz" which is not nice for flow
123             }
124             // interpolate
125             for (int i = 0; i < timeVector.size() - 1; i++)
126             {
127                 // cases where we can take the slice from i to i+1
128                 // case 1: sliceStart & t(i+1) > t [edge case: t(i) = t]
129                 // case 2: sliceEnd & t(i+1) >= t [edge case: t(i+1) = t]
130                 if (sliceStart ? timeVector.get(i + 1).gt(time) : timeVector.get(i + 1).ge(time))
131                 {
132                     return interpolate(demandVector.get(i), timeVector.get(i), demandVector.get(i + 1), timeVector.get(i + 1),
133                             time);
134                 }
135             }
136         }
137         catch (ValueRuntimeException ve)
138         {
139             // should not happen, vector lengths are checked when given is input
140             throw new RuntimeException("Index out of bounds.", ve);
141         }
142         // should not happen
143         throw new RuntimeException("Demand interpolation failed.");
144     }
145 
146 }