1 package org.opentrafficsim.core.geometry;
2
3 import java.util.Map;
4 import java.util.Map.Entry;
5 import java.util.NavigableMap;
6 import java.util.TreeMap;
7
8 import org.djutils.exceptions.Throw;
9 import org.djutils.immutablecollections.ImmutableLinkedHashSet;
10 import org.djutils.immutablecollections.ImmutableNavigableSet;
11 import org.djutils.immutablecollections.ImmutableSet;
12 import org.djutils.immutablecollections.ImmutableTreeSet;
13
14
15
16
17
18
19
20
21
22 public class FractionalLengthData
23 {
24
25
26 private final NavigableMap<Double, Double> data = new TreeMap<>();
27
28
29
30
31
32
33
34 public FractionalLengthData(final double... data) throws IllegalArgumentException
35 {
36 Throw.when(data.length < 2 || data.length % 2 > 0, IllegalArgumentException.class,
37 "Number of input values must be even and at least 2.");
38 for (int i = 0; i < data.length; i = i + 2)
39 {
40 Throw.when(data[i] < 0.0 || data[i] > 1.0, IllegalArgumentException.class,
41 "Fractional length %s is outside of range [0 ... 1].", data[i]);
42 this.data.put(data[i], data[i + 1]);
43 }
44 }
45
46
47
48
49
50
51
52 public FractionalLengthData(final Map<Double, Double> data) throws IllegalArgumentException
53 {
54 Throw.when(data == null || data.isEmpty(), IllegalArgumentException.class, "Input data is empty or null.");
55 for (Entry<Double, Double> entry : data.entrySet())
56 {
57 Throw.when(entry.getKey() < 0.0 || entry.getKey() > 1.0, IllegalArgumentException.class,
58 "Fractional length %s is outside of range [0 ... 1].", entry.getKey());
59 this.data.put(entry.getKey(), entry.getValue());
60 }
61 }
62
63
64
65
66
67
68
69
70 public double get(final double fractionalLength)
71 {
72 Double exact = this.data.get(fractionalLength);
73 if (exact != null)
74 {
75 return exact;
76 }
77 Entry<Double, Double> ceiling = this.data.ceilingEntry(fractionalLength);
78 if (ceiling == null)
79 {
80 return this.data.lastEntry().getValue();
81 }
82 Entry<Double, Double> floor = this.data.floorEntry(fractionalLength);
83 if (floor == null)
84 {
85 return this.data.firstEntry().getValue();
86 }
87 double w = (fractionalLength - floor.getKey()) / (ceiling.getKey() - floor.getKey());
88 return (1.0 - w) * floor.getValue() + w * ceiling.getValue();
89 }
90
91
92
93
94
95
96 public double getDerivative(final double fractionalLength)
97 {
98 Entry<Double, Double> ceiling, floor;
99 if (fractionalLength == 0.0)
100 {
101 ceiling = this.data.higherEntry(fractionalLength);
102 floor = this.data.floorEntry(fractionalLength);
103 }
104 else
105 {
106 ceiling = this.data.ceilingEntry(fractionalLength);
107 floor = this.data.lowerEntry(fractionalLength);
108 }
109 if (ceiling == null || floor == null)
110 {
111 return 0.0;
112 }
113 return (ceiling.getValue() - floor.getValue()) / (ceiling.getKey() - floor.getKey());
114 }
115
116
117
118
119
120 public ImmutableNavigableSet<Double> getFractionalLengths()
121 {
122 return new ImmutableTreeSet<>(this.data.keySet());
123 }
124
125
126
127
128
129 public ImmutableSet<Double> getValues()
130 {
131 return new ImmutableLinkedHashSet<>(this.data.values());
132 }
133
134
135
136
137
138 public double[] getFractionalLengthsAsArray()
139 {
140 NavigableMap<Double, Double> full = fullRange();
141 double[] fractionalLengths = new double[full.size()];
142 int i = 0;
143 for (double f : full.navigableKeySet())
144 {
145 fractionalLengths[i++] = f;
146 }
147 return fractionalLengths;
148 }
149
150
151
152
153
154 public double[] getValuesAsArray()
155 {
156 NavigableMap<Double, Double> full = fullRange();
157 double[] values = new double[full.size()];
158 int i = 0;
159 for (double f : full.navigableKeySet())
160 {
161 values[i++] = full.get(f);
162 }
163 return values;
164 }
165
166
167
168
169
170 private final NavigableMap<Double, Double> fullRange()
171 {
172 NavigableMap<Double, Double> full = new TreeMap<>(this.data);
173 full.put(0.0, full.firstEntry().getValue());
174 full.put(1.0, full.lastEntry().getValue());
175 return full;
176 }
177
178
179
180
181
182 public int size()
183 {
184 return this.data.size();
185 }
186
187
188
189
190
191
192
193
194 public static FractionalLengthData of(final double... data) throws IllegalArgumentException
195 {
196 return new FractionalLengthData(data);
197 }
198
199 }