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