Revision 19993

View differences:

trunk/libraries/libTopology/src/org/gvsig/fmap/core/FGeometryUtil.java
42 42
 *   dac@iver.es
43 43
 */
44 44
/* CVS MESSAGES:
45
*
46
* $Id: 
47
* $Log: 
48
*/
45
 *
46
 * $Id: 
47
 * $Log: 
48
 */
49 49
package org.gvsig.fmap.core;
50 50

  
51 51
import java.awt.Shape;
52 52
import java.awt.geom.Point2D;
53 53
import java.awt.geom.Rectangle2D;
54 54
import java.util.ArrayList;
55
import java.util.Iterator;
55 56
import java.util.List;
56 57

  
57 58
import org.gvsig.jts.JtsUtil;
......
84 85
import com.vividsolutions.jts.geom.LinearRing;
85 86
import com.vividsolutions.jts.geom.MultiPolygon;
86 87
import com.vividsolutions.jts.geom.Polygon;
88
import com.vividsolutions.jts.util.GeometricShapeFactory;
87 89

  
88 90
/**
89 91
 * Utility methods to work with FMap geometries.
90 92
 * 
91 93
 * @author Alvaro Zabala
92
 *
94
 * 
93 95
 */
94 96
public class FGeometryUtil {
95 97

  
96
	
97
	
98
	public static boolean isClosed(Shape shape, double snapTolerance){
98
	public static boolean isClosed(Shape shape, double snapTolerance) {
99 99
		List<Point2D[]> partsCoords = ShapePointExtractor.extractPoints(shape);
100 100
		return isClosed(partsCoords, snapTolerance);
101
		
101

  
102 102
	}
103
	
104
	public static boolean isClosed(List<Point2D[]> partsCoords, double snapTolerance){
105
		//each member of the list is a part 
106
		for(int i = 0; i < partsCoords.size(); i++){
103

  
104
	public static boolean isClosed(List<Point2D[]> partsCoords,
105
			double snapTolerance) {
106
		// each member of the list is a part
107
		for (int i = 0; i < partsCoords.size(); i++) {
107 108
			Point2D[] part = partsCoords.get(i);
108
			if(! isClosed(part, snapTolerance)){
109
			if (!isClosed(part, snapTolerance)) {
109 110
				return false;
110 111
			}
111
		}//for
112
		}// for
112 113
		return true;
113 114
	}
114
	
115
	public static boolean isClosed(Point2D[] coords, double snapTolerance){
115

  
116
	public static boolean isClosed(Point2D[] coords, double snapTolerance) {
116 117
		Point2D start = coords[0];
117
		Point2D end = coords[ coords.length - 1];
118
		Point2D end = coords[coords.length - 1];
118 119
		return snapEquals2D(start, end, snapTolerance);
119 120
	}
120
	
121
	public static boolean isClosed(Shape shape){
121

  
122
	public static boolean isClosed(Shape shape) {
122 123
		return isClosed(shape, 0d);
123 124
	}
124
	
125
	
126
	public static boolean snapEquals2D(Point2D a, Point2D b, double snapTolerance){
125

  
126
	public static boolean snapEquals2D(Point2D a, Point2D b,
127
			double snapTolerance) {
127 128
		return a.distance(b) <= snapTolerance;
128 129
	}
129
	
130
	
131
	
132
	public static boolean isClosed(IGeometry geometry, double snapTolerance){
133
		
134
		//TODO Remove all of this if-then and create a method isClosed
135
		//for IGeometry
136
		if(geometry instanceof FGeometry){
137
			FGeometry fgeo = (FGeometry)geometry;
130

  
131
	public static boolean isClosed(IGeometry geometry, double snapTolerance) {
132

  
133
		// TODO Remove all of this if-then and create a method isClosed
134
		// for IGeometry
135
		if (geometry instanceof FGeometry) {
136
			FGeometry fgeo = (FGeometry) geometry;
138 137
			FShape fshape = (FShape) fgeo.getInternalShape();
139 138
			return isClosed(fshape, snapTolerance);
140
			
141
		}else if(geometry instanceof FGeometryCollection){
142
			FGeometryCollection fgeo = (FGeometryCollection)geometry;
139

  
140
		} else if (geometry instanceof FGeometryCollection) {
141
			FGeometryCollection fgeo = (FGeometryCollection) geometry;
143 142
			IGeometry[] geoms = fgeo.getGeometries();
144
			for(int i = 0; i < geoms.length; i++){
143
			for (int i = 0; i < geoms.length; i++) {
145 144
				IGeometry igeo = geoms[i];
146
				if(! isClosed(igeo, snapTolerance))
145
				if (!isClosed(igeo, snapTolerance))
147 146
					return false;
148 147
			}
149 148
			return true;
150
			
151
		}else if(geometry instanceof FMultiPoint2D){
149

  
150
		} else if (geometry instanceof FMultiPoint2D) {
152 151
			return false;
153
		}else if(geometry instanceof FMultipoint3D){
152
		} else if (geometry instanceof FMultipoint3D) {
154 153
			return false;
155
		}else if(geometry instanceof FNullGeometry){
154
		} else if (geometry instanceof FNullGeometry) {
156 155
			return false;
157
		}else if(geometry instanceof FLiteShape){
158
			FLiteShape fgeo = (FLiteShape)geometry;
156
		} else if (geometry instanceof FLiteShape) {
157
			FLiteShape fgeo = (FLiteShape) geometry;
159 158
			Geometry jtsGeo = fgeo.getGeometry();
160 159
			return JtsUtil.isClosed(jtsGeo, snapTolerance);
161 160
		}
162 161
		return false;
163 162
	}
164
	
165
	public static boolean isClosed(IGeometry geometry){
163

  
164
	public static boolean isClosed(IGeometry geometry) {
166 165
		return isClosed(geometry, 0d);
167 166
	}
168
	
169
	public static IGeometry getGeometryToClose(IGeometry unclosedGeometry, double snapTolerance){
170
		if(unclosedGeometry instanceof FGeometry){
171
			FGeometry fgeo = (FGeometry)unclosedGeometry;
167

  
168
	public static IGeometry getGeometryToClose(IGeometry unclosedGeometry,
169
			double snapTolerance) {
170
		if (unclosedGeometry instanceof FGeometry) {
171
			FGeometry fgeo = (FGeometry) unclosedGeometry;
172 172
			FShape fshape = (FShape) fgeo.getInternalShape();
173
			return ShapeFactory.createPolyline2D((GeneralPathX) getShapeToClose(fshape, snapTolerance));
174
			
175
		}else if(unclosedGeometry instanceof FGeometryCollection){
173
			return ShapeFactory
174
					.createPolyline2D((GeneralPathX) getShapeToClose(fshape,
175
							snapTolerance));
176

  
177
		} else if (unclosedGeometry instanceof FGeometryCollection) {
176 178
			List<IGeometry> geometries = new ArrayList<IGeometry>();
177
			FGeometryCollection fgeo = (FGeometryCollection)unclosedGeometry;
179
			FGeometryCollection fgeo = (FGeometryCollection) unclosedGeometry;
178 180
			IGeometry[] geoms = fgeo.getGeometries();
179
			for(int i = 0; i < geoms.length; i++){
181
			for (int i = 0; i < geoms.length; i++) {
180 182
				IGeometry igeo = geoms[i];
181 183
				IGeometry geomToClose = getGeometryToClose(igeo, snapTolerance);
182 184
				geometries.add(geomToClose);
......
185 187
			geometries.toArray(igeoms);
186 188
			FGeometryCollection solution = new FGeometryCollection(igeoms);
187 189
			return solution;
188
			
189
		}else if((unclosedGeometry instanceof FMultiPoint2D)||
190
			(unclosedGeometry instanceof FMultipoint3D)||
191
			(unclosedGeometry instanceof FNullGeometry)){
190

  
191
		} else if ((unclosedGeometry instanceof FMultiPoint2D)
192
				|| (unclosedGeometry instanceof FMultipoint3D)
193
				|| (unclosedGeometry instanceof FNullGeometry)) {
192 194
			return null;
193
		}else if(unclosedGeometry instanceof FLiteShape){
194
			FLiteShape fgeo = (FLiteShape)unclosedGeometry;
195
		} else if (unclosedGeometry instanceof FLiteShape) {
196
			FLiteShape fgeo = (FLiteShape) unclosedGeometry;
195 197
			Geometry jtsGeo = fgeo.getGeometry();
196
			Geometry jtsGeo2close = JtsUtil.getGeometryToClose(jtsGeo, snapTolerance);
198
			Geometry jtsGeo2close = JtsUtil.getGeometryToClose(jtsGeo,
199
					snapTolerance);
197 200
			return ShapeFactory.createGeometry(new FLiteShape(jtsGeo2close));
198 201
		}
199 202
		return null;
200 203
	}
201
	
204

  
202 205
	/**
203
	 * Receives an unclosed shape (or at least a multipart shape which
204
	 * has an unclosed part) and returns the missed geometry to get
205
	 * a closed shape
206
	 * Receives an unclosed shape (or at least a multipart shape which has an
207
	 * unclosed part) and returns the missed geometry to get a closed shape
206 208
	 * 
207 209
	 * @param unclosedShape
208 210
	 * @param snapTolerance
209 211
	 * @return
210 212
	 */
211
	public static Shape getShapeToClose(Shape unclosedShape, double snapTolerance){
213
	public static Shape getShapeToClose(Shape unclosedShape,
214
			double snapTolerance) {
212 215
		GeneralPathX solution = new GeneralPathX();
213
		
214
		List<Point2D[]> partsCoords = ShapePointExtractor.
215
									extractPoints(unclosedShape);
216
		for(int i = 0; i < partsCoords.size(); i++){
216

  
217
		List<Point2D[]> partsCoords = ShapePointExtractor
218
				.extractPoints(unclosedShape);
219
		for (int i = 0; i < partsCoords.size(); i++) {
217 220
			Point2D[] part = partsCoords.get(i);
218 221
			Point2D start = part[0];
219
			Point2D end = part[ part.length - 1];
220
			if(! snapEquals2D(start, end, snapTolerance))
221
			{
222
			Point2D end = part[part.length - 1];
223
			if (!snapEquals2D(start, end, snapTolerance)) {
222 224
				solution.moveTo(start.getX(), start.getY());
223 225
				solution.lineTo(end.getX(), end.getY());
224 226
			}
225
		}//for
227
		}// for
226 228
		return solution;
227 229
	}
228
	
229
	
230
	
230

  
231 231
	/**
232
	 * Returns the dimension (0, 1, 2) of the specified
233
	 * geometry type.
232
	 * Returns the dimension (0, 1, 2) of the specified geometry type.
234 233
	 * 
235 234
	 * @param shapeType
236 235
	 * @return
237 236
	 */
238
	public static int getDimensions(int shapeType){
239
		switch(shapeType){
237
	public static int getDimensions(int shapeType) {
238
		switch (shapeType) {
240 239
		case FShape.ARC:
241 240
		case FShape.LINE:
242 241
			return 1;
243
			
242

  
244 243
		case FShape.CIRCLE:
245 244
		case FShape.ELLIPSE:
246 245
		case FShape.POLYGON:
247 246
		case FShape.MULTI:
248 247
			return 2;
249
			
248

  
250 249
		case FShape.MULTIPOINT:
251 250
		case FShape.POINT:
252 251
		case FShape.TEXT:
......
255 254
			return -1;
256 255
		}
257 256
	}
258
	
259
	
257

  
260 258
	/**
261
	 * Returns the dimension (0, 1, 2, -1 for NULL geometries)
262
	 * of the given geometry.
259
	 * Returns the dimension (0, 1, 2, -1 for NULL geometries) of the given
260
	 * geometry.
263 261
	 * 
264 262
	 * @param shape
265 263
	 * @return
266 264
	 */
267
	
268
	public static int getDimensions(IGeometry geometry){
269
		if(geometry instanceof FGeometry){
270
			FGeometry fgeo = (FGeometry)geometry;
265

  
266
	public static int getDimensions(IGeometry geometry) {
267
		if (geometry instanceof FGeometry) {
268
			FGeometry fgeo = (FGeometry) geometry;
271 269
			FShape fshape = (FShape) fgeo.getInternalShape();
272 270
			return getXyDimensions(fshape);
273
			
274
		}else if(geometry instanceof FGeometryCollection){
275
			FGeometryCollection fgeo = (FGeometryCollection)geometry;
271

  
272
		} else if (geometry instanceof FGeometryCollection) {
273
			FGeometryCollection fgeo = (FGeometryCollection) geometry;
276 274
			IGeometry[] geoms = fgeo.getGeometries();
277 275
			int dimension = -1;
278
			for(int i = 0; i < geoms.length; i++){
276
			for (int i = 0; i < geoms.length; i++) {
279 277
				IGeometry igeo = geoms[i];
280 278
				int igeoDimension = getDimensions(igeo);
281
				if(igeoDimension > dimension)
279
				if (igeoDimension > dimension)
282 280
					dimension = igeoDimension;
283
				
281

  
284 282
			}
285 283
			return dimension;
286
			
287
		}else if(geometry instanceof FMultiPoint2D){
284

  
285
		} else if (geometry instanceof FMultiPoint2D) {
288 286
			return 0;
289
		}else if(geometry instanceof FMultipoint3D){
287
		} else if (geometry instanceof FMultipoint3D) {
290 288
			return 0;
291
		}else if(geometry instanceof FNullGeometry){
289
		} else if (geometry instanceof FNullGeometry) {
292 290
			return -1;
293
		}else{
291
		} else {
294 292
			return -1;
295 293
		}
296 294
	}
297
	
295

  
298 296
	/**
299
	 * Returns the dimension (0, 1, 2, -1 for NULL geometries)
300
	 * of the given shape.
297
	 * Returns the dimension (0, 1, 2, -1 for NULL geometries) of the given
298
	 * shape.
301 299
	 * 
302 300
	 * @param shape
303 301
	 * @return
304 302
	 */
305
	public static int getXyDimensions(FShape shape){
306
		if(shape instanceof FArc2D){
303
	public static int getXyDimensions(FShape shape) {
304
		if (shape instanceof FArc2D) {
307 305
			return 1;
308
		}else if(shape instanceof FCircle2D){
306
		} else if (shape instanceof FCircle2D) {
309 307
			return 2;
310
		}else if(shape instanceof FEllipse2D){
308
		} else if (shape instanceof FEllipse2D) {
311 309
			return 2;
312
		}else if(shape instanceof FPoint2D){
310
		} else if (shape instanceof FPoint2D) {
313 311
			return 0;
314
		}else if(shape instanceof FPoint3D){
312
		} else if (shape instanceof FPoint3D) {
315 313
			return 0;
316
		}else if(shape instanceof FPolygon2D){
314
		} else if (shape instanceof FPolygon2D) {
317 315
			return 2;
318
		}else if(shape instanceof FPolygon3D){
316
		} else if (shape instanceof FPolygon3D) {
319 317
			return 2;
320
		}else if(shape instanceof FPolyline2D){
318
		} else if (shape instanceof FPolyline2D) {
321 319
			return 1;
322
		}else if (shape instanceof FPolyline3D){
320
		} else if (shape instanceof FPolyline3D) {
323 321
			return 1;
324
		}else if(shape instanceof FSpline2D){
322
		} else if (shape instanceof FSpline2D) {
325 323
			return 1;
326
		}else if(shape instanceof FLiteShape ){
327
			FLiteShape liteShape = (FLiteShape)shape;
324
		} else if (shape instanceof FLiteShape) {
325
			FLiteShape liteShape = (FLiteShape) shape;
328 326
			Geometry jtsGeo = liteShape.getGeometry();
329 327
			return jtsGeo.getDimension();
330
		}else{
328
		} else {
331 329
			return -1;
332 330
		}
333 331
	}
334
	
335
	//TODO Solicitar que este metodo se ponga en FConverter sustituyendo
336
	//al viejo
337
	//TODO Movemos esto a JtsUtil en vez de en esta clase????
338
	
332

  
333
	// TODO Solicitar que este metodo se ponga en FConverter sustituyendo
334
	// al viejo
335
	// TODO Movemos esto a JtsUtil en vez de en esta clase????
336

  
339 337
	/*
340
	 * Justificacion: libTopology constituye un sistema de control de calidad geometrica de los
341
	 * datos. FConverter deber?a asumir que los datos le llegan adecuadamente, y si no es as?,
342
	 * lanzar excepciones.
338
	 * Justificacion: libTopology constituye un sistema de control de calidad
339
	 * geometrica de los datos. FConverter deber?a asumir que los datos le
340
	 * llegan adecuadamente, y si no es as?, lanzar excepciones.
343 341
	 * 
344
	 * La responsabilidad de detectar que un dato (FShape) no es apropiado para pasar a poligono
345
	 * deberia tenerla libTopology (TopologyRule), y en caso de que as? se detecte, tratar el dato
346
	 * de forma previa a llamar a FConverter.
342
	 * La responsabilidad de detectar que un dato (FShape) no es apropiado para
343
	 * pasar a poligono deberia tenerla libTopology (TopologyRule), y en caso de
344
	 * que as? se detecte, tratar el dato de forma previa a llamar a FConverter.
347 345
	 * 
346
	 * Por este motivo en este metodo se eliminan las correcciones que si hace
347
	 * FConverter
348 348
	 * 
349
	 * */
350
	
351
	public static MultiPolygon toJtsPolygon(FShape shp){
352
		
349
	 * 
350
	 */
351

  
352
	public static MultiPolygon toJtsPolygon(FShape shp) {
353

  
353 354
		ArrayList<LinearRing> shells = new ArrayList<LinearRing>();
354 355
		ArrayList<LinearRing> holes = new ArrayList<LinearRing>();
355
	    
356

  
356 357
		List<Point2D[]> shpPoints = ShapePointExtractor.extractPoints(shp);
357
		for(int i = 0; i < shpPoints.size(); i++){
358
		for (int i = 0; i < shpPoints.size(); i++) {
358 359
			Point2D[] partPoints = shpPoints.get(i);
359 360
			Coordinate[] coords = JtsUtil.getPoint2DAsCoordinates(partPoints);
360 361
			try {
361
				LinearRing ring = JtsUtil.GEOMETRY_FACTORY.createLinearRing(coords);
362
				
363
				//TODO REPORTAR ESTO EN FMAP, CREO QUE EST? MAL PORQUE LO HACE AL REV?S.
364
				//EL TEMA EST? EN QUE JTS HACE LAS COSAS AL REVES QUE EL FORMATO SHP
362
				LinearRing ring = JtsUtil.GEOMETRY_FACTORY
363
						.createLinearRing(coords);
364

  
365
				// TODO REPORTAR ESTO EN FMAP, CREO QUE EST? MAL PORQUE LO HACE
366
				// AL REV?S.
367
				// EL TEMA EST? EN QUE JTS HACE LAS COSAS AL REVES QUE EL
368
				// FORMATO SHP
365 369
				if (CGAlgorithms.isCCW(coords)) {
366 370
					shells.add(ring);
367 371
				} else {
......
369 373
				}
370 374
			} catch (Exception e) {
371 375
				/*
372
				 * Leer la cabecera del metodo: FConverter no deber?a hacer estos tratamientos
373
				boolean same = true;
374
				for (int j = 0; i < coords.length - 1 && same; j++) {
375
					if (coords[i].x != coords[i+1].x || coords[i].y != coords[i+1].y) 
376
						same = false;
377
				}
378
				if (same)
379
					return JtsUtil.geomFactory.createPoint(coords[0]);
380
				*/
381
				
376
				 * Leer la cabecera del metodo: FConverter no deber?a hacer
377
				 * estos tratamientos boolean same = true; for (int j = 0; i <
378
				 * coords.length - 1 && same; j++) { if (coords[i].x !=
379
				 * coords[i+1].x || coords[i].y != coords[i+1].y) same = false; }
380
				 * if (same) return JtsUtil.geomFactory.createPoint(coords[0]);
381
				 */
382

  
382 383
				/*
383
				 * caso cuando es una l?nea de 3 puntos, no creo un LinearRing, sino
384
				 * una linea
384
				 * caso cuando es una l?nea de 3 puntos, no creo un LinearRing,
385
				 * sino una linea
385 386
				 */
386 387
				/*
387
				if (coords.length > 1 && coords.length <= 3)
388
					// return geomFactory.createLineString(points);
389
					return JtsUtil.geomFactory.createMultiLineString(new LineString[] {JtsUtil.geomFactory.createLineString(coords)});
390
				*/
391
				System.err.println("Caught Topology exception in GMLLinearRingHandler");
388
				 * if (coords.length > 1 && coords.length <= 3) // return
389
				 * geomFactory.createLineString(points); return
390
				 * JtsUtil.geomFactory.createMultiLineString(new LineString[]
391
				 * {JtsUtil.geomFactory.createLineString(coords)});
392
				 */
393
				System.err
394
						.println("Caught Topology exception in GMLLinearRingHandler");
392 395
				return null;
393 396
			}
394
		}//for
395
		
396
		//At this point, we have a collection of shells and a collection of holes
397
		//Now we have to find for each shell its holes
398
		ArrayList<List<LinearRing>> holesForShells = new ArrayList<List<LinearRing>>(shells.size());
397
		}// for
398

  
399
		// At this point, we have a collection of shells and a collection of
400
		// holes
401
		// Now we have to find for each shell its holes
402
		ArrayList<List<LinearRing>> holesForShells = new ArrayList<List<LinearRing>>(
403
				shells.size());
399 404
		for (int i = 0; i < shells.size(); i++) {
400 405
			holesForShells.add(new ArrayList<LinearRing>());
401 406
		}
402 407

  
403
		
404 408
		/*
405
		 * Now, for each hole we look for the minimal shell that contains it.
406
		 * We look for minimal because many shells could contain the same hole. 
407
		 * */
408
		for (int i = 0; i < holes.size(); i++) {//for each hole
409
		 * Now, for each hole we look for the minimal shell that contains it. We
410
		 * look for minimal because many shells could contain the same hole.
411
		 */
412
		for (int i = 0; i < holes.size(); i++) {// for each hole
409 413
			LinearRing testHole = holes.get(i);
410 414
			LinearRing minShell = null;
411 415
			Envelope minEnv = null;
412 416
			Envelope testEnv = testHole.getEnvelopeInternal();
413 417
			Coordinate testPt = testHole.getCoordinateN(0);
414
			LinearRing tryRing = null;		
415
			
416
			for (int j = 0; j < shells.size(); j++) {//for each shell
418
			LinearRing tryRing = null;
419

  
420
			for (int j = 0; j < shells.size(); j++) {// for each shell
417 421
				tryRing = (LinearRing) shells.get(j);
418 422
				Envelope tryEnv = tryRing.getEnvelopeInternal();
419 423
				boolean isContained = false;
420 424
				Coordinate[] coordList = tryRing.getCoordinates();
421
				
422
				//if testpoint is in ring, or test point is a shell point, is contained
423
				if (tryEnv.contains(testEnv) &&
424
						(SnapCGAlgorithms.isPointInRing(testPt, coordList) ||
425
						JtsUtil.pointInList(testPt, coordList))) {
425

  
426
				// if testpoint is in ring, or test point is a shell point, is
427
				// contained
428
				if (tryEnv.contains(testEnv)
429
						&& (SnapCGAlgorithms.isPointInRing(testPt, coordList) || JtsUtil
430
								.pointInList(testPt, coordList))) {
426 431
					isContained = true;
427 432
				}
428 433

  
429
				// check if this new containing ring is smaller than the current minimum ring
434
				// check if this new containing ring is smaller than the current
435
				// minimum ring
430 436
				if (isContained) {
431 437
					if ((minShell == null) || minEnv.contains(tryEnv)) {
432 438
						minShell = tryRing;
433 439
						minEnv = minShell.getEnvelopeInternal();
434 440
					}
435 441
				}
436
			}//for shells
437
			
438
			//At this point, minShell is the shell that contains a testHole
439
			//if minShell is null, we have a SHELL which points were digitized in the
440
			//wrong order
442
			}// for shells
441 443

  
444
			// At this point, minShell is the shell that contains a testHole
445
			// if minShell is null, we have a SHELL which points were digitized
446
			// in the
447
			// wrong order
448

  
442 449
			if (minShell == null) {
443
				
450

  
444 451
				/*
445
				 * TODO
446
				 * Si las clases de FMap incluyesen en su semantica la diferencia
447
				 * entre un shell y un hole, no deberiamos hacer esta suposicion.
452
				 * TODO Si las clases de FMap incluyesen en su semantica la
453
				 * diferencia entre un shell y un hole, no deberiamos hacer esta
454
				 * suposicion.
448 455
				 * 
449
				 *  Pero como java.awt.geom.Shape no hace esta distincion,
450
				 *  tenemos que hacerla.
456
				 * Pero como java.awt.geom.Shape no hace esta distincion,
457
				 * tenemos que hacerla.
451 458
				 * 
452 459
				 * 
453
				 * */
460
				 */
454 461
				LinearRing reversed = JtsUtil.reverse(testHole);
455 462
				shells.add(reversed);
456 463
				holesForShells.add(new ArrayList<LinearRing>());
457 464
			} else {
458
				((ArrayList<LinearRing>) holesForShells.get(shells.indexOf(minShell))).add(testHole);
465
				((ArrayList<LinearRing>) holesForShells.get(shells
466
						.indexOf(minShell))).add(testHole);
459 467
			}
460
		}//for each hole
468
		}// for each hole
461 469

  
462 470
		Polygon[] polygons = new Polygon[shells.size()];
463 471
		for (int i = 0; i < shells.size(); i++) {
464
			polygons[i] = JtsUtil.GEOMETRY_FACTORY.createPolygon((LinearRing) shells.get(i),
465
					(LinearRing[]) ((ArrayList<LinearRing>) holesForShells.get(i)).toArray(new LinearRing[0]));
472
			polygons[i] = JtsUtil.GEOMETRY_FACTORY.createPolygon(
473
					(LinearRing) shells.get(i),
474
					(LinearRing[]) ((ArrayList<LinearRing>) holesForShells
475
							.get(i)).toArray(new LinearRing[0]));
466 476
		}
467 477
		return JtsUtil.GEOMETRY_FACTORY.createMultiPolygon(polygons);
468 478

  
469 479
	}
470
	
471
	public static Point2D[] getCoordinatesAsPoint2D(Coordinate[] coords){
480

  
481
	public static Point2D[] getCoordinatesAsPoint2D(Coordinate[] coords) {
472 482
		Point2D[] solution = new Point2D[coords.length];
473
		for(int i = 0; i < coords.length; i++){
483
		for (int i = 0; i < coords.length; i++) {
474 484
			solution[i] = new Point2D.Double(coords[i].x, coords[i].y);
475 485
		}
476 486
		return solution;
477 487
	}
478 488

  
479

  
480 489
	public static IGeometry createFPolygon(Point2D[] points) {
481 490
		GeneralPathX gpx = new GeneralPathX();
482 491
		gpx.moveTo(points[0].getX(), points[0].getY());
483 492
		for (int i = 1; i < points.length; i++) {
484 493
			gpx.lineTo(points[i].getX(), points[i].getY());
485 494
		}
486
		if(! isClosed(gpx)){
495
		if (!isClosed(gpx)) {
487 496
			gpx.closePath();
488 497
		}
489 498
		return ShapeFactory.createPolygon2D(gpx);
490 499
	}
491
	
492
	
493
	public static Rectangle2D getSnapRectangle(double x, double y, double snapTolerance){
500

  
501
	public static Rectangle2D getSnapRectangle(double x, double y,
502
			double snapTolerance) {
494 503
		Rectangle2D solution = null;
495 504
		double xmin = x - snapTolerance;
496 505
		double ymin = y - snapTolerance;
......
500 509
		return solution;
501 510
	}
502 511
	
512
	/**
513
	 * Tells if a vertex is in the boundary of a geometry (perimeter or hole perimeer)
514
	 * @param geometry
515
	 * @param point2d
516
	 * @return
517
	 */
518
	public static boolean isInBoundary(IGeometry geometry, Point2D point2d, double tolerance){
519
		Geometry jtsGeom = geometry.toJTSGeometry();
520
		Coordinate coord = new Coordinate(point2d.getX(), point2d.getY());
521
		Coordinate base = new Coordinate(coord.x - tolerance, coord.y - tolerance);
522
		GeometricShapeFactory shpFactory = new GeometricShapeFactory(JtsUtil.GEOMETRY_FACTORY);
523
		shpFactory.setBase(base);
524
		shpFactory.setSize(tolerance * 2);
525
		shpFactory.setNumPoints(4);
526
		Polygon rect = shpFactory.createRectangle();
527
		if(jtsGeom.getDimension() > 1)
528
			jtsGeom = jtsGeom.getBoundary();
529
		return jtsGeom.intersects(rect);
530
	}
531

  
532
	/**
533
	 * Inserts a new vertex in a segment of the geometry boundary.
534
	 * It doesnt moves nothing, it only inserts a new collineaer vertex inside a segment.
535
	 * 
536
	 * It is useful to make topological edition, ensuring two geometries shares a vertex.
537
	 * 
538
	 * @param geometry
539
	 * @param newVertex
540
	 * @param snapTolerance
541
	 * @return
542
	 */
543
	public static IGeometry insertVertex(IGeometry geometry, Point2D newVertex,
544
			double snapTolerance) {
545

  
546
		IGeometry geometryCloned = geometry.cloneGeometry();
547
		double width = snapTolerance * 2;
548
		Rectangle2D rect = new Rectangle2D.Double(newVertex.getX()
549
				- snapTolerance, newVertex.getY() - snapTolerance, width, width);
550

  
551
		if (geometryCloned instanceof FGeometryCollection) {
552
			FGeometryCollection geomCol = (FGeometryCollection) geometryCloned;
553
			IGeometry[] geometries = geomCol.getGeometries();
554
			int length = geometries.length;
555
			IGeometry[] solution = new IGeometry[length];
556
			for (int i = 0; i < length; i++) {
557
				IGeometry geom = geometries[i];
558
				if (geom.intersects(rect)) {
559
					geom = insertVertex(geom, newVertex, snapTolerance);
560
				}
561
				solution[i] = geom;
562
			}// for
563
			return new FGeometryCollection(solution);
564
		}// if
565

  
566
		GeneralPathX gpx = new GeneralPathX();
567
		MultipartShapeIterator it = new MultipartShapeIterator(geometry);
568
		Iterator<Shape> shapeIt = it.getShapeIterator();
569
		while (shapeIt.hasNext()) {
570
			Shape shape = shapeIt.next();
571
			List<Point2D[]> partsCoords = ShapePointExtractor
572
					.extractPoints(shape);
573
			for (int i = 0; i < partsCoords.size(); i++) {
574
				Point2D[] part = partsCoords.get(i);
575
//				boolean mustBreak = false;
576
				for (int j = 0; j < part.length - 1; j++) {
577
					Point2D firstPoint = part[j];
578
					Point2D secondPoint = part[j + 1];
579
					//the first point is the start of a part
580
					if(j == 0){
581
						gpx.moveTo(firstPoint.getX(), firstPoint.getY());
582
					}
583
					else{
584
						gpx.lineTo(firstPoint.getX(), firstPoint.getY());
585
					}
586
					
587
					GeneralPathX gpxAux = new GeneralPathX();
588
					gpxAux.moveTo(firstPoint.getX(), firstPoint.getY());
589
					gpxAux.lineTo(secondPoint.getX(), secondPoint.getY());
590
					IGeometry lineSegment = ShapeFactory.createPolyline2D(gpxAux);
591
					if (lineSegment.intersects(rect)) {
592
						gpx.lineTo(newVertex.getX(), newVertex.getY());
593
//						mustBreak = true;
594
					}
595
					
596
					//if the last point is almost equal to the first point
597
					//close path
598
					if( j + 1 == part.length - 1){
599
						if(part[0].distance(secondPoint) <= snapTolerance){
600
							gpx.closePath();
601
						}else{
602
							gpx.lineTo(secondPoint.getX(), secondPoint.getY());
603
						}	
604
					}
605
				}// for j
606
			}// for i
607
		}// while
608

  
609
		FShape shp = null;
610
		switch (geometryCloned.getGeometryType()) {
611
		case FShape.LINE:
612
		case FShape.LINE + FShape.Z:
613
			shp = new FPolyline2D(gpx);
614
			break;
615
		case FShape.POLYGON:
616
		case FShape.POLYGON + FShape.Z:
617
		case FShape.CIRCLE:
618
		case FShape.ELLIPSE:
619
			shp = new FPolygon2D(gpx);
620
			break;
621
		}
622
		return ShapeFactory.createGeometry(shp);
623
	}
624

  
503 625
}

Also available in: Unified diff