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