Statistics
| Revision:

svn-gvsig-desktop / trunk / extensions / extGeoProcessing / src / com / iver / cit / gvsig / geoprocess / impl / buffer / fmap / BufferVisitor.java @ 10626

History | View | Annotate | Download (13.1 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 10626 2007-03-06 16:55:54Z caballero $
47
* $Log$
48
* Revision 1.4  2007-03-06 16:47:58  caballero
49
* Exceptions
50
*
51
* Revision 1.3  2006/07/26 18:06:27  azabala
52
* fixed bug when internal buffers collapses geometries
53
*
54
* Revision 1.2  2006/07/21 11:04:50  azabala
55
* Unified createTask and process() methods
56
*
57
* Revision 1.1  2006/06/20 18:20:45  azabala
58
* first version in cvs
59
*
60
* Revision 1.2  2006/06/02 18:20:33  azabala
61
* limpieza de imports
62
*
63
* Revision 1.1  2006/05/24 21:14:41  azabala
64
* primera version en cvs despues de refactoring orientado a crear un framework extensible de geoprocessing
65
*
66
* Revision 1.7  2006/05/01 19:18:09  azabala
67
* revisi?n general del buffer (a?adidos anillos concentricos, buffers interiores y exteriores, etc)
68
*
69
* Revision 1.6  2006/04/07 19:00:33  azabala
70
* *** empty log message ***
71
*
72
* Revision 1.5  2006/03/28 16:27:25  azabala
73
* *** empty log message ***
74
*
75
* Revision 1.4  2006/03/07 21:01:33  azabala
76
* *** empty log message ***
77
*
78
* Revision 1.3  2006/03/05 19:56:27  azabala
79
* *** empty log message ***
80
*
81
* Revision 1.1  2006/02/17 16:33:46  azabala
82
* *** empty log message ***
83
*
84
* Revision 1.1  2006/02/09 16:00:36  azabala
85
* First version in CVS
86
*
87
*
88
*/
89
package com.iver.cit.gvsig.geoprocess.impl.buffer.fmap;
90

    
91
import java.util.ArrayList;
92
import java.util.Stack;
93

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

    
112
/**
113
 * Abstract base class to those all FeatureVisitor that computes
114
 * geometry buffers.
115
 *
116
 *
117
 * @author azabala
118
 *
119
 */
120
public abstract class BufferVisitor implements FeatureVisitor {
121
//INTERNAL, EXTERNAL OR BOTH FLAGS
122
        /**
123
         * For polygonal buffers, only compute interior buffers
124
         */
125
        public static final byte BUFFER_INSIDE_POLY = 0;
126

    
127
        /**
128
         * For polygonal buffers, only compute exterior buffers
129
         * (is the default operation, it applies to any geometry type)
130
         */
131
        public static final byte BUFFER_OUTSIDE_POLY = 1;
132

    
133
        /**
134
         * For polygonal buffers, compute interior and exterior buffers
135
         */
136
        public static final byte BUFFER_INSIDE_OUTSIDE_POLY = 2;
137

    
138
//SQUARE OR ROUND CAP FLAGS
139

    
140
        /**
141
         * Buffer with square cap
142
         */
143
        public static final byte CAP_SQUARE = 0;
144
        /**
145
         * Buffer with round cap
146
         */
147
        public static final byte CAP_ROUND = 1;
148

    
149

    
150
        /**
151
         * type of buffer to compute (polygons could have the three types,
152
         * point and lines only outside buffer.
153
         */
154
        private byte typeOfBuffer = BUFFER_OUTSIDE_POLY;
155
        /**
156
         * To use or not round caps.
157
         */
158
        private byte capBuffer = CAP_ROUND;
159

    
160
        /**
161
         * Number of radial rings buffers
162
         */
163
        private int numberOfRadialBuffers = 1;
164

    
165
        /**
166
         * It process individual JTS buffer results
167
         */
168
        protected GeoprocessingResultsProcessor resultsProcessor;
169

    
170
        /**
171
         * Counter of buffers generated
172
         */
173
        protected int numProcessed = 0;
174

    
175
        /**
176
         * Flag to decide if simplify or not the input
177
         * geometries. Actually always is true, but
178
         * it could change
179
         */
180
        private boolean simplifyGeometry = true;
181

    
182

    
183
        public void setSimplifyGeometry(boolean simplify){
184
                this.simplifyGeometry = simplify;
185
        }
186

    
187
        /**
188
         * Sets type of buffer flag
189
         * @param typeOfBuffer
190
         */
191
        public void setTypeOfBuffer(byte typeOfBuffer){
192
                this.typeOfBuffer = typeOfBuffer;
193
        }
194

    
195
        /**
196
         * Sets type of cap flag
197
         * @param typeOfCap
198
         */
199
        public void setTypeOfCap(byte typeOfCap){
200
                capBuffer = typeOfCap;
201
        }
202

    
203
        /**
204
         * Sets number of radial rings buffers
205
         * @param number
206
         */
207
        public void setNumberOfRadialBuffers(int number){
208
                numberOfRadialBuffers = number;
209
        }
210

    
211
        /**
212
         * Return number of radial buffers.
213
         * @return
214
         */
215
        public int getNumberOfRadialBuffers(){
216
                return numberOfRadialBuffers;
217
        }
218

    
219

    
220
        /**
221
         * Computes a buffer distance for a feature of the buffer layer
222
         * @param g
223
         * @param index
224
         * @return
225
         */
226
        public abstract double getBufferDistance(IGeometry g, int index);
227

    
228
        /**
229
         * Generates a buffer geometry for the visited IGeometry
230
         */
231
        public void visit(IGeometry g, int index) throws VisitorException, ProcessVisitorException {
232
                if(g == null)
233
                        return;
234
                Geometry jtsGeo = g.toJTSGeometry();
235
                double bufferDistance = getBufferDistance(g, index);
236
                computeBuffer(jtsGeo, bufferDistance, index);
237
        }
238

    
239
        /**
240
         * Verifys if a geometry buffer is null.
241
         * It is useful when you're working with internal buffers. If the internal
242
         * buffer distance is greater than the geometry radius, the buffer result
243
         * will be null.
244
         *
245
         * @param newGeometry
246
         * @return
247
         */
248
        public boolean verifyNilGeometry(Geometry newGeometry){
249
                if(newGeometry instanceof GeometryCollection){
250
                        if(((GeometryCollection)newGeometry).getNumGeometries() == 0){
251
                                //we have collapsed initial geometry
252
                                return true;
253
                        }
254
                }
255
                return false;
256
        }
257

    
258
        /**
259
         *
260
         * Compute a buffer (in function of the params) of
261
         * the original geometry
262
         *
263
         * @param originalGeometry
264
         * @param bufferDistance
265
         * @return
266
         * @throws VisitorException
267
         * @throws ProcessWriterException
268
         *
269
         */
270
        public void computeBuffer(Geometry originalGeometry, double bufferDistance, int index) throws VisitorException{
271
                Geometry solution = null;
272
                Geometry inputParam = originalGeometry;
273
                /*
274
                 * When we try to apply large buffer distances, we could get OutOfMemoryError
275
                 * exceptions. Explanation in
276
                 * http://lists.jump-project.org/pipermail/jts-devel/2005-February/000991.html
277
                 * http://lists.jump-project.org/pipermail/jts-devel/2005-September/001292.html
278
                 * This problems hasnt been resolved in JTS 1.7.
279
                 */
280
                if(originalGeometry.getDimension() != 0 && simplifyGeometry){
281
                        //ver si 1/10 de la distancia de buffer
282
                        //es un % de simplificaci?n adecuado
283
                        inputParam =
284
                                TopologyPreservingSimplifier.
285
                                        simplify(originalGeometry,
286
                                                        bufferDistance / 10d);
287
                }
288
                int cap = BufferOp.CAP_ROUND;
289
                if(capBuffer == CAP_SQUARE){
290
                        cap = BufferOp.CAP_SQUARE;
291
                }
292

    
293
                //this two references are necessary to compute radial rings
294
                Geometry previousExteriorRing = null;
295
                Geometry previousInteriorRing = null;
296
                //TODO Ya se que no est? muy refinado estos if-else, pero
297
                //en principio he priorizado que funcione
298
                if(typeOfBuffer == BUFFER_INSIDE_POLY){
299
                                //if we have radial internal buffers, we start by
300
                                //most interior buffer
301
                                for(int i = numberOfRadialBuffers; i >= 1; i--){
302
                                        double distRing = i * bufferDistance;
303
                                        BufferOp bufOp = new BufferOp(inputParam);
304
                                        bufOp.setEndCapStyle(cap);
305
                                        Geometry newGeometry = bufOp.getResultGeometry(-1 * distRing);
306
//                                        Geometry newGeometry = BufferOp.bufferOp(inputParam, -1 * distRing, cap);
307
                                        if(verifyNilGeometry(newGeometry)){
308
                                                //we have collapsed original geometry
309
                                                return;
310
                                        }
311
                                        if(previousInteriorRing != null){
312
                                                solution = newGeometry.difference(previousInteriorRing);
313
                                        }else{
314
                                                solution = newGeometry;
315
                                        }
316
                                        numProcessed++;
317
                                        IFeature feature = createFeature(solution, distRing);
318
                                        resultsProcessor.processFeature(feature);
319
                                        previousInteriorRing = newGeometry;
320
                                }
321
                }else if(typeOfBuffer == BUFFER_OUTSIDE_POLY){
322
                        for(int i = 1; i <= numberOfRadialBuffers; i++){
323
                                double distRing = i * bufferDistance;
324
                                BufferOp bufOp = new BufferOp(inputParam);
325
                                bufOp.setEndCapStyle(cap);
326
                                Geometry newGeometry = bufOp.getResultGeometry(distRing);
327
//                                Geometry newGeometry = BufferOp.bufferOp(inputParam, distRing, cap);
328
                                if(previousExteriorRing != null){
329
                                        solution = newGeometry.difference(previousExteriorRing);
330
                                }else{
331
                                        solution = newGeometry;
332
                                }
333
                                numProcessed++;
334
                                IFeature feature = createFeature(solution, distRing);
335
                                resultsProcessor.processFeature(feature);
336
                                previousExteriorRing = newGeometry;
337
                        }
338
                }else if(typeOfBuffer == BUFFER_INSIDE_OUTSIDE_POLY){
339
                        GeometryFactory geomFact = new GeometryFactory();
340
                        for(int i = 1; i <= numberOfRadialBuffers; i++){
341
                                double distRing = i * bufferDistance;
342
                                BufferOp bufOp = new BufferOp(inputParam);
343
                                bufOp.setEndCapStyle(cap);
344
                                Geometry out = bufOp.getResultGeometry(distRing);
345
                                Geometry in = bufOp.getResultGeometry(-1 * distRing);
346
                                boolean collapsedInterior = verifyNilGeometry(in);
347
                                if(previousExteriorRing == null || previousInteriorRing == null){
348
                                        if(collapsedInterior)
349
                                                solution = out;
350
                                        else
351
                                                solution = out.difference(in);
352
                                }else{
353
                                        if(collapsedInterior){
354
                                                solution = out.difference(previousExteriorRing);
355
                                        }else{
356
                                                Geometry outRing = out.difference(previousExteriorRing);
357
                                                Geometry inRing = previousInteriorRing.difference(in);
358
                                                Geometry[] geomArray = new Geometry[]{outRing, inRing};
359
                                                solution = geomFact.createGeometryCollection(geomArray);
360
                                                //FMap doesnt work with GeometryCollection, so we try
361
                                                //to pass to a MultiPolygon.
362
                                                ArrayList polygons = new ArrayList();
363
                                                Stack stack = new Stack();
364
                                                stack.push(solution);
365
                                                while(stack.size() != 0){
366
                                                        GeometryCollection geCol =
367
                                                                (GeometryCollection) stack.pop();
368
                                                        for(int j = 0; j < geCol.getNumGeometries(); j++){
369
                                                                Geometry geometry = geCol.getGeometryN(j);
370
                                                                if(geometry instanceof GeometryCollection)
371
                                                                        stack.push(geometry);
372
                                                                if(geometry instanceof Polygon)
373
                                                                        polygons.add(geometry);
374
                                                        }//for
375
                                                }//while
376
                                                Polygon[] pols = new Polygon[polygons.size()];
377
                                                polygons.toArray(pols);
378
                                                MultiPolygon newSolution = geomFact.createMultiPolygon(pols);
379
                                                solution = newSolution;
380
                                        }
381
                                }//else
382
                                numProcessed++;
383
                                IFeature feature = createFeature(solution, -1 * distRing, distRing);
384
                                resultsProcessor.processFeature(feature);
385
                                previousExteriorRing = out;
386
                                if(!collapsedInterior)
387
                                        previousInteriorRing = in;
388
                        }//for
389
                }//else
390

    
391
        }
392

    
393

    
394

    
395
        /**
396
         * Creates a feature for a simple buffer process for polygons
397
         * (the result feature will have a field with the buffer distance)
398
         * @param jtsGeo
399
         * @param distance
400
         * @return
401
         */
402
        protected IFeature createFeature(Geometry jtsGeo, double distance){
403
                IGeometry iGeo = FConverter.jts_to_igeometry(jtsGeo);
404
                Value[] values = new Value[2];
405
                values[0] = ValueFactory.createValue(numProcessed);
406
                values[1] = ValueFactory.createValue(distance);
407
                return FeatureFactory.createFeature(values, iGeo);
408
        }
409

    
410
        /**
411
         * Creates a feature for a external and internal buffer process for polygons
412
         * (the result feature will have a field with the external buffer distance,
413
         * and a field with the internal buffer distance)
414
         * @param jtsGeo
415
         * @param distance
416
         * @return
417
         */
418
        protected IFeature createFeature(Geometry jtsGeo, double distanceFrom, double distanceTo){
419
                IGeometry iGeo = FConverter.jts_to_igeometry(jtsGeo);
420
                Value[] values = new Value[3];
421
                values[0] = ValueFactory.createValue(numProcessed);
422
                values[1] = ValueFactory.createValue(distanceFrom);
423
                values[2] = ValueFactory.createValue(distanceTo);
424
                return FeatureFactory.createFeature(values, iGeo);
425
        }
426

    
427

    
428
        /**
429
         * Sets BufferResultProcessor, class that has the responsability
430
         * of process individual JTS buffered geometries (save it,
431
         * to cache it, etc)
432
         * @param processor
433
         */
434
        public void setBufferProcessor(GeoprocessingResultsProcessor processor) {
435
                this.resultsProcessor = processor;
436

    
437
        }
438

    
439
        /**
440
         * Returns buffer processor
441
         * @return
442
         */
443
        public GeoprocessingResultsProcessor getBufferProcessor() {
444
                return resultsProcessor;
445
        }
446

    
447
        /**
448
         * Returns type of buffer flag (internal, external, both)
449
         * @return
450
         */
451
        public byte getTypeOfBuffer() {
452
                return typeOfBuffer;
453
        }
454

    
455
}
456