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 :

http://dive4elements.wald.intevation.org