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