Revision 16072 trunk/libraries/libTopology/src/org/gvsig/fmap/core/FGeometryUtil.java
FGeometryUtil.java | ||
---|---|---|
55 | 55 |
|
56 | 56 |
import org.gvsig.jts.JtsUtil; |
57 | 57 |
|
58 |
import com.iver.cit.gvsig.fmap.core.FArc2D; |
|
59 |
import com.iver.cit.gvsig.fmap.core.FCircle2D; |
|
60 |
import com.iver.cit.gvsig.fmap.core.FEllipse2D; |
|
58 | 61 |
import com.iver.cit.gvsig.fmap.core.FGeometry; |
59 | 62 |
import com.iver.cit.gvsig.fmap.core.FGeometryCollection; |
60 | 63 |
import com.iver.cit.gvsig.fmap.core.FMultiPoint2D; |
61 | 64 |
import com.iver.cit.gvsig.fmap.core.FMultipoint3D; |
62 | 65 |
import com.iver.cit.gvsig.fmap.core.FNullGeometry; |
66 |
import com.iver.cit.gvsig.fmap.core.FPoint2D; |
|
67 |
import com.iver.cit.gvsig.fmap.core.FPoint3D; |
|
68 |
import com.iver.cit.gvsig.fmap.core.FPolygon2D; |
|
69 |
import com.iver.cit.gvsig.fmap.core.FPolygon3D; |
|
70 |
import com.iver.cit.gvsig.fmap.core.FPolyline2D; |
|
71 |
import com.iver.cit.gvsig.fmap.core.FPolyline3D; |
|
63 | 72 |
import com.iver.cit.gvsig.fmap.core.FShape; |
73 |
import com.iver.cit.gvsig.fmap.core.FSpline2D; |
|
64 | 74 |
import com.iver.cit.gvsig.fmap.core.GeneralPathX; |
65 | 75 |
import com.iver.cit.gvsig.fmap.core.IGeometry; |
66 | 76 |
import com.iver.cit.gvsig.fmap.core.ShapeFactory; |
67 | 77 |
import com.iver.cit.gvsig.fmap.core.gt2.FLiteShape; |
78 |
import com.vividsolutions.jts.algorithm.CGAlgorithms; |
|
79 |
import com.vividsolutions.jts.algorithms.SnapCGAlgorithms; |
|
80 |
import com.vividsolutions.jts.geom.Coordinate; |
|
81 |
import com.vividsolutions.jts.geom.Envelope; |
|
68 | 82 |
import com.vividsolutions.jts.geom.Geometry; |
83 |
import com.vividsolutions.jts.geom.LinearRing; |
|
84 |
import com.vividsolutions.jts.geom.MultiPolygon; |
|
85 |
import com.vividsolutions.jts.geom.Polygon; |
|
69 | 86 |
|
70 | 87 |
/** |
71 | 88 |
* Utility methods to work with FMap geometries. |
... | ... | |
91 | 108 |
|
92 | 109 |
} |
93 | 110 |
|
111 |
public static boolean isClosed(Shape shape){ |
|
112 |
return isClosed(shape, 0d); |
|
113 |
} |
|
94 | 114 |
|
115 |
|
|
95 | 116 |
public static boolean snapEquals2D(Point2D a, Point2D b, double snapTolerance){ |
96 | 117 |
return a.distance(b) <= snapTolerance; |
97 | 118 |
} |
... | ... | |
195 | 216 |
return solution; |
196 | 217 |
} |
197 | 218 |
|
219 |
|
|
220 |
|
|
221 |
/** |
|
222 |
* Returns the dimension (0, 1, 2) of the specified |
|
223 |
* geometry type. |
|
224 |
* |
|
225 |
* @param shapeType |
|
226 |
* @return |
|
227 |
*/ |
|
228 |
public static int getDimensions(int shapeType){ |
|
229 |
switch(shapeType){ |
|
230 |
case FShape.ARC: |
|
231 |
case FShape.LINE: |
|
232 |
return 1; |
|
233 |
|
|
234 |
case FShape.CIRCLE: |
|
235 |
case FShape.ELLIPSE: |
|
236 |
case FShape.POLYGON: |
|
237 |
case FShape.MULTI: |
|
238 |
return 2; |
|
239 |
|
|
240 |
case FShape.MULTIPOINT: |
|
241 |
case FShape.POINT: |
|
242 |
case FShape.TEXT: |
|
243 |
return 0; |
|
244 |
default: |
|
245 |
return -1; |
|
246 |
} |
|
247 |
} |
|
248 |
|
|
249 |
|
|
250 |
/** |
|
251 |
* Returns the dimension (0, 1, 2, -1 for NULL geometries) |
|
252 |
* of the given geometry. |
|
253 |
* |
|
254 |
* @param shape |
|
255 |
* @return |
|
256 |
*/ |
|
257 |
|
|
258 |
public static int getDimensions(IGeometry geometry){ |
|
259 |
if(geometry instanceof FGeometry){ |
|
260 |
FGeometry fgeo = (FGeometry)geometry; |
|
261 |
FShape fshape = (FShape) fgeo.getInternalShape(); |
|
262 |
return getXyDimensions(fshape); |
|
263 |
|
|
264 |
}else if(geometry instanceof FGeometryCollection){ |
|
265 |
FGeometryCollection fgeo = (FGeometryCollection)geometry; |
|
266 |
IGeometry[] geoms = fgeo.getGeometries(); |
|
267 |
int dimension = -1; |
|
268 |
for(int i = 0; i < geoms.length; i++){ |
|
269 |
IGeometry igeo = geoms[i]; |
|
270 |
int igeoDimension = getDimensions(igeo); |
|
271 |
if(igeoDimension > dimension) |
|
272 |
dimension = igeoDimension; |
|
273 |
|
|
274 |
} |
|
275 |
return dimension; |
|
276 |
|
|
277 |
}else if(geometry instanceof FMultiPoint2D){ |
|
278 |
return 0; |
|
279 |
}else if(geometry instanceof FMultipoint3D){ |
|
280 |
return 0; |
|
281 |
}else if(geometry instanceof FNullGeometry){ |
|
282 |
return -1; |
|
283 |
}else{ |
|
284 |
return -1; |
|
285 |
} |
|
286 |
} |
|
287 |
|
|
288 |
/** |
|
289 |
* Returns the dimension (0, 1, 2, -1 for NULL geometries) |
|
290 |
* of the given shape. |
|
291 |
* |
|
292 |
* @param shape |
|
293 |
* @return |
|
294 |
*/ |
|
295 |
public static int getXyDimensions(FShape shape){ |
|
296 |
if(shape instanceof FArc2D){ |
|
297 |
return 1; |
|
298 |
}else if(shape instanceof FCircle2D){ |
|
299 |
return 2; |
|
300 |
}else if(shape instanceof FEllipse2D){ |
|
301 |
return 2; |
|
302 |
}else if(shape instanceof FPoint2D){ |
|
303 |
return 0; |
|
304 |
}else if(shape instanceof FPoint3D){ |
|
305 |
return 0; |
|
306 |
}else if(shape instanceof FPolygon2D){ |
|
307 |
return 2; |
|
308 |
}else if(shape instanceof FPolygon3D){ |
|
309 |
return 2; |
|
310 |
}else if(shape instanceof FPolyline2D){ |
|
311 |
return 1; |
|
312 |
}else if (shape instanceof FPolyline3D){ |
|
313 |
return 1; |
|
314 |
}else if(shape instanceof FSpline2D){ |
|
315 |
return 1; |
|
316 |
}else if(shape instanceof FLiteShape ){ |
|
317 |
FLiteShape liteShape = (FLiteShape)shape; |
|
318 |
Geometry jtsGeo = liteShape.getGeometry(); |
|
319 |
return jtsGeo.getDimension(); |
|
320 |
}else{ |
|
321 |
return -1; |
|
322 |
} |
|
323 |
} |
|
324 |
|
|
325 |
//TODO Solicitar que este metodo se ponga en FConverter sustituyendo |
|
326 |
//al viejo |
|
327 |
//TODO Movemos esto a JtsUtil en vez de en esta clase???? |
|
328 |
|
|
329 |
/* |
|
330 |
* Justificacion: libTopology constituye un sistema de control de calidad geometrica de los |
|
331 |
* datos. FConverter deber?a asumir que los datos le llegan adecuadamente, y si no es as?, |
|
332 |
* lanzar excepciones. |
|
333 |
* |
|
334 |
* La responsabilidad de detectar que un dato (FShape) no es apropiado para pasar a poligono |
|
335 |
* deberia tenerla libTopology (TopologyRule), y en caso de que as? se detecte, tratar el dato |
|
336 |
* de forma previa a llamar a FConverter. |
|
337 |
* |
|
338 |
* |
|
339 |
* */ |
|
340 |
|
|
341 |
public static MultiPolygon toJtsPolygon(FShape shp){ |
|
342 |
|
|
343 |
ArrayList<LinearRing> shells = new ArrayList<LinearRing>(); |
|
344 |
ArrayList<LinearRing> holes = new ArrayList<LinearRing>(); |
|
345 |
|
|
346 |
List<Point2D[]> shpPoints = ShapePointExtractor.extractPoints(shp); |
|
347 |
for(int i = 0; i < shpPoints.size(); i++){ |
|
348 |
Point2D[] partPoints = shpPoints.get(i); |
|
349 |
Coordinate[] coords = JtsUtil.getPoint2DAsCoordinates(partPoints); |
|
350 |
try { |
|
351 |
LinearRing ring = JtsUtil.geomFactory.createLinearRing(coords); |
|
352 |
|
|
353 |
//TODO REPORTAR ESTO EN FMAP, CREO QUE EST? MAL PORQUE LO HACE AL REV?S. |
|
354 |
//EL TEMA EST? EN QUE JTS HACE LAS COSAS AL REVES QUE EL FORMATO SHP |
|
355 |
if (CGAlgorithms.isCCW(coords)) { |
|
356 |
shells.add(ring); |
|
357 |
} else { |
|
358 |
holes.add(ring); |
|
359 |
} |
|
360 |
} catch (Exception e) { |
|
361 |
/* |
|
362 |
* Leer la cabecera del metodo: FConverter no deber?a hacer estos tratamientos |
|
363 |
boolean same = true; |
|
364 |
for (int j = 0; i < coords.length - 1 && same; j++) { |
|
365 |
if (coords[i].x != coords[i+1].x || coords[i].y != coords[i+1].y) |
|
366 |
same = false; |
|
367 |
} |
|
368 |
if (same) |
|
369 |
return JtsUtil.geomFactory.createPoint(coords[0]); |
|
370 |
*/ |
|
371 |
|
|
372 |
/* |
|
373 |
* caso cuando es una l?nea de 3 puntos, no creo un LinearRing, sino |
|
374 |
* una linea |
|
375 |
*/ |
|
376 |
/* |
|
377 |
if (coords.length > 1 && coords.length <= 3) |
|
378 |
// return geomFactory.createLineString(points); |
|
379 |
return JtsUtil.geomFactory.createMultiLineString(new LineString[] {JtsUtil.geomFactory.createLineString(coords)}); |
|
380 |
*/ |
|
381 |
System.err.println("Caught Topology exception in GMLLinearRingHandler"); |
|
382 |
return null; |
|
383 |
} |
|
384 |
}//for |
|
385 |
|
|
386 |
//At this point, we have a collection of shells and a collection of holes |
|
387 |
//Now we have to find for each shell its holes |
|
388 |
ArrayList<List<LinearRing>> holesForShells = new ArrayList<List<LinearRing>>(shells.size()); |
|
389 |
for (int i = 0; i < shells.size(); i++) { |
|
390 |
holesForShells.add(new ArrayList<LinearRing>()); |
|
391 |
} |
|
392 |
|
|
393 |
|
|
394 |
/* |
|
395 |
* Now, for each hole we look for the minimal shell that contains it. |
|
396 |
* We look for minimal because many shells could contain the same hole. |
|
397 |
* */ |
|
398 |
for (int i = 0; i < holes.size(); i++) {//for each hole |
|
399 |
LinearRing testHole = holes.get(i); |
|
400 |
LinearRing minShell = null; |
|
401 |
Envelope minEnv = null; |
|
402 |
Envelope testEnv = testHole.getEnvelopeInternal(); |
|
403 |
Coordinate testPt = testHole.getCoordinateN(0); |
|
404 |
LinearRing tryRing = null; |
|
405 |
|
|
406 |
for (int j = 0; j < shells.size(); j++) {//for each shell |
|
407 |
tryRing = (LinearRing) shells.get(j); |
|
408 |
Envelope tryEnv = tryRing.getEnvelopeInternal(); |
|
409 |
boolean isContained = false; |
|
410 |
Coordinate[] coordList = tryRing.getCoordinates(); |
|
411 |
|
|
412 |
//if testpoint is in ring, or test point is a shell point, is contained |
|
413 |
if (tryEnv.contains(testEnv) && |
|
414 |
(SnapCGAlgorithms.isPointInRing(testPt, coordList) || |
|
415 |
JtsUtil.pointInList(testPt, coordList))) { |
|
416 |
isContained = true; |
|
417 |
} |
|
418 |
|
|
419 |
// check if this new containing ring is smaller than the current minimum ring |
|
420 |
if (isContained) { |
|
421 |
if ((minShell == null) || minEnv.contains(tryEnv)) { |
|
422 |
minShell = tryRing; |
|
423 |
minEnv = minShell.getEnvelopeInternal(); |
|
424 |
} |
|
425 |
} |
|
426 |
}//for shells |
|
427 |
|
|
428 |
//At this point, minShell is the shell that contains a testHole |
|
429 |
//if minShell is null, we have a SHELL which points were digitized in the |
|
430 |
//wrong order |
|
431 |
|
|
432 |
if (minShell == null) { |
|
433 |
|
|
434 |
/* |
|
435 |
* TODO |
|
436 |
* Si las clases de FMap incluyesen en su semantica la diferencia |
|
437 |
* entre un shell y un hole, no deberiamos hacer esta suposicion. |
|
438 |
* |
|
439 |
* Pero como java.awt.geom.Shape no hace esta distincion, |
|
440 |
* tenemos que hacerla. |
|
441 |
* |
|
442 |
* |
|
443 |
* */ |
|
444 |
LinearRing reversed = JtsUtil.reverse(testHole); |
|
445 |
shells.add(reversed); |
|
446 |
holesForShells.add(new ArrayList<LinearRing>()); |
|
447 |
} else { |
|
448 |
((ArrayList<LinearRing>) holesForShells.get(shells.indexOf(minShell))).add(testHole); |
|
449 |
} |
|
450 |
}//for each hole |
|
451 |
|
|
452 |
Polygon[] polygons = new Polygon[shells.size()]; |
|
453 |
for (int i = 0; i < shells.size(); i++) { |
|
454 |
polygons[i] = JtsUtil.geomFactory.createPolygon((LinearRing) shells.get(i), |
|
455 |
(LinearRing[]) ((ArrayList<LinearRing>) holesForShells.get(i)).toArray(new LinearRing[0])); |
|
456 |
} |
|
457 |
return JtsUtil.geomFactory.createMultiPolygon(polygons); |
|
458 |
|
|
459 |
} |
|
460 |
|
|
461 |
public static Point2D[] getCoordinatesAsPoint2D(Coordinate[] coords){ |
|
462 |
Point2D[] solution = new Point2D[coords.length]; |
|
463 |
for(int i = 0; i < coords.length; i++){ |
|
464 |
solution[i] = new Point2D.Double(coords[i].x, coords[i].y); |
|
465 |
} |
|
466 |
return solution; |
|
467 |
} |
|
468 |
|
|
469 |
|
|
470 |
public static IGeometry createFPolygon(Point2D[] points) { |
|
471 |
GeneralPathX gpx = new GeneralPathX(); |
|
472 |
gpx.moveTo(points[0].getX(), points[0].getY()); |
|
473 |
for (int i = 1; i < points.length; i++) { |
|
474 |
gpx.lineTo(points[i].getX(), points[i].getY()); |
|
475 |
} |
|
476 |
if(! isClosed(gpx)){ |
|
477 |
gpx.closePath(); |
|
478 |
} |
|
479 |
return ShapeFactory.createPolygon2D(gpx); |
|
480 |
} |
|
481 |
|
|
198 | 482 |
} |
Also available in: Unified diff