comparison gnv-artifacts/src/main/java/de/intevation/gnv/raster/Vectorizer.java @ 801:d766fe2d917a

More javadoc. gnv-artifacts/trunk@883 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Tue, 06 Apr 2010 16:53:43 +0000
parents 6cff63d0c434
children f953c9a559d8
comparison
equal deleted inserted replaced
800:db5b04ecb426 801:d766fe2d917a
8 import java.util.List; 8 import java.util.List;
9 9
10 import org.apache.log4j.Logger; 10 import org.apache.log4j.Logger;
11 11
12 /** 12 /**
13 * Instances of this class are able to vectorize 2D integer arrays
14 * with a kind of flood filling regions detection mechanism to
15 * a set of line strings and polygons.
16 *
13 * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> 17 * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a>
14 */ 18 */
15 public class Vectorizer 19 public class Vectorizer
16 { 20 {
17 private static Logger log = Logger.getLogger(Vectorizer.class); 21 private static Logger log = Logger.getLogger(Vectorizer.class);
18 22
23 /**
24 * Callback to process the found line strings and polygons.
25 */
19 public interface RingsHandler { 26 public interface RingsHandler {
20 27
28 /**
29 * Called from {@link #process(de.intevation.gnv.raster.Vectorizer.RingsHandler) }
30 * to give the found features to the postprocessing backend.
31 * @param rings The found line strings and polygons.
32 * @param value The integer value of the raster surrounded by
33 * the features.
34 * @param width The width of the index space of the vectorized data.
35 * @param height The height of the index space of the vectorized data.
36 */
21 void handleRings( 37 void handleRings(
22 List<Edge> rings, 38 List<Edge> rings,
23 int value, 39 int value,
24 int width, 40 int width,
25 int height); 41 int height);
26 42
27 } // interface RingsHandler 43 } // interface RingsHandler
28 44
45 /**
46 * Doubly link list representing a line string or a polygon.
47 */
29 public static final class Edge { 48 public static final class Edge {
30 49
50 /**
51 * The predecessor.
52 */
31 public Edge prev; 53 public Edge prev;
54 /**
55 * The successor.
56 */
32 public Edge next; 57 public Edge next;
33 58
59 /**
60 * Index of first vertex. To separate x and y values
61 * you have to divide the value by the width of the
62 * index space.
63 */
34 public int a; 64 public int a;
65 /**
66 * Index of second vertex. To separate x and y values
67 * you have to divide the value by the width of the
68 * index space.
69 */
35 public int b; 70 public int b;
36 71
72 /**
73 * Default constructor.
74 */
37 public Edge() { 75 public Edge() {
38 } 76 }
39 77
78 /**
79 * Constructor to create Edge with two vertices.
80 * @param a The first vertex.
81 * @param b The second vertex.
82 */
40 public Edge(int a, int b) { 83 public Edge(int a, int b) {
41 this.a = a; 84 this.a = a;
42 this.b = b; 85 this.b = b;
43 } 86 }
44 87
88 /**
89 * Copy constructor
90 * @param other The edge to clone.
91 */
45 public Edge(Edge other) { 92 public Edge(Edge other) {
46 a = other.a; 93 a = other.a;
47 b = other.b; 94 b = other.b;
48 } 95 }
49 96
97 /**
98 * Chain a given edge segment to this segment. The
99 * side of the chaining is determined by a given
100 * parameter.
101 * @param other The segment to chain.
102 * @param found The side to chain.
103 */
50 public void chain(Edge other, int found) { 104 public void chain(Edge other, int found) {
51 105
52 if (found == a) { 106 if (found == a) {
53 other.next = this; 107 other.next = this;
54 prev = other; 108 prev = other;
62 } 116 }
63 117
64 throw new IllegalStateException("cannot chain"); 118 throw new IllegalStateException("cannot chain");
65 } 119 }
66 120
121 /**
122 * Tells if the list is complete which means that this
123 * edge list is a closed polygon.
124 * @return true if edge list is closed else false.
125 */
67 public boolean isComplete() { 126 public boolean isComplete() {
68 Edge current = this; 127 Edge current = this;
69 do { 128 do {
70 if (current.prev == null || current.next == null) { 129 if (current.prev == null || current.next == null) {
71 return false; 130 return false;
74 } 133 }
75 while (current != this); 134 while (current != this);
76 return true; 135 return true;
77 } 136 }
78 137
138 /**
139 * Returns the length of this edge list in next direction.
140 * Segments in prev direction are ignored.
141 * @return The length of this edge list.
142 */
79 public int length() { 143 public int length() {
80 int length = 0; 144 int length = 0;
81 Edge current = this; 145 Edge current = this;
82 do { ++length; } 146 do { ++length; }
83 while ((current = current.next) != null && current != this); 147 while ((current = current.next) != null && current != this);
84 return length; 148 return length;
85 } 149 }
86 150
87 public Edge head() { 151 /**
88 Edge current = this; 152 * Returns the head node of this edge list.
89 while (current.prev != null) { 153 * @return The head node.
90 current = current.prev; 154 */
91 } 155 public Edge head() {
92 return current; 156 Edge current = this;
93 } 157 while (current.prev != null) {
94 158 current = current.prev;
159 }
160 return current;
161 }
162
163 /**
164 * Hashes the two vertex indices to a common value.
165 * @return The hash value.
166 */
167 @Override
95 public int hashCode() { 168 public int hashCode() {
96 return (a << 16) | b; 169 return (a << 16) | b;
97 } 170 }
98 171
172 /**
173 * Two edges are considered equal if they have the same
174 * a and b vertices.
175 * @param other The other edge.
176 * @return true if the edges are equal else false.
177 */
178 @Override
99 public boolean equals(Object other) { 179 public boolean equals(Object other) {
100 Edge e = (Edge)other; 180 Edge e = (Edge)other;
101 return a == e.a && b == e.b; 181 return a == e.a && b == e.b;
102 } 182 }
103 } // class Edge 183 } // class Edge
104 184
185 /**
186 * Simplifies a given edge list by removing collinear vertices.
187 * Attention: The original list is modified.
188 * @param edge The edge list to simplify.
189 * @param width The width of the vertex index space.
190 * @return The simplified list.
191 */
105 protected static Edge simplify(Edge edge, int width) { 192 protected static Edge simplify(Edge edge, int width) {
106 193
107 Edge e1 = edge, start = edge; 194 Edge e1 = edge, start = edge;
108 195
109 int length = edge.length(); 196 int length = edge.length();
148 while (length > 1 && e2 != null && count < length + 2); 235 while (length > 1 && e2 != null && count < length + 2);
149 236
150 return start; 237 return start;
151 } 238 }
152 239
240 /**
241 * The raster to be traced.
242 */
153 protected int [] raster; 243 protected int [] raster;
244 /**
245 * The width of the raster.
246 */
154 protected int width; 247 protected int width;
248 /**
249 * Map of the currently open edges.
250 */
155 protected TIntObjectHashMap openEdges; 251 protected TIntObjectHashMap openEdges;
252 /**
253 * List of rings already found.
254 */
156 protected List<Edge> rings; 255 protected List<Edge> rings;
256 /**
257 * Flag to signal if a simplification should be performed
258 * after a ring is completed.
259 */
157 protected boolean simplify; 260 protected boolean simplify;
158 261
262 /**
263 * Default constructor. Simplification is turned on.
264 */
159 public Vectorizer() { 265 public Vectorizer() {
160 this(true); 266 this(true);
161 } 267 }
162 268
269 /**
270 * Constructor to create a vectorized with an explicit
271 * simplification policy.
272 * @param simplify Indicates if simplification should be
273 * used on ring completion.
274 */
163 public Vectorizer(boolean simplify) { 275 public Vectorizer(boolean simplify) {
164 openEdges = new TIntObjectHashMap(); 276 openEdges = new TIntObjectHashMap();
165 rings = new ArrayList<Edge>(); 277 rings = new ArrayList<Edge>();
166 this.simplify = simplify; 278 this.simplify = simplify;
167 } 279 }
168 280
281 /**
282 * Constructor to create a vectorizer with a given raster and width.
283 * Simplification is turn on.
284 * @param raster The raster to be vectorized.
285 * @param width The width of the raster.
286 */
169 public Vectorizer(int [] raster, int width) { 287 public Vectorizer(int [] raster, int width) {
170 this(true, raster, width); 288 this(true, raster, width);
171 } 289 }
172 290
291 /**
292 * Constructor to create a vectorizer with a given raster, width
293 * and an explicit simplification policy.
294 * @param simplify Indicates if simplification should be
295 * used on ring completion.
296 * @param raster The raster to be vectorized.
297 * @param width The width of the raster.
298 */
173 public Vectorizer(boolean simplify, int [] raster, int width) { 299 public Vectorizer(boolean simplify, int [] raster, int width) {
174 this(simplify); 300 this(simplify);
175 this.raster = raster; 301 this.raster = raster;
176 this.width = width; 302 this.width = width;
177 } 303 }
178 304
305 /**
306 * Returns (x, y+1) for given vertex in index space.
307 * @param i vertex in index space.
308 * @param w width of raster.
309 * @return (x, y+1) in index space.
310 */
179 public static final int tl(int i, int w) { 311 public static final int tl(int i, int w) {
180 int x = i % w; 312 int x = i % w;
181 int y = i / w; 313 int y = i / w;
182 return x + (w + 1)*y; 314 return x + (w + 1)*y;
183 } 315 }
184 316
317 /**
318 * Returns tl(i, w) + 1.
319 * @param i vertex in index space.
320 * @param w width of raster.
321 * @return tl(i, w) + 1.
322 */
185 public static final int tr(int i, int w) { 323 public static final int tr(int i, int w) {
186 return tl(i, w) + 1; 324 return tl(i, w) + 1;
187 } 325 }
188 326
327 /**
328 * Returns tl(i, w) + w + 1.
329 * @param i vertex in index space.
330 * @param w width of raster.
331 * @return tl(i, w) + w + 1.
332 */
189 public static final int bl(int i, int w) { 333 public static final int bl(int i, int w) {
190 return tl(i, w) + w + 1; 334 return tl(i, w) + w + 1;
191 } 335 }
192 336
337 /**
338 * Returns bl(i, w) + 1.
339 * @param i vertex in index space.
340 * @param w width of raster.
341 * @return bl(i, w) + 1.
342 */
193 public static final int br(int i, int w) { 343 public static final int br(int i, int w) {
194 return bl(i, w) + 1; 344 return bl(i, w) + 1;
195 } 345 }
196 346
347 /**
348 * Resets open resources after a set of features were found.
349 */
197 protected void resetRegion() { 350 protected void resetRegion() {
198 openEdges.clear(); 351 openEdges.clear();
199 rings.clear(); 352 rings.clear();
200 } 353 }
201 354
355 /**
356 * Adds an edge to the map of open edges, joins it
357 * with its direct neighbors of if complete add the
358 * list to the complete features.
359 * @param edge
360 */
202 protected void emit(Edge edge) { 361 protected void emit(Edge edge) {
203 362
204 Edge otherA = (Edge)openEdges.remove(edge.a); 363 Edge otherA = (Edge)openEdges.remove(edge.a);
205 if (otherA != null) { 364 if (otherA != null) {
206 otherA.chain(edge, edge.a); 365 otherA.chain(edge, edge.a);
222 openEdges.put(edge.b, edge); 381 openEdges.put(edge.b, edge);
223 } 382 }
224 } 383 }
225 } 384 }
226 385
386 /**
387 * Vectorize the raster. The found features are fed into
388 * the given ring handler.
389 * @param handler The RingHandler to postprocess the found features.
390 * @return The number of regions found.
391 */
227 public int process(RingsHandler handler) { 392 public int process(RingsHandler handler) {
228 393
229 BitSet visited = new BitSet(raster.length); 394 BitSet visited = new BitSet(raster.length);
230 395
231 TIntStack stack = new TIntStack(); 396 TIntStack stack = new TIntStack();

http://dive4elements.wald.intevation.org