Statistics
| Revision:

root / trunk / extensions / extGeoProcessing / src / com / iver / cit / gvsig / geoprocess / dissolve / fmap / GenericDissolveVisitor.java @ 5628

History | View | Annotate | Download (11 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 5628 2006-06-02 18:21:28Z azabala $
47
* $Log$
48
* Revision 1.2  2006-06-02 18:21:28  azabala
49
* *** empty log message ***
50
*
51
* Revision 1.1  2006/05/24 21:11:14  azabala
52
* primera version en cvs despues de refactoring orientado a crear un framework extensible de geoprocessing
53
*
54
* Revision 1.2  2006/03/23 21:03:45  azabala
55
* *** empty log message ***
56
*
57
* Revision 1.1  2006/03/21 19:27:38  azabala
58
* *** empty log message ***
59
*
60
*
61
*/
62
package com.iver.cit.gvsig.geoprocess.dissolve.fmap;
63

    
64
import java.awt.geom.Rectangle2D;
65
import java.util.ArrayList;
66
import java.util.List;
67
import java.util.Stack;
68

    
69
import com.hardcode.gdbms.engine.data.driver.DriverException;
70
import com.hardcode.gdbms.engine.values.Value;
71
import com.iver.cit.gvsig.fmap.core.DefaultFeature;
72
import com.iver.cit.gvsig.fmap.core.IGeometry;
73
import com.iver.cit.gvsig.fmap.core.v02.FConverter;
74
import com.iver.cit.gvsig.fmap.edition.EditionException;
75
import com.iver.cit.gvsig.fmap.layers.FBitSet;
76
import com.iver.cit.gvsig.fmap.layers.FLayer;
77
import com.iver.cit.gvsig.fmap.layers.FLyrVect;
78
import com.iver.cit.gvsig.fmap.layers.SelectableDataSource;
79
import com.iver.cit.gvsig.fmap.layers.layerOperations.AlphanumericData;
80
import com.iver.cit.gvsig.fmap.layers.layerOperations.VectorialData;
81
import com.iver.cit.gvsig.fmap.operations.strategies.FeatureVisitor;
82
import com.iver.cit.gvsig.fmap.operations.strategies.Strategy;
83
import com.iver.cit.gvsig.fmap.operations.strategies.VisitException;
84
import com.iver.cit.gvsig.geoprocess.core.fmap.FeatureProcessor;
85
import com.vividsolutions.jts.geom.Geometry;
86
import com.vividsolutions.jts.geom.GeometryFactory;
87

    
88
public class GenericDissolveVisitor implements FeatureVisitor {
89

    
90
        /**
91
         * Allows to get attributes of disolved layer features
92
         */
93
        SelectableDataSource recordset;
94

    
95
        /**
96
         * Is used to do spatial querys (looking for adjacent polygons to visited
97
         * feature geometry
98
         */
99
        FLyrVect dissolvedLayer;
100

    
101
        /**
102
         * It marks all features that have already been dissolved (to avoid process
103
         * them in subsecuent steps)
104
         * 
105
         */
106
        FBitSet dissolvedGeometries;
107

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

    
123
        /**
124
         * Constructor
125
         * 
126
         * @param layerToDissolve
127
         */
128
        public GenericDissolveVisitor(FeatureProcessor processor) {
129
                this.featureProcessor = processor;
130
                dissolvedGeometries = new FBitSet();
131
        }
132
        
133
        public int getNumProcessedGeometries(){
134
                return dissolvedGeometries.cardinality();
135
        }
136

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

    
186
        /**
187
         * Inner class to manage dissolve geoprocess. It mantains feature info
188
         * interesting for dissolve (int index, JTS Geometry, etc)
189
         * 
190
         * @author azabala
191
         * 
192
         */
193
        class DissolvedFeature extends DefaultFeature {
194
                int index;
195

    
196
                Geometry jtsGeometry;
197

    
198
                public DissolvedFeature(IGeometry geom, Value[] att, int index) {
199
                        super(geom, att);
200
                        this.index = index;
201
                }
202

    
203
                public int getIndex() {
204
                        return index;
205
                }
206

    
207
                public Geometry getJtsGeometry() {
208
                        return jtsGeometry;
209
                }
210

    
211
                public void setJtsGeometry(Geometry jtsGeometry) {
212
                        this.jtsGeometry = jtsGeometry;
213
                }
214

    
215
                public IGeometry getGeometry() {
216
                        IGeometry solution = super.getGeometry();
217
                        if (solution == null && jtsGeometry != null) {
218
                                solution = FConverter.jts_to_igeometry(jtsGeometry);
219
                        }
220
                        return solution;
221
                }
222

    
223
        }
224

    
225
        /**
226
         * Creates a new IFeature with util info for dissolve geoprocess (it ignores
227
         * non numerical values, etc)
228
         * 
229
         * @param g
230
         * @param index
231
         * @return
232
         * @throws DriverException
233
         */
234
        private DissolvedFeature createFeature(IGeometry g, int index)
235
                        throws DriverException {
236
                DissolvedFeature solution = new DissolvedFeature(g, null, index);
237
                return solution;
238
        }
239

    
240
        /**
241
         * For each individual geometry processed in DissolveVisitor's visit
242
         * method, this Visitor visits its adjacent polygons geometries to check 
243
         * dissolve conditions.
244
         * 
245
         * @author azabala
246
         * 
247
         */
248
        class IndividualGeometryDissolveVisitor implements FeatureVisitor {
249
                /**
250
                 * Marks index of features that have been dissolved yet
251
                 */
252
                FBitSet dissolvedFeatures;
253

    
254
                /**
255
                 * It saves all features for we are looking for adjacent geometries.
256
                 * Dissolving is similar to network tracking algorithms: one feature is
257
                 * adjacent to two, two is adjacent to four, etc We will save features
258
                 * to process in this stack (Depth first aproximation)
259
                 */
260
                Stack featuresToDissolve;
261

    
262
                /**
263
                 * Feature for which we are looking for features to dissolve
264
                 */
265
                DissolvedFeature feature;
266

    
267
                /**
268
                 * Recordset to recover attribute values
269
                 */
270
                SelectableDataSource recordset;
271

    
272
                IndividualGeometryDissolveVisitor(DissolvedFeature feature,
273
                                FBitSet dissolvedFeatures, Stack featuresToDissolve) {
274
                        this.dissolvedFeatures = dissolvedFeatures;
275
                        this.feature = feature;
276
                        this.featuresToDissolve = featuresToDissolve;
277
                }
278
                
279
                public String getProcessDescription() {
280
                        return "Dissolving a polygon with its adjacents";
281
                }
282
                
283

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

    
311
                public void stop(FLayer layer) {
312
                }
313

    
314
                // FIXME Create an abstract FeatureVisitor
315
                public boolean start(FLayer layer) {
316
                        try {
317
                                recordset = ((AlphanumericData) layer).getRecordset();
318
                        } catch (com.iver.cit.gvsig.fmap.DriverException e) {
319
                                return false;
320
                        }
321
                        return true;
322
                }
323

    
324
                public void setProcessedFeature(DissolvedFeature feature2) {
325
                        this.feature = feature2;
326

    
327
                }
328
        }// IndividualDissolve
329

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

    
353
                        strategy.process(visitor, query);
354
                }// while
355
                return solution;
356
        }
357

    
358
        public void stop(FLayer layer) {
359
                this.featureProcessor.finish();
360
        }
361

    
362
        public boolean start(FLayer layer) {
363
                if (layer instanceof AlphanumericData && layer instanceof VectorialData) {
364
                        try {
365
                                dissolvedLayer = (FLyrVect) layer;
366
                                recordset = ((AlphanumericData) layer).getRecordset();
367
                                featureProcessor.start();
368
                        } catch (com.iver.cit.gvsig.fmap.DriverException e) {
369
                                return false;
370
                        } catch (EditionException e) {
371
                                return false;
372
                        }
373
                        return true;
374
                }
375
                return false;
376
        }
377

    
378
        public void setStrategy(Strategy strategy) {
379
                this.strategy = strategy;
380
        }
381
        
382
        public String getProcessDescription() {
383
                return "Dissolving polygons of a layer";
384
        }
385

    
386

    
387
}
388