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 :

http://dive4elements.wald.intevation.org