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