Revision 19993
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