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} (≤ {@code time})
78 * @param frequency1 frequency at {@code time1}
79 * @param time1 time of {@code frequency1} (> {@code time})
80 * @param time {@code time0} ≤ {@code time} < {@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} (≤ {@code time})
89 * @param frequency1 frequency at {@code time1}
90 * @param time1 time of {@code frequency1} (> {@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 }