Mercurial > dive4elements > gnv-client
comparison gnv-artifacts/src/main/java/de/intevation/gnv/raster/Raster.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.raster; | |
10 | |
11 /** | |
12 * A 2D double valued representation of a raster array. | |
13 * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> | |
14 */ | |
15 public class Raster | |
16 { | |
17 /** | |
18 * A kernel can be applied to a raster to fold its values. | |
19 */ | |
20 public static class Kernel { | |
21 | |
22 /** | |
23 * The coefficients of the folding matrix. | |
24 */ | |
25 public static final class Coeff { | |
26 /** | |
27 * i delta index of this coefficient. | |
28 */ | |
29 public int di; | |
30 /** | |
31 * i delta index of this coefficient. | |
32 */ | |
33 public int dj; | |
34 /** | |
35 * The weight of this coefficient. | |
36 */ | |
37 public double c; | |
38 | |
39 /** | |
40 * Default constructor | |
41 */ | |
42 public Coeff() { | |
43 } | |
44 | |
45 /** | |
46 * Constructor to set the delta position and the weight of | |
47 * the coefficient. | |
48 * @param di The i delta index. | |
49 * @param dj The j delta index. | |
50 * @param c The weight of the index. | |
51 */ | |
52 public Coeff(int di, int dj, double c) { | |
53 this.di = di; | |
54 this.dj = dj; | |
55 this.c = c; | |
56 } | |
57 | |
58 /** | |
59 * Returns a string representation of this coefficient. | |
60 * @return The string representation. | |
61 */ | |
62 @Override | |
63 public String toString() { | |
64 return String.valueOf(c); | |
65 } | |
66 | |
67 } // class Coeff | |
68 | |
69 /** | |
70 * The coefficients of the folding matrix. | |
71 */ | |
72 protected Coeff [] coeffs; | |
73 /** | |
74 * The width of the kernel. | |
75 */ | |
76 protected int width; | |
77 | |
78 /** | |
79 * Default constrcutor. | |
80 */ | |
81 public Kernel() { | |
82 } | |
83 | |
84 /** | |
85 * Constructor for a kernel with a given coefficient matrix | |
86 * with a given width. | |
87 * @param coeffs The coefficients. | |
88 * @param width The width of the kernel. | |
89 */ | |
90 public Kernel(Coeff [] coeffs, int width) { | |
91 this.coeffs = coeffs; | |
92 this.width = width; | |
93 } | |
94 | |
95 /** | |
96 * Evaluates the 2D Gauss normal distribution a given x, y | |
97 * and a given sigma. | |
98 * @param x The x location. | |
99 * @param y The y location. | |
100 * @param sigma The sigma of the distribution. | |
101 * @return The value at point x, y. | |
102 */ | |
103 public static double gauss(double x, double y, double sigma) { | |
104 double s2 = sigma*sigma; | |
105 return 1.0d/(2.0d*Math.PI*s2)*Math.exp(-(x*x + y*y)/(2.0d*s2)); | |
106 } | |
107 | |
108 /** | |
109 * Creates a Gauss kernel with sigma = 1 and width 5. | |
110 * @return The new kernel. | |
111 */ | |
112 public static Kernel createGauss() { | |
113 return createGauss(1.0d, 5); | |
114 } | |
115 | |
116 /** | |
117 * Creates a Gauss kernel with a given sigma and width. | |
118 * @param sigma The sigma value | |
119 * @param width The width of the kernel. | |
120 * @return The new kernel. | |
121 */ | |
122 public static Kernel createGauss(double sigma, int width) { | |
123 Coeff [] coeffs = new Coeff[width*width]; | |
124 double sum = 0d; | |
125 for (int j = 0; j < width; ++j) { | |
126 int y = -width/2 + j; | |
127 for (int i = 0; i < width; ++i) { | |
128 int x = -width/2 + i; | |
129 double c = gauss(x, y, sigma); | |
130 coeffs[j*width + i] = new Coeff(x, y, c); | |
131 sum += c; | |
132 } | |
133 } | |
134 sum = 1.0/sum; | |
135 for (int i = 0; i < coeffs.length; ++i) { | |
136 coeffs[i].c *= sum; | |
137 } | |
138 return new Kernel(coeffs, width); | |
139 } | |
140 | |
141 /** | |
142 * Evaluates this Kernel at the raster at raster index i, j. | |
143 * @param access The accessor to the raster. | |
144 * @param i The i raster index. | |
145 * @param j The j raster index. | |
146 * @return The folded value at index i, j. | |
147 */ | |
148 public double fold(Access access, int i, int j) { | |
149 | |
150 double s = 0.0d; | |
151 | |
152 double unused = 0d; | |
153 | |
154 for (int k = 0; k < coeffs.length; ++k) { | |
155 Coeff coeff = coeffs[k]; | |
156 double v = access.get(i + coeff.di, j + coeff.dj); | |
157 if (Double.isNaN(v)) { | |
158 unused += coeff.c; | |
159 } | |
160 else { | |
161 s += v * coeff.c; | |
162 } | |
163 } | |
164 | |
165 return s*(1.0d - unused); | |
166 } | |
167 | |
168 /** | |
169 * Returns a string representation of this kernel. | |
170 * @return The string representation. | |
171 */ | |
172 @Override | |
173 public String toString() { | |
174 StringBuilder sb = new StringBuilder(); | |
175 | |
176 int height = coeffs.length/width; | |
177 | |
178 for (int j = 0; j < height; ++j) { | |
179 if (j > 0) { | |
180 sb.append(System.getProperty("line.separator")); | |
181 } | |
182 for (int i = 0; i < width; ++i) { | |
183 if (i > 0) { | |
184 sb.append("\t"); | |
185 } | |
186 sb.append(coeffs[j*width + i]); | |
187 } | |
188 } | |
189 | |
190 return sb.toString(); | |
191 } | |
192 } // class Kernel | |
193 | |
194 /** | |
195 * Interface of allow special access patterns to the raster, | |
196 * especially at the border. | |
197 */ | |
198 public interface Access { | |
199 | |
200 /** | |
201 * Returns the raster value at a given index point. | |
202 * @param i The i index | |
203 * @param j The j index | |
204 * @return The value of the raster. | |
205 */ | |
206 double get(int i, int j); | |
207 | |
208 } // interface Access | |
209 | |
210 /** | |
211 * Interface to decouple the lookup which is needed | |
212 * to map double values to integers. | |
213 */ | |
214 public interface ValueToIndex { | |
215 | |
216 /** | |
217 * Returns the corresponding integer to a given value. | |
218 * @param value The value to be queried. | |
219 * @return The integer value or -1 if no such value was found. | |
220 */ | |
221 int toIndex(double value); | |
222 | |
223 } // interface ValueToIndex; | |
224 | |
225 /** | |
226 * Brings raster to an integer representation with equal sized bins | |
227 * of integer vaules. | |
228 */ | |
229 public static class IsoClasses | |
230 implements ValueToIndex | |
231 { | |
232 /** | |
233 * Linear scaling factor. | |
234 */ | |
235 protected double m; | |
236 /** | |
237 * Linear offset. | |
238 */ | |
239 protected double b; | |
240 /** | |
241 * Number of classes. | |
242 */ | |
243 protected int numClasses; | |
244 | |
245 /** | |
246 * Constructor to build a lookup to transform | |
247 * an raster into an equals sized bin integer array. | |
248 * @param raster The original raster | |
249 * @param numClasses The number of classes. | |
250 */ | |
251 public IsoClasses(Raster raster, int numClasses) { | |
252 | |
253 this.numClasses = numClasses; | |
254 | |
255 double min = Double.MAX_VALUE; | |
256 double max = -Double.MAX_VALUE; | |
257 | |
258 double [] src = raster.raster; | |
259 | |
260 for (int i = 0; i < src.length; ++i) { | |
261 double x = src[i]; | |
262 if (!Double.isNaN(x)) { | |
263 if (x < min) min = x; | |
264 if (x > max) max = x; | |
265 } | |
266 } | |
267 | |
268 /* f(min) = 0, f(max) = numClasses - 1 | |
269 | |
270 I: 0 = m*min + b | |
271 II: numClasses - 1 = m*max + b | |
272 | |
273 II - I: | |
274 numClasses - 1 = m*(max - min) | |
275 | |
276 m = (numClasses - 1)/(max - min) | |
277 b = m*min | |
278 */ | |
279 | |
280 if (max == min) { | |
281 m = 0; | |
282 b = 0; | |
283 } | |
284 else { | |
285 m = (numClasses - 1)/(max - min); | |
286 b = m*min; | |
287 } | |
288 } | |
289 | |
290 /** | |
291 * Implements the double to integer lookup. | |
292 * @param value The value to map. | |
293 * @return The mapped value. | |
294 */ | |
295 public int toIndex(double value) { | |
296 return Double.isNaN(value) | |
297 ? -1 | |
298 : Math.min(Math.max(0, (int)Math.round(m*value + b)), numClasses-1); | |
299 } | |
300 } // class IsoClasses | |
301 | |
302 /** | |
303 * Internal representation of the raster. | |
304 */ | |
305 protected double [] raster; | |
306 /** | |
307 * The width of the raster. | |
308 */ | |
309 protected int width; | |
310 | |
311 /** | |
312 * Default constructor. Creates an 0x0 raster. | |
313 */ | |
314 public Raster() { | |
315 this(new double[0], 0); | |
316 } | |
317 | |
318 /** | |
319 * Creates a raster with given values and width. | |
320 * @param raster | |
321 * @param width | |
322 */ | |
323 public Raster(double [] raster, int width) { | |
324 this.raster = raster; | |
325 this.width = width; | |
326 } | |
327 | |
328 /** | |
329 * The width of the raster. | |
330 * @return The width. | |
331 */ | |
332 public int getWidth() { | |
333 return width; | |
334 } | |
335 | |
336 /** | |
337 * The height of the raster. | |
338 * @return The height. | |
339 */ | |
340 public int getHeight() { | |
341 return raster.length / width; | |
342 } | |
343 | |
344 /** | |
345 * The values of the raster. | |
346 * @return The values. | |
347 */ | |
348 public double [] getValues() { | |
349 return raster; | |
350 } | |
351 | |
352 /** | |
353 * Creates an integer representation of this raster using a | |
354 * given value to index transformer. | |
355 * @param valueToIndex The transformer to map double to integer values. | |
356 * @return The new created integer representation. | |
357 */ | |
358 public int [] toIndexed(ValueToIndex valueToIndex) { | |
359 int [] dst = new int[raster.length]; | |
360 for (int i = 0; i < raster.length; ++i) { | |
361 dst[i] = valueToIndex.toIndex(raster[i]); | |
362 } | |
363 return dst; | |
364 } | |
365 | |
366 /** | |
367 * Creates a new raster by applying a kernel to this raster. | |
368 * Access to the raster is continuous at the border. | |
369 * The original raster is not modified. | |
370 * @param kernel The kernel to be applied. | |
371 * @return The new raster. | |
372 */ | |
373 public Raster create(Kernel kernel) { | |
374 double [] dst = new double[raster.length]; | |
375 Raster r = new Raster(dst, width); | |
376 r.apply(kernel, continueBorder()); | |
377 return r; | |
378 } | |
379 | |
380 /** | |
381 * Creates a new raster by applying a kernel to this raster. | |
382 * Access to the raster is given. | |
383 * @param kernel The kernel to be applied. | |
384 * @param access The access method to the raster. | |
385 */ | |
386 public void apply(Kernel kernel, Access access) { | |
387 int height = getHeight(); | |
388 for (int j = 0; j < height; ++j) { | |
389 int row = j*width; | |
390 for (int i = 0; i < width; ++i) { | |
391 raster[row + i] = kernel.fold(access, i, j); | |
392 } | |
393 } | |
394 } | |
395 | |
396 /** | |
397 * Returns a continuous access to the raster. Values | |
398 * at the border are repeated til infinity if indices | |
399 * are accessed outside the defined area. | |
400 * @return The accessor. | |
401 */ | |
402 public Access continueBorder() { | |
403 return new Access() { | |
404 int height = getHeight()-1; | |
405 public double get(int i, int j) { | |
406 if (i < 0) i = 0; | |
407 else if (i >= width) i = width-1; | |
408 if (j < 0) j = 0; | |
409 else if (j > height) j = height; | |
410 return raster[j*width + i]; | |
411 } | |
412 }; | |
413 } | |
414 } | |
415 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : |