Statistics
| Revision:

svn-gvsig-desktop / trunk / extensions / extGeoProcessing / src / com / iver / gvsig / geoprocessing / impl / dissolve / GenericDissolveVisitor.java @ 4993

History | View | Annotate | Download (10.8 KB)

1
/*
2
 * Created on 21-mar-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: GenericDissolveVisitor.java 4571 2006-03-23 21:07:32Z azabala $
47
* $Log$
48
* Revision 1.2  2006-03-23 21:03:45  azabala
49
* *** empty log message ***
50
*
51
* Revision 1.1  2006/03/21 19:27:38  azabala
52
* *** empty log message ***
53
*
54
*
55
*/
56
package com.iver.gvsig.geoprocessing.impl.dissolve;
57

    
58
import java.awt.geom.Rectangle2D;
59
import java.util.ArrayList;
60
import java.util.List;
61
import java.util.Stack;
62

    
63
import com.hardcode.gdbms.engine.data.driver.DriverException;
64
import com.hardcode.gdbms.engine.values.Value;
65
import com.iver.cit.gvsig.fmap.core.DefaultFeature;
66
import com.iver.cit.gvsig.fmap.core.IGeometry;
67
import com.iver.cit.gvsig.fmap.core.v02.FConverter;
68
import com.iver.cit.gvsig.fmap.edition.EditionException;
69
import com.iver.cit.gvsig.fmap.layers.FBitSet;
70
import com.iver.cit.gvsig.fmap.layers.FLayer;
71
import com.iver.cit.gvsig.fmap.layers.FLyrVect;
72
import com.iver.cit.gvsig.fmap.layers.SelectableDataSource;
73
import com.iver.cit.gvsig.fmap.layers.layerOperations.AlphanumericData;
74
import com.iver.cit.gvsig.fmap.layers.layerOperations.VectorialData;
75
import com.iver.cit.gvsig.fmap.operations.strategies.FeatureVisitor;
76
import com.iver.cit.gvsig.fmap.operations.strategies.Strategy;
77
import com.iver.cit.gvsig.fmap.operations.strategies.VisitException;
78
import com.iver.gvsig.geoprocessing.model.FeatureProcessor;
79
import com.vividsolutions.jts.geom.Geometry;
80
import com.vividsolutions.jts.geom.GeometryFactory;
81

    
82
public class GenericDissolveVisitor implements FeatureVisitor {
83

    
84
        /**
85
         * Allows to get attributes of disolved layer features
86
         */
87
        SelectableDataSource recordset;
88

    
89
        /**
90
         * Is used to do spatial querys (looking for adjacent polygons to visited
91
         * feature geometry
92
         */
93
        FLyrVect dissolvedLayer;
94

    
95
        /**
96
         * It marks all features that have already been dissolved (to avoid process
97
         * them in subsecuent steps)
98
         * 
99
         */
100
        FBitSet dissolvedGeometries;
101

    
102
        /**
103
         * Processes results of dissolve operations (save them in a file, or cache
104
         * them in memory, etc)
105
         */
106
        FeatureProcessor featureProcessor;
107
        /**
108
         * Strategy to make queries to first layer
109
         */
110
        Strategy strategy;
111
        
112
        /**
113
         * Visitor to looks for adjacent features to a given feature
114
         */
115
        IndividualGeometryDissolveVisitor visitor = null;
116

    
117
        /**
118
         * Constructor
119
         * 
120
         * @param layerToDissolve
121
         */
122
        public GenericDissolveVisitor(FeatureProcessor processor) {
123
                this.featureProcessor = processor;
124
                dissolvedGeometries = new FBitSet();
125
        }
126
        
127
        public int getNumProcessedGeometries(){
128
                return dissolvedGeometries.cardinality();
129
        }
130

    
131
        /*
132
         * Algorithm to compute dissolve is strongly based in depth first 
133
         * algorithm to traverse graphs.
134
         * 
135
         * It puts features to dissolve in a stack. 
136
         * While stack is not empty, get Features and looks for adjacent to it. 
137
         * For each adjacent feature, verify its dissolve field value, 
138
         * and if it is similar to feature to dissolve
139
         * with, obtain a new feature by unioning their geometries.
140
         * For each adjacent feature, put it in the Stack
141
         */
142
        public void visit(IGeometry g, int index) throws VisitException {
143
                if (!dissolvedGeometries.get(index)) {
144
                        // if we havent dissolved this feature
145
                        Stack toDissol = new Stack();// stack for adjacent features
146
                        DissolvedFeature feature;
147
                        try {
148
                                feature = createFeature(g, index);
149
                                Geometry jtsGeometry = g.toJTSGeometry();
150
                                feature.setJtsGeometry(jtsGeometry);
151
                                toDissol.push(feature);
152
                                List geometries = dissolve(toDissol);
153
                                Geometry dissolvedGeom = union(geometries);
154
                                DissolvedFeature newFeature = 
155
                                        createFeature(null, -1);
156
                                newFeature.setJtsGeometry(dissolvedGeom);
157
                                this.featureProcessor.processFeature(newFeature);
158
                        } catch (DriverException e) {
159
                                throw new VisitException(
160
                                                "Error al procesar las geometrias a fusionar durante dissolve");
161
                        } catch (com.iver.cit.gvsig.fmap.DriverException e) {
162
                                throw new VisitException(
163
                                                "Error al procesar las geometrias a fusionar durante dissolve");
164
                        }
165
                }// if
166
        }
167
        
168
        protected Geometry union(List geometries){
169
                Geometry[] geom = new Geometry[geometries.size()];
170
                geometries.toArray(geom);
171
                GeometryFactory fact = geom[0].getFactory();
172
            Geometry geomColl = fact.createGeometryCollection(geom);
173
            Geometry union = geomColl.buffer(0.0);
174
            return union;
175
        }
176
        
177

    
178
        /**
179
         * Inner class to manage dissolve geoprocess. It mantains feature info
180
         * interesting for dissolve (int index, JTS Geometry, etc)
181
         * 
182
         * @author azabala
183
         * 
184
         */
185
        class DissolvedFeature extends DefaultFeature {
186
                int index;
187

    
188
                Geometry jtsGeometry;
189

    
190
                public DissolvedFeature(IGeometry geom, Value[] att, int index) {
191
                        super(geom, att);
192
                        this.index = index;
193
                }
194

    
195
                public int getIndex() {
196
                        return index;
197
                }
198

    
199
                public Geometry getJtsGeometry() {
200
                        return jtsGeometry;
201
                }
202

    
203
                public void setJtsGeometry(Geometry jtsGeometry) {
204
                        this.jtsGeometry = jtsGeometry;
205
                }
206

    
207
                public IGeometry getGeometry() {
208
                        IGeometry solution = super.getGeometry();
209
                        if (solution == null && jtsGeometry != null) {
210
                                solution = FConverter.jts_to_igeometry(jtsGeometry);
211
                        }
212
                        return solution;
213
                }
214

    
215
        }
216

    
217
        /**
218
         * Creates a new IFeature with util info for dissolve geoprocess (it ignores
219
         * non numerical values, etc)
220
         * 
221
         * @param g
222
         * @param index
223
         * @return
224
         * @throws DriverException
225
         */
226
        private DissolvedFeature createFeature(IGeometry g, int index)
227
                        throws DriverException {
228
                DissolvedFeature solution = new DissolvedFeature(g, null, index);
229
                return solution;
230
        }
231

    
232
        /**
233
         * For each individual geometry processed in DissolveVisitor's visit
234
         * method, this Visitor visits its adjacent polygons geometries to check 
235
         * dissolve conditions.
236
         * 
237
         * @author azabala
238
         * 
239
         */
240
        class IndividualGeometryDissolveVisitor implements FeatureVisitor {
241
                /**
242
                 * Marks index of features that have been dissolved yet
243
                 */
244
                FBitSet dissolvedFeatures;
245

    
246
                /**
247
                 * It saves all features for we are looking for adjacent geometries.
248
                 * Dissolving is similar to network tracking algorithms: one feature is
249
                 * adjacent to two, two is adjacent to four, etc We will save features
250
                 * to process in this stack (Depth first aproximation)
251
                 */
252
                Stack featuresToDissolve;
253

    
254
                /**
255
                 * Feature for which we are looking for features to dissolve
256
                 */
257
                DissolvedFeature feature;
258

    
259
                /**
260
                 * Recordset to recover attribute values
261
                 */
262
                SelectableDataSource recordset;
263

    
264
                IndividualGeometryDissolveVisitor(DissolvedFeature feature,
265
                                FBitSet dissolvedFeatures, Stack featuresToDissolve) {
266
                        this.dissolvedFeatures = dissolvedFeatures;
267
                        this.feature = feature;
268
                        this.featuresToDissolve = featuresToDissolve;
269
                }
270
                
271
                public String getProcessDescription() {
272
                        return "Dissolving a polygon with its adjacents";
273
                }
274
                
275

    
276
                public void visit(IGeometry g, int index) throws VisitException {
277
                        // Its the feature whose adjacents we are looking for?
278
                        if (index == feature.getIndex())
279
                                return;
280
                        // have we dissolved this feature yet?
281
                        if (dissolvedFeatures.get(index))
282
                                return;
283
                        try {
284
                                Geometry jtsGeo = g.toJTSGeometry();
285
                                DissolvedFeature adjacentFeature = createFeature(g, index);
286
                                adjacentFeature.setJtsGeometry(jtsGeo);
287
                                if (jtsGeo.intersects(feature.getJtsGeometry())) {// They are adjacent
288
                                        //TODO PARA HACER GENERICO EL ALGORITMO, Y QUE VALGA
289
                                        //TANTO PARA DISSOLVER BUFFERS COMO PARA UN DISSOLVE 
290
                                        //ALFANUMERICO, AQUI USARIAMOS UN STRATEGY DISTINTO
291
                                                dissolvedFeatures.set(index);
292
                                                featuresToDissolve.push(adjacentFeature);
293
                                                //geometriesToDissolve.add(jtsGeo);
294
                                                //List toDissolve = dissolve(featuresToDissolve);
295
                                                //geometriesToDissolve.addAll(toDissolve);
296
                                }// if touches
297
                        } catch (DriverException e) {
298
                                throw new VisitException(
299
                                                "Error al cargar los pol?gonos adyacentes durante un dissolve");
300
                        } 
301
                }// visit
302

    
303
                public void stop(FLayer layer) {
304
                }
305

    
306
                // FIXME Create an abstract FeatureVisitor
307
                public boolean start(FLayer layer) {
308
                        try {
309
                                recordset = ((AlphanumericData) layer).getRecordset();
310
                        } catch (com.iver.cit.gvsig.fmap.DriverException e) {
311
                                return false;
312
                        }
313
                        return true;
314
                }
315

    
316
                public void setProcessedFeature(DissolvedFeature feature2) {
317
                        this.feature = feature2;
318

    
319
                }
320
        }// IndividualDissolve
321

    
322
        private List dissolve(Stack toDissol)
323
                        throws com.iver.cit.gvsig.fmap.DriverException, VisitException {
324
                List solution = new ArrayList();
325
                while (toDissol.size() != 0) {
326
                        DissolvedFeature feature = (DissolvedFeature) toDissol.pop();
327
                        // flags this idx (to not to process in future)
328
                        dissolvedGeometries.set(feature.getIndex());
329
                        if (visitor == null) {
330
                                visitor = new IndividualGeometryDissolveVisitor(feature,
331
                                                dissolvedGeometries, toDissol);
332
                        } else {
333
                                visitor.setProcessedFeature(feature);
334
                        }
335
                        solution.add(feature.getJtsGeometry());
336
                        Rectangle2D bounds = feature.getGeometry().getBounds2D();
337
                        double xmin = bounds.getMinX();
338
                        double ymin = bounds.getMinY();
339
                        double xmax = bounds.getMaxX();
340
                        double ymax = bounds.getMaxY();
341
                        double magnify = 15d;
342
                        Rectangle2D query = new Rectangle2D.Double(xmin - magnify, ymin
343
                                        - magnify, (xmax - xmin) + magnify, (ymax - ymin) + magnify);
344

    
345
                        strategy.process(visitor, query);
346
                }// while
347
                return solution;
348
        }
349

    
350
        public void stop(FLayer layer) {
351
                this.featureProcessor.finish();
352
        }
353

    
354
        public boolean start(FLayer layer) {
355
                if (layer instanceof AlphanumericData && layer instanceof VectorialData) {
356
                        try {
357
                                dissolvedLayer = (FLyrVect) layer;
358
                                recordset = ((AlphanumericData) layer).getRecordset();
359
                                featureProcessor.start();
360
                        } catch (com.iver.cit.gvsig.fmap.DriverException e) {
361
                                return false;
362
                        } catch (EditionException e) {
363
                                return false;
364
                        }
365
                        return true;
366
                }
367
                return false;
368
        }
369

    
370
        public void setStrategy(Strategy strategy) {
371
                this.strategy = strategy;
372
        }
373
        
374
        public String getProcessDescription() {
375
                return "Dissolving polygons of a layer";
376
        }
377

    
378

    
379
}
380