Mercurial > dive4elements > gnv-client
comparison gnv-artifacts/src/main/java/de/intevation/gnv/math/Interpolation3D.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 import com.vividsolutions.jts.geom.Envelope; | |
13 | |
14 import com.vividsolutions.jts.index.strtree.STRtree; | |
15 | |
16 import java.awt.Dimension; | |
17 | |
18 import java.io.Serializable; | |
19 | |
20 import java.util.Arrays; | |
21 import java.util.List; | |
22 | |
23 import org.apache.log4j.Logger; | |
24 | |
25 /** | |
26 * Interpolates parameter values along a given line string from surface | |
27 * to the sea ground to generate "Profilschnitte". | |
28 * | |
29 * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> | |
30 */ | |
31 public class Interpolation3D | |
32 implements Serializable | |
33 { | |
34 private static Logger log = Logger.getLogger(Interpolation3D.class); | |
35 | |
36 /** | |
37 * The default width of the interpolation: {@value} | |
38 */ | |
39 public static final int DEFAULT_WIDTH = 1024; | |
40 | |
41 /** | |
42 * The default height of the interpolation: {@value} | |
43 */ | |
44 public static final int DEFAULT_HEIGHT = 768; | |
45 | |
46 /** | |
47 * Epsilon for numerical stability. | |
48 */ | |
49 public static final double EPS = 1e-6d; | |
50 | |
51 /** | |
52 * The width of the interpolation. | |
53 */ | |
54 protected int width; | |
55 | |
56 /** | |
57 * The height of the interpolation. | |
58 */ | |
59 protected int height; | |
60 | |
61 /** | |
62 * The cell width of the interpolation in world units. | |
63 */ | |
64 protected double cellWidth; | |
65 | |
66 /** | |
67 * The cell height of the interpolation in world units. | |
68 */ | |
69 protected double cellHeight; | |
70 | |
71 /** | |
72 * The coordinates of the interpolation. | |
73 */ | |
74 protected Coordinate[] coordinates; | |
75 | |
76 /** | |
77 * The generated raster. | |
78 */ | |
79 protected double [] raster; | |
80 | |
81 /** | |
82 * The sea ground depth along the line string. | |
83 */ | |
84 protected double [] depths; | |
85 | |
86 /** | |
87 * Default constructor. | |
88 */ | |
89 public Interpolation3D() { | |
90 this(DEFAULT_WIDTH, DEFAULT_HEIGHT); | |
91 } | |
92 | |
93 /** | |
94 * Constructor to create a Interpolation3D with a given size. | |
95 * @param size The size of the interpolation. | |
96 */ | |
97 public Interpolation3D(Dimension size) { | |
98 this(size.width, size.height); | |
99 } | |
100 | |
101 /** | |
102 * Constructor to create a Interpolation3D with a given size. | |
103 * @param width The width of the interpolation. | |
104 * @param height the height of the interpolation. | |
105 */ | |
106 public Interpolation3D(int width, int height) { | |
107 this.width = width; | |
108 this.height = height; | |
109 } | |
110 | |
111 /** | |
112 * Returns the raster height of the interpolation. | |
113 * @return The raster height of the interpolation. | |
114 */ | |
115 public int getHeight() { | |
116 return height; | |
117 } | |
118 | |
119 /** | |
120 * Returns the raster width of the interpolation. | |
121 * @return The raster width of the interpolation. | |
122 */ | |
123 public int getWidth() { | |
124 return width; | |
125 } | |
126 | |
127 /** | |
128 * Returns the cell width of the interpolation in world units. | |
129 * @return The cell width of the interpolation in world units. | |
130 */ | |
131 public double getCellWidth() { | |
132 return cellWidth; | |
133 } | |
134 | |
135 /** | |
136 * Returns the cell height of the interpolation in world units. | |
137 * @return The cell height of the interpolation in world units. | |
138 */ | |
139 public double getCellHeight() { | |
140 return cellHeight; | |
141 } | |
142 | |
143 /** | |
144 * Returns the coordinates used for the interpolation. | |
145 * @return the coordinates used for the interpolation. | |
146 */ | |
147 public Coordinate[] getCoordinates() { | |
148 return coordinates; | |
149 } | |
150 | |
151 /** | |
152 * Returns the generated raster. | |
153 * @return The generated raster. | |
154 */ | |
155 public double [] getRaster() { | |
156 return raster; | |
157 } | |
158 | |
159 /** | |
160 * Returns the depths along the line string path. | |
161 * @return The depth along the line string path. | |
162 */ | |
163 public double [] getDepths() { | |
164 return depths; | |
165 } | |
166 | |
167 /** | |
168 * Returns the deepest depth along the line string path. | |
169 * @return The deepest depth along the line string path. | |
170 */ | |
171 public double getMaxDepth() { | |
172 double maxDepth = Double.MAX_VALUE; | |
173 for (int i = depths!=null?depths.length-1:0; i >= 0; --i) { | |
174 double d = depths[i]; | |
175 if (!Double.isNaN(d) && d < maxDepth) { | |
176 maxDepth = d; | |
177 } | |
178 } | |
179 return maxDepth; | |
180 } | |
181 | |
182 /** | |
183 * Interpolates parameters along a given line string path from the surface | |
184 * to the sea ground. | |
185 * @param path The line string path. | |
186 * @param points The sample points. | |
187 * @param from Start point in scalar terms of the line string. | |
188 * @param to End point in scalar terms of the line string. | |
189 * @param metrics The used metric. | |
190 * @param xyDepth The callback to query the depth at a given point. | |
191 * @return true if the interpolation succeeds, else false. | |
192 */ | |
193 public boolean interpolate( | |
194 List<? extends Coordinate> path, | |
195 List<? extends XYColumn> points, | |
196 double from, | |
197 double to, | |
198 Metrics metrics, | |
199 XYDepth xyDepth | |
200 ) { | |
201 boolean debug = log.isDebugEnabled(); | |
202 | |
203 int N = path.size(); | |
204 int M = points.size(); | |
205 | |
206 if (debug) { | |
207 log.debug("Size of path: " + N); | |
208 log.debug("Size of points: " + M); | |
209 } | |
210 | |
211 if (M < 1 || N < 2) { // nothing to do | |
212 return false; | |
213 } | |
214 | |
215 List<GridCell> cells = GridCell.pointsToGridCells( | |
216 points, Interpolation2D.relevantArea(path, points)); | |
217 | |
218 if (cells.isEmpty()) { | |
219 log.warn("no cells found"); | |
220 return false; | |
221 } | |
222 | |
223 // put into spatial index to speed up finding neighbors. | |
224 STRtree spatialIndex = new STRtree(); | |
225 | |
226 for (GridCell cell: cells) { | |
227 spatialIndex.insert(cell.getEnvelope(), cell); | |
228 } | |
229 | |
230 LinearToMap linearToMap = new LinearToMap( | |
231 path, from, to, metrics); | |
232 | |
233 double [] depths = new double[width]; | |
234 Arrays.fill(depths, Double.NaN); | |
235 | |
236 Coordinate[] coordinates = new Coordinate[width]; | |
237 | |
238 double cellWidth = (to - from)/width; | |
239 | |
240 double maxDepth = Double.MAX_VALUE; | |
241 | |
242 int i = 0; | |
243 Coordinate center = new Coordinate(); | |
244 for (double p = cellWidth*0.5; i < depths.length; ++i, p += cellWidth) { | |
245 if (linearToMap.locate(p, center)) { | |
246 coordinates[i] = (Coordinate) center.clone(); | |
247 double depth = xyDepth.depth(center); | |
248 depths[i] = depth; | |
249 if (depth < maxDepth) { | |
250 maxDepth = depth; | |
251 } | |
252 } | |
253 } | |
254 | |
255 if (maxDepth == Double.MAX_VALUE) { | |
256 log.warn("no depth found -> no interpolation"); | |
257 return false; | |
258 } | |
259 | |
260 double cellHeight = Math.abs(maxDepth)/height; | |
261 | |
262 if (debug) { | |
263 log.debug("max depth found: " + maxDepth); | |
264 log.debug("cell size: " + cellWidth + " x " + cellHeight); | |
265 } | |
266 | |
267 double [] raster = new double[width*height]; | |
268 Arrays.fill(raster, Double.NaN); | |
269 | |
270 Envelope queryBuffer = new Envelope(); | |
271 GridCell.CellFinder finder = new GridCell.CellFinder(); | |
272 | |
273 i = 0; | |
274 for (double p = cellWidth*0.5; i < depths.length; ++i, p += cellWidth) { | |
275 double depth = depths[i]; | |
276 if (Double.isNaN(depth) || depth >= 0d) { | |
277 continue; | |
278 } | |
279 linearToMap.locate(p, center); | |
280 | |
281 queryBuffer.init( | |
282 center.x - EPS, center.x + EPS, | |
283 center.y - EPS, center.y + EPS); | |
284 | |
285 finder.prepare(center); | |
286 spatialIndex.query(queryBuffer, finder); | |
287 | |
288 GridCell found = finder.found; | |
289 | |
290 if (found == null) { | |
291 continue; | |
292 } | |
293 | |
294 XYColumn n0 = (XYColumn)found.p1; | |
295 XYColumn n1 = (XYColumn)found.p2; | |
296 XYColumn n2 = (XYColumn)found.p3; | |
297 XYColumn n3 = (XYColumn)found.p4; | |
298 | |
299 if (n0.prepare(xyDepth) | |
300 && n1.prepare(xyDepth) | |
301 && n2.prepare(xyDepth) | |
302 && n3.prepare(xyDepth) | |
303 ) { | |
304 double y1 = Interpolation2D.interpolate( | |
305 n0.x, n0.y, | |
306 n1.x, n1.y, | |
307 center.x); | |
308 double y2 = Interpolation2D.interpolate( | |
309 n2.x, n2.y, | |
310 n3.x, n3.y, | |
311 center.x); | |
312 double z = -cellHeight*0.5; | |
313 for (int j = i; | |
314 j < raster.length && z >= depth; | |
315 z -= cellHeight, j += width) { | |
316 | |
317 double v0 = n0.value(z); | |
318 double v1 = n1.value(z); | |
319 double v2 = n2.value(z); | |
320 double v3 = n3.value(z); | |
321 | |
322 double z1 = Interpolation2D.interpolate( | |
323 n0.x, v0, | |
324 n1.x, v1, | |
325 center.x); | |
326 double z2 = Interpolation2D.interpolate( | |
327 n2.x, v2, | |
328 n3.x, v3, | |
329 center.x); | |
330 double value = Interpolation2D.interpolate( | |
331 y1, z1, | |
332 y2, z2, | |
333 center.y); | |
334 raster[j] = value; | |
335 } | |
336 // XXX: Adjusted depth to create no gap | |
337 // between last value and ground. | |
338 depths[i] = z+0.5d*cellHeight; | |
339 } // down the x/y column | |
340 } // all along the track | |
341 | |
342 this.coordinates = coordinates; | |
343 this.depths = depths; | |
344 this.raster = raster; | |
345 this.cellWidth = cellWidth; | |
346 this.cellHeight = cellHeight; | |
347 | |
348 return true; | |
349 } | |
350 } | |
351 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : |