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 :

http://dive4elements.wald.intevation.org