root / trunk / libraries / libTopology / src / org / gvsig / jts / voronoi / chew / DTriangulationForJTS.java @ 21246
History | View | Annotate | Download (15.1 KB)
1 |
/***********************************************
|
---|---|
2 |
* created on 12.06.2006
|
3 |
* last modified:
|
4 |
*
|
5 |
* author: sstein
|
6 |
*
|
7 |
* description:
|
8 |
*
|
9 |
*
|
10 |
***********************************************/
|
11 |
package org.gvsig.jts.voronoi.chew; |
12 |
|
13 |
import java.util.ArrayList; |
14 |
import java.util.Collection; |
15 |
import java.util.Iterator; |
16 |
import java.util.List; |
17 |
import java.util.Set; |
18 |
|
19 |
import org.gvsig.jts.JtsUtil; |
20 |
import org.gvsig.topology.Messages; |
21 |
|
22 |
import com.iver.utiles.swing.threads.CancellableProgressTask; |
23 |
import com.vividsolutions.jts.geom.Coordinate; |
24 |
import com.vividsolutions.jts.geom.Envelope; |
25 |
import com.vividsolutions.jts.geom.Geometry; |
26 |
import com.vividsolutions.jts.geom.GeometryFactory; |
27 |
import com.vividsolutions.jts.geom.LineString; |
28 |
import com.vividsolutions.jts.geom.LinearRing; |
29 |
import com.vividsolutions.jts.geom.Point; |
30 |
import com.vividsolutions.jts.geom.Polygon; |
31 |
import com.vividsolutions.jts.operation.polygonize.Polygonizer; |
32 |
|
33 |
/**
|
34 |
* OpenJUMP's modified version.
|
35 |
*
|
36 |
* We have added generics to the input point's list, and passed
|
37 |
* CancellableProgressTask as parameter of those methods we want to monitor its
|
38 |
* progress.
|
39 |
*
|
40 |
*
|
41 |
*
|
42 |
* @author sstein
|
43 |
* @author azabala
|
44 |
*
|
45 |
* Use the class to access the delauney trinagulation by L. Paul Chew Methods of
|
46 |
* the class are modified versions from DelaunayPanel.java in DelaunayAp.java.
|
47 |
*/
|
48 |
public class DTriangulationForJTS { |
49 |
private DelaunayTriangulation dt; // The Delaunay triangulation |
50 |
private Simplex initialTriangle; // The large initial triangle |
51 |
private int initialSize = 10000; // Controls size of initial triangle |
52 |
private boolean isVoronoi; // True iff VoD instead of DT |
53 |
private double dx = 0; |
54 |
private double dy = 0; |
55 |
private Pnt lowerLeftPnt = null; |
56 |
public boolean debug = false; // True iff printing info for debugging |
57 |
|
58 |
public DTriangulationForJTS(List<Point> pointList) { |
59 |
double argmaxx = 0; |
60 |
double argmaxy = 0; |
61 |
double argminx = 0; |
62 |
double argminy = 0; |
63 |
int count = 0; |
64 |
// -- calc coordinates of initial symplex
|
65 |
for (Iterator iter = pointList.iterator(); iter.hasNext();) { |
66 |
Point pt = (Point) iter.next(); |
67 |
if (count == 0) { |
68 |
argmaxx = pt.getX(); |
69 |
argminx = pt.getX(); |
70 |
argmaxy = pt.getY(); |
71 |
argminy = pt.getY(); |
72 |
} else {
|
73 |
if (pt.getX() < argminx) {
|
74 |
argminx = pt.getX(); |
75 |
} |
76 |
if (pt.getX() > argmaxx) {
|
77 |
argmaxx = pt.getX(); |
78 |
} |
79 |
if (pt.getY() < argminy) {
|
80 |
argminy = pt.getY(); |
81 |
} |
82 |
if (pt.getY() > argmaxy) {
|
83 |
argmaxy = pt.getY(); |
84 |
} |
85 |
} |
86 |
count++; |
87 |
} |
88 |
this.dx = argmaxx - argminx;
|
89 |
this.dy = argmaxy - argminy;
|
90 |
// -- the initial simplex must contain all points
|
91 |
// -- take the bounding box, move the diagonals (sidewards)
|
92 |
// the meeting point will be the mirrored bbox-center on the top edge
|
93 |
this.initialTriangle = new Simplex(new Pnt[] { |
94 |
new Pnt(argminx - (1.5 * dx), argminy - dy), // lower left |
95 |
new Pnt(argmaxx + (1.5 * dx), argminy - dy), // lower right |
96 |
// new Pnt(argminx+(dx/2.0), argmaxy+(dy/2.0))}); //center, top
|
97 |
new Pnt(argminx + (dx / 2.0), argmaxy + 1.5 * dy) }); // center, |
98 |
// top
|
99 |
|
100 |
this.lowerLeftPnt = new Pnt(argminx - (1.5 * dx), argminy - dy); |
101 |
this.dt = new DelaunayTriangulation(initialTriangle); |
102 |
this.addPoints(pointList);
|
103 |
} |
104 |
|
105 |
/**
|
106 |
*
|
107 |
* @param pointList
|
108 |
* @param envelope
|
109 |
* the envelope my extend the initial point cloud and result in a
|
110 |
* larger initial simplex
|
111 |
*/
|
112 |
public DTriangulationForJTS(List<Point> pointList, Envelope envelope) { |
113 |
double argmaxx = 0; |
114 |
double argmaxy = 0; |
115 |
double argminx = 0; |
116 |
double argminy = 0; |
117 |
int count = 0; |
118 |
// -- calc coordinates of initial symplex
|
119 |
for (Iterator iter = pointList.iterator(); iter.hasNext();) { |
120 |
Point pt = (Point) iter.next(); |
121 |
if (count == 0) { |
122 |
argmaxx = pt.getX(); |
123 |
argminx = pt.getX(); |
124 |
argmaxy = pt.getY(); |
125 |
argminy = pt.getY(); |
126 |
} else {
|
127 |
if (pt.getX() < argminx) {
|
128 |
argminx = pt.getX(); |
129 |
} |
130 |
if (pt.getX() > argmaxx) {
|
131 |
argmaxx = pt.getX(); |
132 |
} |
133 |
if (pt.getY() < argminy) {
|
134 |
argminy = pt.getY(); |
135 |
} |
136 |
if (pt.getY() > argmaxy) {
|
137 |
argmaxy = pt.getY(); |
138 |
} |
139 |
} |
140 |
count++; |
141 |
} |
142 |
// -- do check also for the delivered envelope
|
143 |
if (envelope.getMinX() < argminx) {
|
144 |
argminx = envelope.getMinX(); |
145 |
} |
146 |
if (envelope.getMaxX() > argmaxx) {
|
147 |
argmaxx = envelope.getMaxX(); |
148 |
} |
149 |
if (envelope.getMinY() < argminy) {
|
150 |
argminy = envelope.getMinY(); |
151 |
} |
152 |
if (envelope.getMaxY() > argmaxy) {
|
153 |
argmaxy = envelope.getMaxY(); |
154 |
} |
155 |
// --
|
156 |
this.dx = argmaxx - argminx;
|
157 |
this.dy = argmaxy - argminy;
|
158 |
// -- the initial simplex must contain all points
|
159 |
// -- take the bounding box, move the diagonals (sidewards)
|
160 |
// the meeting point will be the mirrored bbox-center on the top edge
|
161 |
this.initialTriangle = new Simplex(new Pnt[] { |
162 |
new Pnt(argminx - (1.5 * dx), argminy - dy), // lower left |
163 |
new Pnt(argmaxx + (1.5 * dx), argminy - dy), // lower right |
164 |
// new Pnt(argminx+(dx/2.0), argmaxy+(dy/2.0))}); //center, top
|
165 |
new Pnt(argminx + (dx / 2.0), argmaxy + 1.5 * dy) }); // center, |
166 |
// top
|
167 |
|
168 |
this.lowerLeftPnt = new Pnt(argminx - (1.5 * dx), argminy - dy); |
169 |
this.dt = new DelaunayTriangulation(initialTriangle); |
170 |
this.addPoints(pointList);
|
171 |
} |
172 |
|
173 |
public void addPoints(List<Point> pointList) { |
174 |
for (Iterator iter = pointList.iterator(); iter.hasNext();) { |
175 |
try {
|
176 |
Geometry element = (Geometry) iter.next(); |
177 |
if (element instanceof Point) { |
178 |
Point jtsPt = (Point) element; |
179 |
Pnt point = new Pnt(jtsPt.getX(), jtsPt.getY());
|
180 |
dt.delaunayPlace(point); |
181 |
} else {
|
182 |
if (debug)
|
183 |
System.out.println("no point geometry"); |
184 |
} |
185 |
} catch (Exception e) { |
186 |
if (debug)
|
187 |
System.out.println("no geometry"); |
188 |
} |
189 |
} |
190 |
} |
191 |
|
192 |
public void addPoint(double x, double y) { |
193 |
Pnt point = new Pnt(x, y);
|
194 |
if (debug)
|
195 |
System.out.println("Click " + point); |
196 |
dt.delaunayPlace(point); |
197 |
} |
198 |
|
199 |
/**
|
200 |
* Draw all the Delaunay edges.
|
201 |
*
|
202 |
* @return Arraylist with LineString geometries.
|
203 |
*/
|
204 |
public List<Geometry> drawAllDelaunay() { |
205 |
// Loop through all the edges of the DT (each is done twice)
|
206 |
GeometryFactory gf = new GeometryFactory();
|
207 |
ArrayList<Geometry> lines = new ArrayList<Geometry>(); |
208 |
for (Iterator it = dt.iterator(); it.hasNext();) { |
209 |
Simplex triangle = (Simplex) it.next(); |
210 |
for (Iterator otherIt = triangle.facets().iterator(); otherIt |
211 |
.hasNext();) { |
212 |
Set facet = (Set) otherIt.next(); |
213 |
Pnt[] endpoint = (Pnt[]) facet.toArray(new Pnt[2]); |
214 |
Coordinate[] coords = new Coordinate[2]; |
215 |
coords[0] = new Coordinate(endpoint[0].coord(0), endpoint[0] |
216 |
.coord(1));
|
217 |
coords[1] = new Coordinate(endpoint[1].coord(0), endpoint[1] |
218 |
.coord(1));
|
219 |
LineString ls = gf.createLineString(coords); |
220 |
lines.add(ls); |
221 |
} |
222 |
} |
223 |
return lines;
|
224 |
} |
225 |
|
226 |
public List<Geometry> getTinTriangles( |
227 |
CancellableProgressTask progressMonitor) { |
228 |
ArrayList<Geometry> triangles = new ArrayList<Geometry>(); |
229 |
|
230 |
if (progressMonitor != null) { |
231 |
progressMonitor.setInitialStep(0);
|
232 |
int numOfSteps = dt.size();
|
233 |
progressMonitor.setFinalStep(numOfSteps); |
234 |
progressMonitor.setDeterminatedProcess(true);
|
235 |
progressMonitor |
236 |
.setNote(Messages.getText("computing_tin_triangles"));
|
237 |
progressMonitor.setStatusMessage(Messages |
238 |
.getText("voronoi_diagram_layer_message"));
|
239 |
} |
240 |
|
241 |
for (Iterator it = dt.iterator(); it.hasNext();) { |
242 |
Simplex triangle = (Simplex) it.next(); |
243 |
|
244 |
if (triangle.size() == 3) { |
245 |
List<Coordinate> coords = new ArrayList<Coordinate>(); |
246 |
Iterator itPt = triangle.iterator();
|
247 |
while (itPt.hasNext()) {
|
248 |
Pnt p = (Pnt) itPt.next(); |
249 |
coords.add(new Coordinate(p.coord(0), p.coord(1))); |
250 |
} |
251 |
coords.add(coords.get(0));
|
252 |
Coordinate[] coordsArray = new Coordinate[coords.size()]; |
253 |
coords.toArray(coordsArray); |
254 |
Polygon polygon = JtsUtil.createPolygon(coordsArray,
|
255 |
new int[] { 0 }); |
256 |
triangles.add(polygon); |
257 |
|
258 |
if (progressMonitor != null) |
259 |
progressMonitor.reportStep(); |
260 |
} |
261 |
} |
262 |
return triangles;
|
263 |
} |
264 |
|
265 |
/**
|
266 |
* Draw all the Voronoi edges.
|
267 |
*
|
268 |
* @return Arraylist with LineString geometries.
|
269 |
*/
|
270 |
public List<LineString> drawAllVoronoi() { |
271 |
GeometryFactory gf = new GeometryFactory();
|
272 |
ArrayList<LineString> lines = new ArrayList<LineString>(); |
273 |
// Loop through all the edges of the DT (each is done twice)
|
274 |
for (Iterator it = dt.iterator(); it.hasNext();) { |
275 |
Simplex triangle = (Simplex) it.next(); |
276 |
for (Iterator otherIt = dt.neighbors(triangle).iterator(); otherIt |
277 |
.hasNext();) { |
278 |
Simplex other = (Simplex) otherIt.next(); |
279 |
Pnt p = Pnt.circumcenter((Pnt[]) triangle.toArray(new Pnt[0])); |
280 |
Pnt q = Pnt.circumcenter((Pnt[]) other.toArray(new Pnt[0])); |
281 |
Coordinate[] coords = new Coordinate[2]; |
282 |
coords[0] = new Coordinate(p.coord(0), p.coord(1)); |
283 |
coords[1] = new Coordinate(q.coord(0), q.coord(1)); |
284 |
LineString ls = gf.createLineString(coords); |
285 |
lines.add(ls); |
286 |
} |
287 |
} |
288 |
return lines;
|
289 |
} |
290 |
|
291 |
/**
|
292 |
* Draw all the sites (i.e., the input points) of the DT.
|
293 |
*
|
294 |
* @return Arraylist with point geometries.
|
295 |
*/
|
296 |
public ArrayList drawAllSites() { |
297 |
// Loop through all sites of the DT
|
298 |
// Each is done several times, about 6 times each on average; this
|
299 |
// can be proved via Euler's formula.
|
300 |
GeometryFactory gf = new GeometryFactory();
|
301 |
ArrayList points = new ArrayList(); |
302 |
for (Iterator it = dt.iterator(); it.hasNext();) { |
303 |
for (Iterator otherIt = ((Simplex) it.next()).iterator(); otherIt |
304 |
.hasNext();) { |
305 |
Pnt pt = (Pnt) otherIt.next(); |
306 |
Coordinate coord = new Coordinate(pt.coord(0), pt.coord(1)); |
307 |
Point jtsPt = gf.createPoint(coord);
|
308 |
points.add(jtsPt); |
309 |
} |
310 |
} |
311 |
return points;
|
312 |
} |
313 |
|
314 |
/**
|
315 |
* Draw all the empty circles (one for each triangle) of the DT.
|
316 |
*
|
317 |
* @return Arraylist with polygon geometries.
|
318 |
*/
|
319 |
public ArrayList drawAllCircles() { |
320 |
// Loop through all triangles of the DT
|
321 |
GeometryFactory gf = new GeometryFactory();
|
322 |
ArrayList circles = new ArrayList(); |
323 |
loop: for (Iterator it = dt.iterator(); it.hasNext();) { |
324 |
Simplex triangle = (Simplex) it.next(); |
325 |
for (Iterator otherIt = initialTriangle.iterator(); otherIt |
326 |
.hasNext();) { |
327 |
Pnt p = (Pnt) otherIt.next(); |
328 |
if (triangle.contains(p))
|
329 |
continue loop;
|
330 |
} |
331 |
Pnt c = Pnt.circumcenter((Pnt[]) triangle.toArray(new Pnt[0])); |
332 |
double radius = c.subtract((Pnt) triangle.iterator().next())
|
333 |
.magnitude(); |
334 |
Coordinate coord = new Coordinate(c.coord(0), c.coord(1)); |
335 |
Point jtsPt = gf.createPoint(coord);
|
336 |
circles.add(jtsPt.buffer(radius)); |
337 |
} |
338 |
return circles;
|
339 |
} |
340 |
|
341 |
public DelaunayTriangulation getDelaunayTriangulation() {
|
342 |
return dt;
|
343 |
} |
344 |
|
345 |
/**
|
346 |
*
|
347 |
* @return the corner points of the initial simplex which is divided into
|
348 |
* smaller simplexes by the iterative insertion of the point dataset
|
349 |
*/
|
350 |
public ArrayList getInitialSimmplexAsJTSPoints() { |
351 |
GeometryFactory gf = new GeometryFactory();
|
352 |
ArrayList points = new ArrayList(); |
353 |
|
354 |
for (Iterator otherIt = this.initialTriangle.iterator(); otherIt |
355 |
.hasNext();) { |
356 |
Pnt pt = (Pnt) otherIt.next(); |
357 |
Coordinate coord = new Coordinate(pt.coord(0), pt.coord(1)); |
358 |
Point jtsPt = gf.createPoint(coord);
|
359 |
points.add(jtsPt); |
360 |
} |
361 |
return points;
|
362 |
} |
363 |
|
364 |
/**
|
365 |
* the size of the box has been empirically defined to get "undistorted"
|
366 |
* outer thiessen polygons
|
367 |
*
|
368 |
* @return a bounding box necesseray to create the final thiessenpolygons
|
369 |
*/
|
370 |
public Geometry getThiessenBoundingBox() {
|
371 |
GeometryFactory gf = new GeometryFactory();
|
372 |
Coordinate[] coords = new Coordinate[5]; |
373 |
coords[0] = new Coordinate(this.lowerLeftPnt.coord(0) + 1 * this.dx, |
374 |
this.lowerLeftPnt.coord(1) + 0.5 * this.dy); // lowerleft |
375 |
coords[1] = new Coordinate(this.lowerLeftPnt.coord(0) + 3 * this.dx, |
376 |
this.lowerLeftPnt.coord(1) + 0.5 * this.dy); // lowerright |
377 |
coords[2] = new Coordinate(this.lowerLeftPnt.coord(0) + 3 * this.dx, |
378 |
this.lowerLeftPnt.coord(1) + 2.5 * this.dy); // topright |
379 |
coords[3] = new Coordinate(this.lowerLeftPnt.coord(0) + 1 * this.dx, |
380 |
this.lowerLeftPnt.coord(1) + 2.5 * this.dy); // topleft |
381 |
// -- to close linestring
|
382 |
coords[4] = new Coordinate(this.lowerLeftPnt.coord(0) + 1 * this.dx, |
383 |
this.lowerLeftPnt.coord(1) + 0.5 * dy); // lowerleft |
384 |
LinearRing lr = gf.createLinearRing(coords); |
385 |
Geometry bbox = gf.createPolygon(lr, null);
|
386 |
return bbox;
|
387 |
} |
388 |
|
389 |
public List<Geometry> getThiessenPolys() { |
390 |
return getThiessenPolys(null); |
391 |
} |
392 |
|
393 |
/**
|
394 |
* Method returns thiessen polygons within a empirically enlarged bounding
|
395 |
* box around the point set. Therefore the voronoi edges are polygonized and
|
396 |
* the intersecting voronoi polygons with the bounding box are calculated.
|
397 |
* These intersecting thiessen polygons (in the bounding box) are returned.
|
398 |
* <p>
|
399 |
* Note: "thiesen" and "voronoi" is exchangeable.
|
400 |
*
|
401 |
* @return
|
402 |
*/
|
403 |
public List<Geometry> getThiessenPolys( |
404 |
CancellableProgressTask progressMonitor) { |
405 |
// -- do union of all edges and use the polygonizer to create polygons
|
406 |
// from it
|
407 |
if (debug)
|
408 |
System.out.println("get voronoi egdes"); |
409 |
List<LineString> edges = this.drawAllVoronoi(); |
410 |
|
411 |
if (debug)
|
412 |
System.out.println("merge voronoi egdes to multiLineString"); |
413 |
|
414 |
Geometry lines = null;
|
415 |
|
416 |
Geometry mls = (Geometry) edges.get(0);
|
417 |
|
418 |
if (progressMonitor != null) { |
419 |
progressMonitor.setInitialStep(0);
|
420 |
int numOfSteps = edges.size();
|
421 |
progressMonitor.setFinalStep(numOfSteps); |
422 |
progressMonitor.setDeterminatedProcess(true);
|
423 |
progressMonitor.setNote(Messages |
424 |
.getText("topology_clean_of_thiessen_edges"));
|
425 |
progressMonitor.setStatusMessage(Messages |
426 |
.getText("voronoi_diagram_layer_message"));
|
427 |
progressMonitor.reportStep(); |
428 |
} |
429 |
|
430 |
for (int i = 1; i < edges.size(); i++) { |
431 |
Geometry line = (Geometry) edges.get(i); |
432 |
mls = mls.union(line); |
433 |
if (progressMonitor != null) |
434 |
progressMonitor.reportStep(); |
435 |
} |
436 |
|
437 |
lines = mls; |
438 |
|
439 |
// Geometry[] geometries = new Geometry[edges.size()];
|
440 |
// edges.toArray(geometries);
|
441 |
// lines =
|
442 |
// JtsUtil.GEOMETRY_FACTORY.createGeometryCollection(geometries);
|
443 |
// if(lines.getClass().equals(GeometryCollection.class)){
|
444 |
// GeometryCollection gc = (GeometryCollection) lines;
|
445 |
// lines = JtsUtil.convertToMultiLineString(gc);
|
446 |
// }
|
447 |
|
448 |
if (debug)
|
449 |
System.out.println("polygonize"); |
450 |
Polygonizer poly = new Polygonizer();
|
451 |
poly.add(lines); |
452 |
Collection polys = poly.getPolygons();
|
453 |
// -- get final polygons in bounding box (=intersection polygons with
|
454 |
// the bbox)
|
455 |
Geometry bbox = this.getThiessenBoundingBox();
|
456 |
if (debug)
|
457 |
System.out.println("get intersections and final polys.."); |
458 |
|
459 |
if (progressMonitor != null) { |
460 |
progressMonitor.setInitialStep(0);
|
461 |
int numOfSteps = polys.size();
|
462 |
progressMonitor.setFinalStep(numOfSteps); |
463 |
progressMonitor.setDeterminatedProcess(true);
|
464 |
progressMonitor.setNote(Messages |
465 |
.getText("computing_thiessen_polygons"));
|
466 |
progressMonitor.setStatusMessage(Messages |
467 |
.getText("voronoi_diagram_layer_message"));
|
468 |
} |
469 |
|
470 |
ArrayList<Geometry> finalPolys = new ArrayList<Geometry>(); |
471 |
for (Iterator iter = polys.iterator(); iter.hasNext();) { |
472 |
Geometry candPoly = (Geometry) iter.next(); |
473 |
Geometry intersection = bbox.intersection(candPoly); |
474 |
if (progressMonitor != null) |
475 |
progressMonitor.reportStep(); |
476 |
if (intersection != null) { |
477 |
if (intersection.getArea() > 0) { |
478 |
finalPolys.add(intersection); |
479 |
} |
480 |
} |
481 |
} |
482 |
if (progressMonitor != null) |
483 |
progressMonitor.reportStep(); |
484 |
return finalPolys;
|
485 |
} |
486 |
|
487 |
} |