Statistics
| Revision:

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

History | View | Annotate | Download (12.4 KB)

1 5412 azabala
/*
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$
47
* $Log$
48
* Revision 1.1  2006-05-24 21:14:41  azabala
49
* primera version en cvs despues de refactoring orientado a crear un framework extensible de geoprocessing
50
*
51
* Revision 1.7  2006/05/01 19:18:09  azabala
52
* revisi?n general del buffer (a?adidos anillos concentricos, buffers interiores y exteriores, etc)
53
*
54
* Revision 1.6  2006/04/07 19:00:33  azabala
55
* *** empty log message ***
56
*
57
* Revision 1.5  2006/03/28 16:27:25  azabala
58
* *** empty log message ***
59
*
60
* Revision 1.4  2006/03/07 21:01:33  azabala
61
* *** empty log message ***
62
*
63
* Revision 1.3  2006/03/05 19:56:27  azabala
64
* *** empty log message ***
65
*
66
* Revision 1.1  2006/02/17 16:33:46  azabala
67
* *** empty log message ***
68
*
69
* Revision 1.1  2006/02/09 16:00:36  azabala
70
* First version in CVS
71
*
72
*
73
*/
74
package com.iver.cit.gvsig.geoprocess.buffer.fmap;
75
76
import java.util.ArrayList;
77
import java.util.Stack;
78
79
import com.hardcode.gdbms.engine.values.Value;
80
import com.hardcode.gdbms.engine.values.ValueFactory;
81
import com.iver.cit.gvsig.fmap.core.IFeature;
82
import com.iver.cit.gvsig.fmap.core.IGeometry;
83
import com.iver.cit.gvsig.fmap.core.v02.FConverter;
84
import com.iver.cit.gvsig.fmap.operations.strategies.FeatureVisitor;
85
import com.iver.cit.gvsig.fmap.operations.strategies.VisitException;
86
import com.iver.cit.gvsig.geoprocess.core.fmap.FeatureFactory;
87
import com.iver.cit.gvsig.geoprocess.core.fmap.GeoprocessingResultsProcessor;
88
import com.vividsolutions.jts.geom.Geometry;
89
import com.vividsolutions.jts.geom.GeometryCollection;
90
import com.vividsolutions.jts.geom.GeometryFactory;
91
import com.vividsolutions.jts.geom.MultiPolygon;
92
import com.vividsolutions.jts.geom.Polygon;
93
import com.vividsolutions.jts.operation.buffer.BufferOp;
94
import com.vividsolutions.jts.simplify.TopologyPreservingSimplifier;
95
96
/**
97
 * Abstract base class to those all FeatureVisitor that computes
98
 * geometry buffers.
99
 *
100
 *
101
 * @author azabala
102
 *
103
 */
104
public abstract class BufferVisitor implements FeatureVisitor {
105
//INTERNAL, EXTERNAL OR BOTH FLAGS
106
        /**
107
         * For polygonal buffers, only compute interior buffers
108
         */
109
        public static final byte BUFFER_INSIDE_POLY = 0;
110
111
        /**
112
         * For polygonal buffers, only compute exterior buffers
113
         * (is the default operation, it applies to any geometry type)
114
         */
115
        public static final byte BUFFER_OUTSIDE_POLY = 1;
116
117
        /**
118
         * For polygonal buffers, compute interior and exterior buffers
119
         */
120
        public static final byte BUFFER_INSIDE_OUTSIDE_POLY = 2;
121
122
//SQUARE OR ROUND CAP FLAGS
123
124
        /**
125
         * Buffer with square cap
126
         */
127
        public static final byte CAP_SQUARE = 0;
128
        /**
129
         * Buffer with round cap
130
         */
131
        public static final byte CAP_ROUND = 1;
132
133
134
        /**
135
         * type of buffer to compute (polygons could have the three types,
136
         * point and lines only outside buffer.
137
         */
138
        private byte typeOfBuffer = BUFFER_OUTSIDE_POLY;
139
        /**
140
         * To use or not round caps.
141
         */
142
        private byte capBuffer = CAP_ROUND;
143
144
        /**
145
         * Number of radial rings buffers
146
         */
147
        private int numberOfRadialBuffers = 1;
148
149
        /**
150
         * It process individual JTS buffer results
151
         */
152
        protected GeoprocessingResultsProcessor resultsProcessor;
153
154
        /**
155
         * Counter of buffers generated
156
         */
157
        protected int numProcessed = 0;
158
159
        /**
160
         * Sets type of buffer flag
161
         * @param typeOfBuffer
162
         */
163
        public void setTypeOfBuffer(byte typeOfBuffer){
164
                this.typeOfBuffer = typeOfBuffer;
165
        }
166
167
        /**
168
         * Sets type of cap flag
169
         * @param typeOfCap
170
         */
171
        public void setTypeOfCap(byte typeOfCap){
172
                capBuffer = typeOfCap;
173
        }
174
175
        /**
176
         * Sets number of radial rings buffers
177
         * @param number
178
         */
179
        public void setNumberOfRadialBuffers(int number){
180
                numberOfRadialBuffers = number;
181
        }
182
183
        /**
184
         * Return number of radial buffers.
185
         * @return
186
         */
187
        public int getNumberOfRadialBuffers(){
188
                return numberOfRadialBuffers;
189
        }
190
191
192
        /**
193
         * Computes a buffer distance for a feature of the buffer layer
194
         * @param g
195
         * @param index
196
         * @return
197
         */
198
        public abstract double getBufferDistance(IGeometry g, int index);
199
200
        /**
201
         * Generates a buffer geometry for the visited IGeometry
202
         */
203
        public void visit(IGeometry g, int index) throws VisitException {
204
                Geometry jtsGeo = g.toJTSGeometry();
205
                double bufferDistance = getBufferDistance(g, index);
206
                computeBuffer(jtsGeo, bufferDistance, index);
207
        }
208
209
        /**
210
         * Verifys if a geometry buffer is null.
211
         * It is useful when you're working with internal buffers. If the internal
212
         * buffer distance is greater than the geometry radius, the buffer result
213
         * will be null.
214
         *
215
         * @param newGeometry
216
         * @return
217
         */
218
        public boolean verifyNilGeometry(Geometry newGeometry){
219
                if(newGeometry instanceof GeometryCollection){
220
                        if(((GeometryCollection)newGeometry).getNumGeometries() == 0){
221
                                //we have collapsed initial geometry
222
                                return true;
223
                        }
224
                }
225
                return false;
226
        }
227
228
        /**
229
         *
230
         * Compute a buffer (in function of the params) of
231
         * the original geometry
232
         *
233
         * @param originalGeometry
234
         * @param bufferDistance
235
         * @return
236
         *
237
         */
238
        public void computeBuffer(Geometry originalGeometry, double bufferDistance, int index){
239
                Geometry solution = null;
240
                Geometry inputParam = originalGeometry;
241
                /*
242
                 * When we try to apply large buffer distances, we could get OutOfMemoryError
243
                 * exceptions. Explanation in
244
                 * http://lists.jump-project.org/pipermail/jts-devel/2005-February/000991.html
245
                 * http://lists.jump-project.org/pipermail/jts-devel/2005-September/001292.html
246
                 * This problems hasnt been resolved in JTS 1.7.
247
                 */
248
                if(originalGeometry.getDimension() != 0){
249
                        //ver si 1/10 de la distancia de buffer
250
                        //es un % de simplificaci?n adecuado
251
                        inputParam =
252
                                TopologyPreservingSimplifier.
253
                                        simplify(originalGeometry,
254
                                                        bufferDistance / 10d);
255
                }
256
                int cap = BufferOp.CAP_ROUND;
257
                if(capBuffer == CAP_SQUARE){
258
                        cap = BufferOp.CAP_SQUARE;
259
                }
260
261
                /*
262
                 *NO ME EST? COGIENDO BIEN EL CAP. PROBAR CON ESTO
263
                 Geometry g = . . .
264
                 BufferOp bufOp = new BufferOp(g);
265
                 bufOp.setEndCapStyle(BufferOp.CAP_BUTT);
266
                 Geometry buffer = bufOp.getResultGeometry(distance);
267
                 */
268
                //this two references are necessary to compute radial rings
269
                Geometry previousExteriorRing = null;
270
                Geometry previousInteriorRing = null;
271
                //TODO Ya se que no est? muy refinado estos if-else, pero
272
                //en principio he priorizado que funcione
273
                if(typeOfBuffer == BUFFER_INSIDE_POLY){
274
                                //if we have radial internal buffers, we start by
275
                                //most interior buffer
276
                                for(int i = numberOfRadialBuffers; i >= 1; i--){
277
                                        double distRing = i * bufferDistance;
278
                                        BufferOp bufOp = new BufferOp(inputParam);
279
                                        bufOp.setEndCapStyle(cap);
280
                                        Geometry newGeometry = bufOp.getResultGeometry(-1 * distRing);
281
//                                        Geometry newGeometry = BufferOp.bufferOp(inputParam, -1 * distRing, cap);
282
                                        if(verifyNilGeometry(newGeometry)){
283
                                                //we have collapsed original geometry
284
                                                return;
285
                                        }
286
                                        if(previousInteriorRing != null){
287
                                                solution = newGeometry.difference(previousInteriorRing);
288
                                        }else{
289
                                                solution = newGeometry;
290
                                        }
291
                                        numProcessed++;
292
                                        IFeature feature = createFeature(solution, distRing);
293
                                        resultsProcessor.processFeature(feature);
294
                                        previousInteriorRing = newGeometry;
295
                                }
296
                }else if(typeOfBuffer == BUFFER_OUTSIDE_POLY){
297
                        for(int i = 1; i <= numberOfRadialBuffers; i++){
298
                                double distRing = i * bufferDistance;
299
                                BufferOp bufOp = new BufferOp(inputParam);
300
                                bufOp.setEndCapStyle(cap);
301
                                Geometry newGeometry = bufOp.getResultGeometry(distRing);
302
//                                Geometry newGeometry = BufferOp.bufferOp(inputParam, distRing, cap);
303
                                if(previousExteriorRing != null){
304
                                        solution = newGeometry.difference(previousExteriorRing);
305
                                }else{
306
                                        solution = newGeometry;
307
                                }
308
                                numProcessed++;
309
                                IFeature feature = createFeature(solution, distRing);
310
                                resultsProcessor.processFeature(feature);
311
                                previousExteriorRing = newGeometry;
312
                        }
313
                }else if(typeOfBuffer == BUFFER_INSIDE_OUTSIDE_POLY){
314
                        GeometryFactory geomFact = new GeometryFactory();
315
                        for(int i = 1; i <= numberOfRadialBuffers; i++){
316
                                double distRing = i * bufferDistance;
317
                                BufferOp bufOp = new BufferOp(inputParam);
318
                                bufOp.setEndCapStyle(cap);
319
                                Geometry out = bufOp.getResultGeometry(distRing);
320
                                Geometry in = bufOp.getResultGeometry(-1 * distRing);
321
//                                Geometry out = BufferOp.bufferOp(inputParam, distRing, cap);
322
//                                Geometry in = BufferOp.bufferOp(inputParam, -1 * distRing, cap);
323
                                if(previousExteriorRing == null || previousInteriorRing == null){
324
                                        solution = out.difference(in);
325
                                }else{
326
                                        Geometry outRing = out.difference(previousExteriorRing);
327
                                        Geometry inRing = previousInteriorRing.difference(in);
328
                                        Geometry[] geomArray = new Geometry[]{outRing, inRing};
329
                                        solution = geomFact.createGeometryCollection(geomArray);
330
                                        //FMap doesnt work with GeometryCollection, so we try
331
                                        //to pass to a MultiPolygon.
332
                                        ArrayList polygons = new ArrayList();
333
                                        Stack stack = new Stack();
334
                                        stack.push(solution);
335
                                        while(stack.size() != 0){
336
                                                GeometryCollection geCol =
337
                                                        (GeometryCollection) stack.pop();
338
                                                for(int j = 0; j < geCol.getNumGeometries(); j++){
339
                                                        Geometry geometry = geCol.getGeometryN(j);
340
                                                        if(geometry instanceof GeometryCollection)
341
                                                                stack.push(geometry);
342
                                                        if(geometry instanceof Polygon)
343
                                                                polygons.add(geometry);
344
                                                }//for
345
                                        }//for
346
                                        Polygon[] pols = new Polygon[polygons.size()];
347
                                        polygons.toArray(pols);
348
                                        MultiPolygon newSolution = geomFact.createMultiPolygon(pols);
349
                                        solution = newSolution;
350
                                }//else
351
                                numProcessed++;
352
                                IFeature feature = createFeature(solution, -1 * distRing, distRing);
353
                                resultsProcessor.processFeature(feature);
354
                                previousExteriorRing = out;
355
                                previousInteriorRing = in;
356
                        }//for
357
                }//else
358
359
        }
360
361
        /**
362
         * Creates a feature for a simple buffer process for polygons
363
         * (the result feature will have a field with the buffer distance)
364
         * @param jtsGeo
365
         * @param distance
366
         * @return
367
         */
368
        protected IFeature createFeature(Geometry jtsGeo, double distance){
369
                IGeometry iGeo = FConverter.jts_to_igeometry(jtsGeo);
370
                Value[] values = new Value[2];
371
                values[0] = ValueFactory.createValue(numProcessed);
372
                values[1] = ValueFactory.createValue(distance);
373
                return FeatureFactory.createFeature(values, iGeo);
374
        }
375
376
        /**
377
         * Creates a feature for a external and internal buffer process for polygons
378
         * (the result feature will have a field with the external buffer distance,
379
         * and a field with the internal buffer distance)
380
         * @param jtsGeo
381
         * @param distance
382
         * @return
383
         */
384
        protected IFeature createFeature(Geometry jtsGeo, double distanceFrom, double distanceTo){
385
                IGeometry iGeo = FConverter.jts_to_igeometry(jtsGeo);
386
                Value[] values = new Value[3];
387
                values[0] = ValueFactory.createValue(numProcessed);
388
                values[1] = ValueFactory.createValue(distanceFrom);
389
                values[2] = ValueFactory.createValue(distanceTo);
390
                return FeatureFactory.createFeature(values, iGeo);
391
        }
392
393
394
        /**
395
         * Sets BufferResultProcessor, class that has the responsability
396
         * of process individual JTS buffered geometries (save it,
397
         * to cache it, etc)
398
         * @param processor
399
         */
400
        public void setBufferProcessor(GeoprocessingResultsProcessor processor) {
401
                this.resultsProcessor = processor;
402
403
        }
404
405
        /**
406
         * Returns buffer processor
407
         * @return
408
         */
409
        public GeoprocessingResultsProcessor getBufferProcessor() {
410
                return resultsProcessor;
411
        }
412
413
        /**
414
         * Returns type of buffer flag (internal, external, both)
415
         * @return
416
         */
417
        public byte getTypeOfBuffer() {
418
                return typeOfBuffer;
419
        }
420
421
}