Statistics
| Revision:

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

History | View | Annotate | Download (11.7 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 10626 2007-03-06 16:55:54Z caballero $
47
* $Log$
48
* Revision 1.2  2007-03-06 16:47:58  caballero
49
* Exceptions
50
*
51
* Revision 1.1  2006/06/20 18:20:45  azabala
52
* first version in cvs
53
*
54
* Revision 1.3  2006/06/08 18:24:43  azabala
55
* modificaciones para admitir capas de shapeType MULTI
56
*
57
* Revision 1.2  2006/06/02 18:21:28  azabala
58
* *** empty log message ***
59
*
60
* Revision 1.1  2006/05/24 21:11:14  azabala
61
* primera version en cvs despues de refactoring orientado a crear un framework extensible de geoprocessing
62
*
63
* Revision 1.2  2006/03/23 21:03:45  azabala
64
* *** empty log message ***
65
*
66
* Revision 1.1  2006/03/21 19:27:38  azabala
67
* *** empty log message ***
68
*
69
*
70
*/
71
package com.iver.cit.gvsig.geoprocess.impl.dissolve.fmap;
72

    
73
import java.awt.geom.Rectangle2D;
74
import java.util.ArrayList;
75
import java.util.List;
76
import java.util.Stack;
77

    
78
import com.hardcode.gdbms.driver.exceptions.ReadDriverException;
79
import com.hardcode.gdbms.engine.data.driver.DriverException;
80
import com.hardcode.gdbms.engine.values.Value;
81
import com.iver.cit.gvsig.exceptions.expansionfile.ExpansionFileReadException;
82
import com.iver.cit.gvsig.exceptions.visitors.ProcessVisitorException;
83
import com.iver.cit.gvsig.exceptions.visitors.StartVisitorException;
84
import com.iver.cit.gvsig.exceptions.visitors.VisitorException;
85
import com.iver.cit.gvsig.fmap.core.DefaultFeature;
86
import com.iver.cit.gvsig.fmap.core.IGeometry;
87
import com.iver.cit.gvsig.fmap.core.v02.FConverter;
88
import com.iver.cit.gvsig.fmap.layers.FBitSet;
89
import com.iver.cit.gvsig.fmap.layers.FLayer;
90
import com.iver.cit.gvsig.fmap.layers.FLyrVect;
91
import com.iver.cit.gvsig.fmap.layers.SelectableDataSource;
92
import com.iver.cit.gvsig.fmap.layers.layerOperations.AlphanumericData;
93
import com.iver.cit.gvsig.fmap.layers.layerOperations.VectorialData;
94
import com.iver.cit.gvsig.fmap.operations.strategies.FeatureVisitor;
95
import com.iver.cit.gvsig.fmap.operations.strategies.Strategy;
96
import com.iver.cit.gvsig.geoprocess.core.fmap.FeatureProcessor;
97
import com.iver.cit.gvsig.geoprocess.core.fmap.XTypes;
98
import com.vividsolutions.jts.geom.Geometry;
99
import com.vividsolutions.jts.geom.GeometryFactory;
100

    
101
public class GenericDissolveVisitor implements FeatureVisitor {
102

    
103
        /**
104
         * Allows to get attributes of disolved layer features
105
         */
106
        SelectableDataSource recordset;
107

    
108
        /**
109
         * Is used to do spatial querys (looking for adjacent polygons to visited
110
         * feature geometry
111
         */
112
        FLyrVect dissolvedLayer;
113

    
114
        /**
115
         * It marks all features that have already been dissolved (to avoid process
116
         * them in subsecuent steps)
117
         *
118
         */
119
        FBitSet dissolvedGeometries;
120

    
121
        /**
122
         * Processes results of dissolve operations (save them in a file, or cache
123
         * them in memory, etc)
124
         */
125
        FeatureProcessor featureProcessor;
126
        /**
127
         * Strategy to make queries to first layer
128
         */
129
        Strategy strategy;
130

    
131
        /**
132
         * Visitor to looks for adjacent features to a given feature
133
         */
134
        IndividualGeometryDissolveVisitor visitor = null;
135

    
136
        /**
137
         * Constructor
138
         *
139
         * @param layerToDissolve
140
         */
141
        public GenericDissolveVisitor(FeatureProcessor processor) {
142
                this.featureProcessor = processor;
143
                dissolvedGeometries = new FBitSet();
144
        }
145

    
146
        public int getNumProcessedGeometries(){
147
                return dissolvedGeometries.cardinality();
148
        }
149

    
150
        /*
151
         * Algorithm to compute dissolve is strongly based in depth first
152
         * algorithm to traverse graphs.
153
         *
154
         * It puts features to dissolve in a stack.
155
         * While stack is not empty, get Features and looks for adjacent to it.
156
         * For each adjacent feature, verify its dissolve field value,
157
         * and if it is similar to feature to dissolve
158
         * with, obtain a new feature by unioning their geometries.
159
         * For each adjacent feature, put it in the Stack
160
         */
161
        public void visit(IGeometry g, int index) throws VisitorException, ProcessVisitorException {
162
                if(g == null)
163
                        return;
164
                if(g.getGeometryType() != XTypes.POLYGON &&
165
                                g.getGeometryType() != XTypes.MULTI)
166
                        return;
167
                if (!dissolvedGeometries.get(index)) {
168
                        // if we havent dissolved this feature
169
                        Stack toDissol = new Stack();// stack for adjacent features
170
                        DissolvedFeature feature;
171
                        try {
172
                                feature = createFeature(g, index);
173
                                Geometry jtsGeometry = g.toJTSGeometry();
174
                                feature.setJtsGeometry(jtsGeometry);
175
                                toDissol.push(feature);
176
                                List geometries = dissolve(toDissol);
177
                                Geometry dissolvedGeom = union(geometries);
178
                                DissolvedFeature newFeature =
179
                                        createFeature(null, -1);
180
                                newFeature.setJtsGeometry(dissolvedGeom);
181
                                this.featureProcessor.processFeature(newFeature);
182
                        } catch (ReadDriverException e) {
183
                                throw new ProcessVisitorException(recordset.getName(),e,
184
                                                "Error al procesar las geometrias a fusionar durante dissolve");
185
                        } catch (ExpansionFileReadException e) {
186
                                throw new ProcessVisitorException(recordset.getName(),e,
187
                                                "Error al procesar las geometrias a fusionar durante dissolve");
188
                        } catch (VisitorException e) {
189
                                throw new ProcessVisitorException(recordset.getName(),e,
190
                                                "Error al procesar las geometrias a fusionar durante dissolve");
191
                        }
192
                }// if
193
        }
194

    
195
        protected Geometry union(List geometries){
196
                Geometry[] geom = new Geometry[geometries.size()];
197
                geometries.toArray(geom);
198
                GeometryFactory fact = geom[0].getFactory();
199
            Geometry geomColl = fact.createGeometryCollection(geom);
200
            Geometry union = geomColl.buffer(0.0);
201
            return union;
202
        }
203

    
204

    
205
        /**
206
         * Inner class to manage dissolve geoprocess. It mantains feature info
207
         * interesting for dissolve (int index, JTS Geometry, etc)
208
         *
209
         * @author azabala
210
         *
211
         */
212
        class DissolvedFeature extends DefaultFeature {
213
                int index;
214

    
215
                Geometry jtsGeometry;
216

    
217
                public DissolvedFeature(IGeometry geom, Value[] att, int index) {
218
                        super(geom, att);
219
                        this.index = index;
220
                }
221

    
222
                public int getIndex() {
223
                        return index;
224
                }
225

    
226
                public Geometry getJtsGeometry() {
227
                        return jtsGeometry;
228
                }
229

    
230
                public void setJtsGeometry(Geometry jtsGeometry) {
231
                        this.jtsGeometry = jtsGeometry;
232
                }
233

    
234
                public IGeometry getGeometry() {
235
                        IGeometry solution = super.getGeometry();
236
                        if (solution == null && jtsGeometry != null) {
237
                                solution = FConverter.jts_to_igeometry(jtsGeometry);
238
                        }
239
                        return solution;
240
                }
241

    
242
        }
243

    
244
        /**
245
         * Creates a new IFeature with util info for dissolve geoprocess (it ignores
246
         * non numerical values, etc)
247
         *
248
         * @param g
249
         * @param index
250
         * @return
251
         * @throws DriverException
252
         */
253
        private DissolvedFeature createFeature(IGeometry g, int index) {
254
                DissolvedFeature solution = new DissolvedFeature(g, null, index);
255
                return solution;
256
        }
257

    
258
        /**
259
         * For each individual geometry processed in DissolveVisitor's visit
260
         * method, this Visitor visits its adjacent polygons geometries to check
261
         * dissolve conditions.
262
         *
263
         * @author azabala
264
         *
265
         */
266
        class IndividualGeometryDissolveVisitor implements FeatureVisitor {
267
                /**
268
                 * Marks index of features that have been dissolved yet
269
                 */
270
                FBitSet dissolvedFeatures;
271

    
272
                /**
273
                 * It saves all features for we are looking for adjacent geometries.
274
                 * Dissolving is similar to network tracking algorithms: one feature is
275
                 * adjacent to two, two is adjacent to four, etc We will save features
276
                 * to process in this stack (Depth first aproximation)
277
                 */
278
                Stack featuresToDissolve;
279

    
280
                /**
281
                 * Feature for which we are looking for features to dissolve
282
                 */
283
                DissolvedFeature feature;
284

    
285
                /**
286
                 * Recordset to recover attribute values
287
                 */
288
                SelectableDataSource recordset;
289

    
290
                IndividualGeometryDissolveVisitor(DissolvedFeature feature,
291
                                FBitSet dissolvedFeatures, Stack featuresToDissolve) {
292
                        this.dissolvedFeatures = dissolvedFeatures;
293
                        this.feature = feature;
294
                        this.featuresToDissolve = featuresToDissolve;
295
                }
296

    
297
                public String getProcessDescription() {
298
                        return "Dissolving a polygon with its adjacents";
299
                }
300

    
301

    
302
                public void visit(IGeometry g, int index) throws VisitorException, ProcessVisitorException {
303
                        // Its the feature whose adjacents we are looking for?
304
                        if (index == feature.getIndex())
305
                                return;
306
                        // have we dissolved this feature yet?
307
                        if (dissolvedFeatures.get(index))
308
                                return;
309
                                Geometry jtsGeo = g.toJTSGeometry();
310
                                DissolvedFeature adjacentFeature = createFeature(g, index);
311
                                adjacentFeature.setJtsGeometry(jtsGeo);
312
                                if (jtsGeo.intersects(feature.getJtsGeometry())) {// They are adjacent
313
                                        //TODO PARA HACER GENERICO EL ALGORITMO, Y QUE VALGA
314
                                        //TANTO PARA DISSOLVER BUFFERS COMO PARA UN DISSOLVE
315
                                        //ALFANUMERICO, AQUI USARIAMOS UN STRATEGY DISTINTO
316
                                                dissolvedFeatures.set(index);
317
                                                featuresToDissolve.push(adjacentFeature);
318
                                                //geometriesToDissolve.add(jtsGeo);
319
                                                //List toDissolve = dissolve(featuresToDissolve);
320
                                                //geometriesToDissolve.addAll(toDissolve);
321
                                }// if touches
322
                }// visit
323

    
324
                public void stop(FLayer layer) throws VisitorException {
325
                }
326

    
327
                // FIXME Create an abstract FeatureVisitor
328
                public boolean start(FLayer layer) throws StartVisitorException {
329
                        try {
330
                                recordset = ((AlphanumericData) layer).getRecordset();
331
                        } catch (ReadDriverException e) {
332
                                return false;
333
                        }
334
                        return true;
335
                }
336

    
337
                public void setProcessedFeature(DissolvedFeature feature2) {
338
                        this.feature = feature2;
339

    
340
                }
341
        }// IndividualDissolve
342

    
343
        private List dissolve(Stack toDissol)
344
                        throws ReadDriverException, ExpansionFileReadException, VisitorException {
345
                List solution = new ArrayList();
346
                while (toDissol.size() != 0) {
347
                        DissolvedFeature feature = (DissolvedFeature) toDissol.pop();
348
                        // flags this idx (to not to process in future)
349
                        dissolvedGeometries.set(feature.getIndex());
350
                        if (visitor == null) {
351
                                visitor = new IndividualGeometryDissolveVisitor(feature,
352
                                                dissolvedGeometries, toDissol);
353
                        } else {
354
                                visitor.setProcessedFeature(feature);
355
                        }
356
                        solution.add(feature.getJtsGeometry());
357
                        Rectangle2D bounds = feature.getGeometry().getBounds2D();
358
                        double xmin = bounds.getMinX();
359
                        double ymin = bounds.getMinY();
360
                        double xmax = bounds.getMaxX();
361
                        double ymax = bounds.getMaxY();
362
                        double magnify = 15d;
363
                        Rectangle2D query = new Rectangle2D.Double(xmin - magnify, ymin
364
                                        - magnify, (xmax - xmin) + magnify, (ymax - ymin) + magnify);
365

    
366
                        strategy.process(visitor, query);
367
                }// while
368
                return solution;
369
        }
370

    
371
        public void stop(FLayer layer) throws VisitorException {
372
                this.featureProcessor.finish();
373
        }
374

    
375
        public boolean start(FLayer layer) throws StartVisitorException {
376
                if (layer instanceof AlphanumericData && layer instanceof VectorialData) {
377
                        try {
378
                                dissolvedLayer = (FLyrVect) layer;
379
                                recordset = ((AlphanumericData) layer).getRecordset();
380
                                featureProcessor.start();
381
                        } catch (ReadDriverException e) {
382
                                return false;
383
                        }
384
                        return true;
385
                }
386
                return false;
387
        }
388

    
389
        public void setStrategy(Strategy strategy) {
390
                this.strategy = strategy;
391
        }
392

    
393
        public String getProcessDescription() {
394
                return "Dissolving polygons of a layer";
395
        }
396

    
397

    
398
}
399