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