Mercurial > dive4elements > gnv-client
comparison gnv-artifacts/src/main/java/de/intevation/gnv/math/LinearToMap.java @ 875:5e9efdda6894
merged gnv-artifacts/1.0
author | Thomas Arendsen Hein <thomas@intevation.de> |
---|---|
date | Fri, 28 Sep 2012 12:13:56 +0200 |
parents | bb7afd783321 |
children | f953c9a559d8 |
comparison
equal
deleted
inserted
replaced
722:bb3ffe7d719e | 875:5e9efdda6894 |
---|---|
1 package de.intevation.gnv.math; | |
2 | |
3 import com.vividsolutions.jts.geom.Coordinate; | |
4 | |
5 import java.util.Iterator; | |
6 import java.util.List; | |
7 import java.util.NoSuchElementException; | |
8 | |
9 /** | |
10 * Given a list of line segments instances of this class are able | |
11 * to span a metric system between a start and an end point | |
12 * represented as scalar values to 2D coordinate on the | |
13 * course of the segments. | |
14 * | |
15 * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> | |
16 */ | |
17 public class LinearToMap | |
18 { | |
19 /** | |
20 * Represents a segment of the line string. | |
21 */ | |
22 public static final class Range { | |
23 private Range next; | |
24 | |
25 private double from; | |
26 private double to; | |
27 private double b; | |
28 | |
29 private Coordinate p1; | |
30 private Coordinate p2; | |
31 | |
32 private Interpolator interpolator; | |
33 | |
34 /** | |
35 * Default constructor. | |
36 */ | |
37 public Range() { | |
38 } | |
39 | |
40 /** | |
41 * Constructor to create a segment that maps | |
42 * a coordinate pair to two scalar values. | |
43 * Interpolations inside this segment are done with | |
44 * a given interpolator. | |
45 * @param from Start point of the segment. | |
46 * @param to End point of the segment. | |
47 * @param interpolator The interpolator. | |
48 * @param p1 The scalar value mapped to the start point. | |
49 * @param p2 The scalar value mappend to the end point. | |
50 */ | |
51 public Range( | |
52 double from, | |
53 double to, | |
54 Interpolator interpolator, | |
55 Coordinate p1, | |
56 Coordinate p2 | |
57 ) { | |
58 this.from = from; | |
59 this.to = to; | |
60 this.interpolator = interpolator; | |
61 this.p1 = p1; | |
62 this.p2 = p2; | |
63 | |
64 b = from == to | |
65 ? 0d | |
66 : 1.0d/(to - from); | |
67 } | |
68 | |
69 /** | |
70 * Interpolated a coordinate on the segment given a scalar value | |
71 * between the start and end point of the range. | |
72 * @param x The scalar value. | |
73 * @param v The interpolated value is stored here. | |
74 */ | |
75 public void eval(double x, Coordinate v) { | |
76 interpolator.interpolate((x - from)*b, v); | |
77 } | |
78 | |
79 /** | |
80 * Checks if a given value is inside this segment. | |
81 * @param x The value to test | |
82 * @return true if inside, else false. | |
83 */ | |
84 public boolean inside(double x) { | |
85 return x >= from && x <= to; | |
86 } | |
87 | |
88 /** | |
89 * Returns the start point of this segment. | |
90 * @return The start point. | |
91 */ | |
92 public Coordinate startPoint() { | |
93 return p1; | |
94 } | |
95 | |
96 /** | |
97 * Return the end point of this segment. | |
98 * @return The end point. | |
99 */ | |
100 public Coordinate endPoint() { | |
101 return p2; | |
102 } | |
103 } // class Range | |
104 | |
105 /** | |
106 * The head of the internal range list. | |
107 */ | |
108 protected Range head; | |
109 | |
110 /** | |
111 * The last accessed segment. Used to accelerate | |
112 * the access of the right segment. | |
113 */ | |
114 protected Range last; | |
115 | |
116 /** | |
117 * Default constructor. | |
118 */ | |
119 public LinearToMap() { | |
120 } | |
121 | |
122 /** | |
123 * Constructor to create a LinearToMap that maps | |
124 * given scalar values to coordinates of a given | |
125 * list of line segments. | |
126 * @param path The list of line segments. | |
127 * @param from The start value mapped to the start point | |
128 * of the first line segment. | |
129 * @param to The end value mapped to the end point of | |
130 * the last line segment. | |
131 * @param metrics The metric used to span the 2D space. | |
132 */ | |
133 public LinearToMap( | |
134 List<? extends Coordinate> path, | |
135 double from, | |
136 double to, | |
137 Metrics metrics | |
138 ) { | |
139 double diagramLength = Math.abs(to - from); | |
140 | |
141 double worldLength = length(path, metrics); | |
142 | |
143 double rangeStart = from; | |
144 | |
145 Range last = null; | |
146 | |
147 for (int i = 1, N = path.size(); i < N; ++i) { | |
148 Coordinate p1 = path.get(i-1); | |
149 Coordinate p2 = path.get(i); | |
150 double segmentLength = metrics.distance(p1, p2); | |
151 | |
152 double relativeLength = segmentLength / worldLength; | |
153 | |
154 double rangeLength = diagramLength * relativeLength; | |
155 | |
156 double rangeEnd = rangeStart + rangeLength; | |
157 | |
158 Range range = new Range( | |
159 rangeStart, rangeEnd, | |
160 metrics.getInterpolator(p1, p2), | |
161 p1, p2); | |
162 | |
163 if (last == null) { | |
164 last = head = range; | |
165 } | |
166 else { | |
167 last.next = range; | |
168 last = range; | |
169 } | |
170 rangeStart = rangeEnd; | |
171 } | |
172 } | |
173 | |
174 /** | |
175 * Returns a segment on which a given value is found. | |
176 * @param diagramX The value. | |
177 * @return The segment or null if no matching segment was found. | |
178 */ | |
179 protected Range locateRange(double diagramX) { | |
180 | |
181 if (last != null && last.inside(diagramX)) { | |
182 return last; | |
183 } | |
184 | |
185 Range current = head; | |
186 while (current != null) { | |
187 if (current.inside(diagramX)) { | |
188 return last = current; | |
189 } | |
190 current = current.next; | |
191 } | |
192 | |
193 return null; | |
194 } | |
195 | |
196 /** | |
197 * Interpolates a coordinate at a given scalar position. | |
198 * @param diagramX The scalar position. | |
199 * @param v The interpolated coordinate is stored here. | |
200 * @return true if the scalar position is inside the | |
201 * spanned range of the line string, else false. | |
202 */ | |
203 public boolean locate(double diagramX, Coordinate v) { | |
204 Range range = locateRange(diagramX); | |
205 if (range == null) { | |
206 return false; | |
207 } | |
208 range.eval(diagramX, v); | |
209 return true; | |
210 } | |
211 | |
212 /** | |
213 * Returns the length of a given line string using | |
214 * a given metric. | |
215 * @param path The line string. | |
216 * @param metrics The used metric. | |
217 * @return The length of the line string. | |
218 */ | |
219 public static double length( | |
220 List<? extends Coordinate> path, | |
221 Metrics metrics | |
222 ) { | |
223 double sum = 0d; | |
224 for (int i = path.size()-1; i >= 1; --i) { | |
225 Coordinate p1 = path.get(i); | |
226 Coordinate p2 = path.get(i-1); | |
227 sum += metrics.distance(p1, p2); | |
228 } | |
229 return sum; | |
230 } | |
231 | |
232 /** | |
233 * Return the number of segments in this map. | |
234 * @return the number of segments. | |
235 */ | |
236 public int numRanges() { | |
237 int count = 0; | |
238 Range current = head; | |
239 while (current != null) { | |
240 ++count; | |
241 current = current.next; | |
242 } | |
243 return count; | |
244 } | |
245 | |
246 /** | |
247 * Returns an iterator over all segments of this map. | |
248 * @return The iterator. | |
249 */ | |
250 public Iterator ranges() { | |
251 return new Iterator() { | |
252 | |
253 Range current = head; | |
254 | |
255 public boolean hasNext() { | |
256 return current != null; | |
257 } | |
258 | |
259 public Object next() { | |
260 if (!hasNext()) { | |
261 throw new NoSuchElementException(); | |
262 } | |
263 Range x = current; | |
264 current = current.next; | |
265 return x; | |
266 } | |
267 | |
268 public void remove() { | |
269 throw new UnsupportedOperationException(); | |
270 } | |
271 }; | |
272 } | |
273 } | |
274 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : |