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