Statistics
| Revision:

root / trunk / extensions / extGeoProcessing / src / com / iver / cit / gvsig / geoprocess / impl / buffer / fmap / BufferVisitor.java @ 6526

History | View | Annotate | Download (12.9 KB)

1
/*
2
 * Created on 05-feb-2006
3
 *
4
 * gvSIG. Sistema de Informaci?n Geogr?fica de la Generalitat Valenciana
5
 *
6
 * Copyright (C) 2004 IVER T.I. and Generalitat Valenciana.
7
 *
8
 * This program is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU General Public License
10
 * as published by the Free Software Foundation; either version 2
11
 * of the License, or (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program; if not, write to the Free Software
20
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
21
 *
22
 * For more information, contact:
23
 *
24
 *  Generalitat Valenciana
25
 *   Conselleria d'Infraestructures i Transport
26
 *   Av. Blasco Ib??ez, 50
27
 *   46010 VALENCIA
28
 *   SPAIN
29
 *
30
 *      +34 963862235
31
 *   gvsig@gva.es
32
 *      www.gvsig.gva.es
33
 *
34
 *    or
35
 *
36
 *   IVER T.I. S.A
37
 *   Salamanca 50
38
 *   46005 Valencia
39
 *   Spain
40
 *
41
 *   +34 963163400
42
 *   dac@iver.es
43
 */
44
/* CVS MESSAGES:
45
*
46
* $Id: BufferVisitor.java 6526 2006-07-26 18:06:27Z azabala $
47
* $Log$
48
* Revision 1.3  2006-07-26 18:06:27  azabala
49
* fixed bug when internal buffers collapses geometries
50
*
51
* Revision 1.2  2006/07/21 11:04:50  azabala
52
* Unified createTask and process() methods
53
*
54
* Revision 1.1  2006/06/20 18:20:45  azabala
55
* first version in cvs
56
*
57
* Revision 1.2  2006/06/02 18:20:33  azabala
58
* limpieza de imports
59
*
60
* Revision 1.1  2006/05/24 21:14:41  azabala
61
* primera version en cvs despues de refactoring orientado a crear un framework extensible de geoprocessing
62
*
63
* Revision 1.7  2006/05/01 19:18:09  azabala
64
* revisi?n general del buffer (a?adidos anillos concentricos, buffers interiores y exteriores, etc)
65
*
66
* Revision 1.6  2006/04/07 19:00:33  azabala
67
* *** empty log message ***
68
*
69
* Revision 1.5  2006/03/28 16:27:25  azabala
70
* *** empty log message ***
71
*
72
* Revision 1.4  2006/03/07 21:01:33  azabala
73
* *** empty log message ***
74
*
75
* Revision 1.3  2006/03/05 19:56:27  azabala
76
* *** empty log message ***
77
*
78
* Revision 1.1  2006/02/17 16:33:46  azabala
79
* *** empty log message ***
80
*
81
* Revision 1.1  2006/02/09 16:00:36  azabala
82
* First version in CVS
83
*
84
*
85
*/
86
package com.iver.cit.gvsig.geoprocess.impl.buffer.fmap;
87

    
88
import java.util.ArrayList;
89
import java.util.Stack;
90

    
91
import com.hardcode.gdbms.engine.values.Value;
92
import com.hardcode.gdbms.engine.values.ValueFactory;
93
import com.iver.cit.gvsig.fmap.core.IFeature;
94
import com.iver.cit.gvsig.fmap.core.IGeometry;
95
import com.iver.cit.gvsig.fmap.core.v02.FConverter;
96
import com.iver.cit.gvsig.fmap.operations.strategies.FeatureVisitor;
97
import com.iver.cit.gvsig.fmap.operations.strategies.VisitException;
98
import com.iver.cit.gvsig.geoprocess.core.fmap.FeatureFactory;
99
import com.iver.cit.gvsig.geoprocess.core.fmap.GeoprocessingResultsProcessor;
100
import com.vividsolutions.jts.geom.Geometry;
101
import com.vividsolutions.jts.geom.GeometryCollection;
102
import com.vividsolutions.jts.geom.GeometryFactory;
103
import com.vividsolutions.jts.geom.MultiPolygon;
104
import com.vividsolutions.jts.geom.Polygon;
105
import com.vividsolutions.jts.operation.buffer.BufferOp;
106
import com.vividsolutions.jts.simplify.TopologyPreservingSimplifier;
107

    
108
/**
109
 * Abstract base class to those all FeatureVisitor that computes
110
 * geometry buffers.
111
 * 
112
 * 
113
 * @author azabala
114
 *
115
 */
116
public abstract class BufferVisitor implements FeatureVisitor {
117
//INTERNAL, EXTERNAL OR BOTH FLAGS        
118
        /**
119
         * For polygonal buffers, only compute interior buffers
120
         */
121
        public static final byte BUFFER_INSIDE_POLY = 0;
122
        
123
        /**
124
         * For polygonal buffers, only compute exterior buffers
125
         * (is the default operation, it applies to any geometry type)
126
         */
127
        public static final byte BUFFER_OUTSIDE_POLY = 1;
128
        
129
        /**
130
         * For polygonal buffers, compute interior and exterior buffers
131
         */
132
        public static final byte BUFFER_INSIDE_OUTSIDE_POLY = 2;
133
        
134
//SQUARE OR ROUND CAP FLAGS        
135
        
136
        /**
137
         * Buffer with square cap
138
         */
139
        public static final byte CAP_SQUARE = 0;
140
        /**
141
         * Buffer with round cap
142
         */
143
        public static final byte CAP_ROUND = 1;
144

    
145
        
146
        /**
147
         * type of buffer to compute (polygons could have the three types,
148
         * point and lines only outside buffer.
149
         */
150
        private byte typeOfBuffer = BUFFER_OUTSIDE_POLY;
151
        /**
152
         * To use or not round caps.
153
         */
154
        private byte capBuffer = CAP_ROUND;
155
        
156
        /**
157
         * Number of radial rings buffers
158
         */
159
        private int numberOfRadialBuffers = 1;
160
        
161
        /**
162
         * It process individual JTS buffer results
163
         */
164
        protected GeoprocessingResultsProcessor resultsProcessor;
165
        
166
        /**
167
         * Counter of buffers generated
168
         */
169
        protected int numProcessed = 0;
170
        
171
        /**
172
         * Flag to decide if simplify or not the input
173
         * geometries. Actually always is true, but 
174
         * it could change
175
         */
176
        private boolean simplifyGeometry = true;
177
        
178
        
179
        public void setSimplifyGeometry(boolean simplify){
180
                this.simplifyGeometry = simplify;
181
        }
182
        
183
        /**
184
         * Sets type of buffer flag
185
         * @param typeOfBuffer
186
         */
187
        public void setTypeOfBuffer(byte typeOfBuffer){
188
                this.typeOfBuffer = typeOfBuffer;
189
        }
190
        
191
        /**
192
         * Sets type of cap flag
193
         * @param typeOfCap
194
         */
195
        public void setTypeOfCap(byte typeOfCap){
196
                capBuffer = typeOfCap;
197
        }
198
        
199
        /**
200
         * Sets number of radial rings buffers
201
         * @param number
202
         */
203
        public void setNumberOfRadialBuffers(int number){
204
                numberOfRadialBuffers = number;
205
        }
206
        
207
        /**
208
         * Return number of radial buffers.
209
         * @return
210
         */
211
        public int getNumberOfRadialBuffers(){
212
                return numberOfRadialBuffers;
213
        }
214
        
215

    
216
        /**
217
         * Computes a buffer distance for a feature of the buffer layer
218
         * @param g
219
         * @param index
220
         * @return
221
         */
222
        public abstract double getBufferDistance(IGeometry g, int index);
223
        
224
        /**
225
         * Generates a buffer geometry for the visited IGeometry
226
         */
227
        public void visit(IGeometry g, int index) throws VisitException {
228
                if(g == null)
229
                        return;
230
                Geometry jtsGeo = g.toJTSGeometry();
231
                double bufferDistance = getBufferDistance(g, index);
232
                computeBuffer(jtsGeo, bufferDistance, index);
233
        }
234
        
235
        /**
236
         * Verifys if a geometry buffer is null.
237
         * It is useful when you're working with internal buffers. If the internal
238
         * buffer distance is greater than the geometry radius, the buffer result
239
         * will be null.
240
         * 
241
         * @param newGeometry
242
         * @return
243
         */
244
        public boolean verifyNilGeometry(Geometry newGeometry){
245
                if(newGeometry instanceof GeometryCollection){
246
                        if(((GeometryCollection)newGeometry).getNumGeometries() == 0){
247
                                //we have collapsed initial geometry
248
                                return true;
249
                        }
250
                }
251
                return false;
252
        }
253
        
254
        /**
255
         * 
256
         * Compute a buffer (in function of the params) of
257
         * the original geometry
258
         * 
259
         * @param originalGeometry
260
         * @param bufferDistance
261
         * @return
262
         * 
263
         */
264
        public void computeBuffer(Geometry originalGeometry, double bufferDistance, int index){
265
                Geometry solution = null;
266
                Geometry inputParam = originalGeometry;
267
                /*
268
                 * When we try to apply large buffer distances, we could get OutOfMemoryError
269
                 * exceptions. Explanation in
270
                 * http://lists.jump-project.org/pipermail/jts-devel/2005-February/000991.html
271
                 * http://lists.jump-project.org/pipermail/jts-devel/2005-September/001292.html
272
                 * This problems hasnt been resolved in JTS 1.7.
273
                 */
274
                if(originalGeometry.getDimension() != 0 && simplifyGeometry){
275
                        //ver si 1/10 de la distancia de buffer
276
                        //es un % de simplificaci?n adecuado
277
                        inputParam = 
278
                                TopologyPreservingSimplifier.
279
                                        simplify(originalGeometry, 
280
                                                        bufferDistance / 10d);
281
                }
282
                int cap = BufferOp.CAP_ROUND;
283
                if(capBuffer == CAP_SQUARE){
284
                        cap = BufferOp.CAP_SQUARE;
285
                }
286
                
287
                //this two references are necessary to compute radial rings
288
                Geometry previousExteriorRing = null;
289
                Geometry previousInteriorRing = null;
290
                //TODO Ya se que no est? muy refinado estos if-else, pero
291
                //en principio he priorizado que funcione 
292
                if(typeOfBuffer == BUFFER_INSIDE_POLY){
293
                                //if we have radial internal buffers, we start by
294
                                //most interior buffer
295
                                for(int i = numberOfRadialBuffers; i >= 1; i--){
296
                                        double distRing = i * bufferDistance;
297
                                        BufferOp bufOp = new BufferOp(inputParam);
298
                                        bufOp.setEndCapStyle(cap);
299
                                        Geometry newGeometry = bufOp.getResultGeometry(-1 * distRing);
300
//                                        Geometry newGeometry = BufferOp.bufferOp(inputParam, -1 * distRing, cap);
301
                                        if(verifyNilGeometry(newGeometry)){
302
                                                //we have collapsed original geometry
303
                                                return;
304
                                        }
305
                                        if(previousInteriorRing != null){
306
                                                solution = newGeometry.difference(previousInteriorRing);
307
                                        }else{
308
                                                solution = newGeometry;
309
                                        }
310
                                        numProcessed++;
311
                                        IFeature feature = createFeature(solution, distRing);
312
                                        resultsProcessor.processFeature(feature);
313
                                        previousInteriorRing = newGeometry;
314
                                }
315
                }else if(typeOfBuffer == BUFFER_OUTSIDE_POLY){
316
                        for(int i = 1; i <= numberOfRadialBuffers; i++){
317
                                double distRing = i * bufferDistance;
318
                                BufferOp bufOp = new BufferOp(inputParam);
319
                                bufOp.setEndCapStyle(cap);
320
                                Geometry newGeometry = bufOp.getResultGeometry(distRing);
321
//                                Geometry newGeometry = BufferOp.bufferOp(inputParam, distRing, cap);
322
                                if(previousExteriorRing != null){
323
                                        solution = newGeometry.difference(previousExteriorRing);
324
                                }else{
325
                                        solution = newGeometry;
326
                                }
327
                                numProcessed++;
328
                                IFeature feature = createFeature(solution, distRing);
329
                                resultsProcessor.processFeature(feature);
330
                                previousExteriorRing = newGeometry;
331
                        }
332
                }else if(typeOfBuffer == BUFFER_INSIDE_OUTSIDE_POLY){
333
                        GeometryFactory geomFact = new GeometryFactory();
334
                        for(int i = 1; i <= numberOfRadialBuffers; i++){
335
                                double distRing = i * bufferDistance;
336
                                BufferOp bufOp = new BufferOp(inputParam);
337
                                bufOp.setEndCapStyle(cap);
338
                                Geometry out = bufOp.getResultGeometry(distRing);
339
                                Geometry in = bufOp.getResultGeometry(-1 * distRing);
340
                                boolean collapsedInterior = verifyNilGeometry(in);
341
                                if(previousExteriorRing == null || previousInteriorRing == null){
342
                                        if(collapsedInterior)
343
                                                solution = out;
344
                                        else
345
                                                solution = out.difference(in);
346
                                }else{
347
                                        if(collapsedInterior){
348
                                                solution = out.difference(previousExteriorRing);
349
                                        }else{
350
                                                Geometry outRing = out.difference(previousExteriorRing);
351
                                                Geometry inRing = previousInteriorRing.difference(in);
352
                                                Geometry[] geomArray = new Geometry[]{outRing, inRing};
353
                                                solution = geomFact.createGeometryCollection(geomArray);
354
                                                //FMap doesnt work with GeometryCollection, so we try
355
                                                //to pass to a MultiPolygon.
356
                                                ArrayList polygons = new ArrayList();
357
                                                Stack stack = new Stack();
358
                                                stack.push(solution);
359
                                                while(stack.size() != 0){
360
                                                        GeometryCollection geCol = 
361
                                                                (GeometryCollection) stack.pop(); 
362
                                                        for(int j = 0; j < geCol.getNumGeometries(); j++){
363
                                                                Geometry geometry = geCol.getGeometryN(j);
364
                                                                if(geometry instanceof GeometryCollection)
365
                                                                        stack.push(geometry);
366
                                                                if(geometry instanceof Polygon)
367
                                                                        polygons.add(geometry);
368
                                                        }//for
369
                                                }//while
370
                                                Polygon[] pols = new Polygon[polygons.size()];
371
                                                polygons.toArray(pols);
372
                                                MultiPolygon newSolution = geomFact.createMultiPolygon(pols);
373
                                                solution = newSolution;
374
                                        }
375
                                }//else
376
                                numProcessed++;
377
                                IFeature feature = createFeature(solution, -1 * distRing, distRing);
378
                                resultsProcessor.processFeature(feature);
379
                                previousExteriorRing = out;
380
                                if(!collapsedInterior)
381
                                        previousInteriorRing = in;
382
                        }//for
383
                }//else
384
                
385
        }
386
        
387
        
388
        
389
        /**
390
         * Creates a feature for a simple buffer process for polygons
391
         * (the result feature will have a field with the buffer distance)
392
         * @param jtsGeo
393
         * @param distance
394
         * @return
395
         */
396
        protected IFeature createFeature(Geometry jtsGeo, double distance){
397
                IGeometry iGeo = FConverter.jts_to_igeometry(jtsGeo);
398
                Value[] values = new Value[2];
399
                values[0] = ValueFactory.createValue(numProcessed);
400
                values[1] = ValueFactory.createValue(distance);
401
                return FeatureFactory.createFeature(values, iGeo);
402
        }
403
        
404
        /**
405
         * Creates a feature for a external and internal buffer process for polygons
406
         * (the result feature will have a field with the external buffer distance,
407
         * and a field with the internal buffer distance)
408
         * @param jtsGeo
409
         * @param distance
410
         * @return
411
         */
412
        protected IFeature createFeature(Geometry jtsGeo, double distanceFrom, double distanceTo){
413
                IGeometry iGeo = FConverter.jts_to_igeometry(jtsGeo);
414
                Value[] values = new Value[3];
415
                values[0] = ValueFactory.createValue(numProcessed);
416
                values[1] = ValueFactory.createValue(distanceFrom);
417
                values[2] = ValueFactory.createValue(distanceTo);
418
                return FeatureFactory.createFeature(values, iGeo);
419
        }
420
        
421

    
422
        /**
423
         * Sets BufferResultProcessor, class that has the responsability
424
         * of process individual JTS buffered geometries (save it, 
425
         * to cache it, etc)
426
         * @param processor
427
         */
428
        public void setBufferProcessor(GeoprocessingResultsProcessor processor) {
429
                this.resultsProcessor = processor;
430
                
431
        }
432
        
433
        /**
434
         * Returns buffer processor
435
         * @return
436
         */
437
        public GeoprocessingResultsProcessor getBufferProcessor() {
438
                return resultsProcessor;
439
        }
440

    
441
        /**
442
         * Returns type of buffer flag (internal, external, both)
443
         * @return
444
         */
445
        public byte getTypeOfBuffer() {
446
                return typeOfBuffer;
447
        }
448
        
449
}
450