Statistics
| Revision:

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

History | View | Annotate | Download (11.3 KB)

1 5918 azabala
/*
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$
47
* $Log$
48
* Revision 1.1  2006-06-20 18:20:45  azabala
49
* first version in cvs
50
*
51
* Revision 1.3  2006/06/08 18:24:43  azabala
52
* modificaciones para admitir capas de shapeType MULTI
53
*
54
* Revision 1.2  2006/06/02 18:21:28  azabala
55
* *** empty log message ***
56
*
57
* Revision 1.1  2006/05/24 21:11:14  azabala
58
* primera version en cvs despues de refactoring orientado a crear un framework extensible de geoprocessing
59
*
60
* Revision 1.2  2006/03/23 21:03:45  azabala
61
* *** empty log message ***
62
*
63
* Revision 1.1  2006/03/21 19:27:38  azabala
64
* *** empty log message ***
65
*
66
*
67
*/
68
package com.iver.cit.gvsig.geoprocess.impl.dissolve.fmap;
69
70
import java.awt.geom.Rectangle2D;
71
import java.util.ArrayList;
72
import java.util.List;
73
import java.util.Stack;
74
75
import com.hardcode.gdbms.engine.data.driver.DriverException;
76
import com.hardcode.gdbms.engine.values.Value;
77
import com.iver.cit.gvsig.fmap.core.DefaultFeature;
78
import com.iver.cit.gvsig.fmap.core.IGeometry;
79
import com.iver.cit.gvsig.fmap.core.v02.FConverter;
80
import com.iver.cit.gvsig.fmap.edition.EditionException;
81
import com.iver.cit.gvsig.fmap.layers.FBitSet;
82
import com.iver.cit.gvsig.fmap.layers.FLayer;
83
import com.iver.cit.gvsig.fmap.layers.FLyrVect;
84
import com.iver.cit.gvsig.fmap.layers.SelectableDataSource;
85
import com.iver.cit.gvsig.fmap.layers.layerOperations.AlphanumericData;
86
import com.iver.cit.gvsig.fmap.layers.layerOperations.VectorialData;
87
import com.iver.cit.gvsig.fmap.operations.strategies.FeatureVisitor;
88
import com.iver.cit.gvsig.fmap.operations.strategies.Strategy;
89
import com.iver.cit.gvsig.fmap.operations.strategies.VisitException;
90
import com.iver.cit.gvsig.geoprocess.core.fmap.FeatureProcessor;
91
import com.iver.cit.gvsig.geoprocess.core.fmap.XTypes;
92
import com.vividsolutions.jts.geom.Geometry;
93
import com.vividsolutions.jts.geom.GeometryFactory;
94
95
public class GenericDissolveVisitor implements FeatureVisitor {
96
97
        /**
98
         * Allows to get attributes of disolved layer features
99
         */
100
        SelectableDataSource recordset;
101
102
        /**
103
         * Is used to do spatial querys (looking for adjacent polygons to visited
104
         * feature geometry
105
         */
106
        FLyrVect dissolvedLayer;
107
108
        /**
109
         * It marks all features that have already been dissolved (to avoid process
110
         * them in subsecuent steps)
111
         *
112
         */
113
        FBitSet dissolvedGeometries;
114
115
        /**
116
         * Processes results of dissolve operations (save them in a file, or cache
117
         * them in memory, etc)
118
         */
119
        FeatureProcessor featureProcessor;
120
        /**
121
         * Strategy to make queries to first layer
122
         */
123
        Strategy strategy;
124
125
        /**
126
         * Visitor to looks for adjacent features to a given feature
127
         */
128
        IndividualGeometryDissolveVisitor visitor = null;
129
130
        /**
131
         * Constructor
132
         *
133
         * @param layerToDissolve
134
         */
135
        public GenericDissolveVisitor(FeatureProcessor processor) {
136
                this.featureProcessor = processor;
137
                dissolvedGeometries = new FBitSet();
138
        }
139
140
        public int getNumProcessedGeometries(){
141
                return dissolvedGeometries.cardinality();
142
        }
143
144
        /*
145
         * Algorithm to compute dissolve is strongly based in depth first
146
         * algorithm to traverse graphs.
147
         *
148
         * It puts features to dissolve in a stack.
149
         * While stack is not empty, get Features and looks for adjacent to it.
150
         * For each adjacent feature, verify its dissolve field value,
151
         * and if it is similar to feature to dissolve
152
         * with, obtain a new feature by unioning their geometries.
153
         * For each adjacent feature, put it in the Stack
154
         */
155
        public void visit(IGeometry g, int index) throws VisitException {
156
                if(g == null)
157
                        return;
158
                if(g.getGeometryType() != XTypes.POLYGON &&
159
                                g.getGeometryType() != XTypes.MULTI)
160
                        return;
161
                if (!dissolvedGeometries.get(index)) {
162
                        // if we havent dissolved this feature
163
                        Stack toDissol = new Stack();// stack for adjacent features
164
                        DissolvedFeature feature;
165
                        try {
166
                                feature = createFeature(g, index);
167
                                Geometry jtsGeometry = g.toJTSGeometry();
168
                                feature.setJtsGeometry(jtsGeometry);
169
                                toDissol.push(feature);
170
                                List geometries = dissolve(toDissol);
171
                                Geometry dissolvedGeom = union(geometries);
172
                                DissolvedFeature newFeature =
173
                                        createFeature(null, -1);
174
                                newFeature.setJtsGeometry(dissolvedGeom);
175
                                this.featureProcessor.processFeature(newFeature);
176
                        } catch (DriverException e) {
177
                                throw new VisitException(
178
                                                "Error al procesar las geometrias a fusionar durante dissolve");
179
                        } catch (com.iver.cit.gvsig.fmap.DriverException e) {
180
                                throw new VisitException(
181
                                                "Error al procesar las geometrias a fusionar durante dissolve");
182
                        }
183
                }// if
184
        }
185
186
        protected Geometry union(List geometries){
187
                Geometry[] geom = new Geometry[geometries.size()];
188
                geometries.toArray(geom);
189
                GeometryFactory fact = geom[0].getFactory();
190
            Geometry geomColl = fact.createGeometryCollection(geom);
191
            Geometry union = geomColl.buffer(0.0);
192
            return union;
193
        }
194
195
196
        /**
197
         * Inner class to manage dissolve geoprocess. It mantains feature info
198
         * interesting for dissolve (int index, JTS Geometry, etc)
199
         *
200
         * @author azabala
201
         *
202
         */
203
        class DissolvedFeature extends DefaultFeature {
204
                int index;
205
206
                Geometry jtsGeometry;
207
208
                public DissolvedFeature(IGeometry geom, Value[] att, int index) {
209
                        super(geom, att);
210
                        this.index = index;
211
                }
212
213
                public int getIndex() {
214
                        return index;
215
                }
216
217
                public Geometry getJtsGeometry() {
218
                        return jtsGeometry;
219
                }
220
221
                public void setJtsGeometry(Geometry jtsGeometry) {
222
                        this.jtsGeometry = jtsGeometry;
223
                }
224
225
                public IGeometry getGeometry() {
226
                        IGeometry solution = super.getGeometry();
227
                        if (solution == null && jtsGeometry != null) {
228
                                solution = FConverter.jts_to_igeometry(jtsGeometry);
229
                        }
230
                        return solution;
231
                }
232
233
        }
234
235
        /**
236
         * Creates a new IFeature with util info for dissolve geoprocess (it ignores
237
         * non numerical values, etc)
238
         *
239
         * @param g
240
         * @param index
241
         * @return
242
         * @throws DriverException
243
         */
244
        private DissolvedFeature createFeature(IGeometry g, int index)
245
                        throws DriverException {
246
                DissolvedFeature solution = new DissolvedFeature(g, null, index);
247
                return solution;
248
        }
249
250
        /**
251
         * For each individual geometry processed in DissolveVisitor's visit
252
         * method, this Visitor visits its adjacent polygons geometries to check
253
         * dissolve conditions.
254
         *
255
         * @author azabala
256
         *
257
         */
258
        class IndividualGeometryDissolveVisitor implements FeatureVisitor {
259
                /**
260
                 * Marks index of features that have been dissolved yet
261
                 */
262
                FBitSet dissolvedFeatures;
263
264
                /**
265
                 * It saves all features for we are looking for adjacent geometries.
266
                 * Dissolving is similar to network tracking algorithms: one feature is
267
                 * adjacent to two, two is adjacent to four, etc We will save features
268
                 * to process in this stack (Depth first aproximation)
269
                 */
270
                Stack featuresToDissolve;
271
272
                /**
273
                 * Feature for which we are looking for features to dissolve
274
                 */
275
                DissolvedFeature feature;
276
277
                /**
278
                 * Recordset to recover attribute values
279
                 */
280
                SelectableDataSource recordset;
281
282
                IndividualGeometryDissolveVisitor(DissolvedFeature feature,
283
                                FBitSet dissolvedFeatures, Stack featuresToDissolve) {
284
                        this.dissolvedFeatures = dissolvedFeatures;
285
                        this.feature = feature;
286
                        this.featuresToDissolve = featuresToDissolve;
287
                }
288
289
                public String getProcessDescription() {
290
                        return "Dissolving a polygon with its adjacents";
291
                }
292
293
294
                public void visit(IGeometry g, int index) throws VisitException {
295
                        // Its the feature whose adjacents we are looking for?
296
                        if (index == feature.getIndex())
297
                                return;
298
                        // have we dissolved this feature yet?
299
                        if (dissolvedFeatures.get(index))
300
                                return;
301
                        try {
302
                                Geometry jtsGeo = g.toJTSGeometry();
303
                                DissolvedFeature adjacentFeature = createFeature(g, index);
304
                                adjacentFeature.setJtsGeometry(jtsGeo);
305
                                if (jtsGeo.intersects(feature.getJtsGeometry())) {// They are adjacent
306
                                        //TODO PARA HACER GENERICO EL ALGORITMO, Y QUE VALGA
307
                                        //TANTO PARA DISSOLVER BUFFERS COMO PARA UN DISSOLVE
308
                                        //ALFANUMERICO, AQUI USARIAMOS UN STRATEGY DISTINTO
309
                                                dissolvedFeatures.set(index);
310
                                                featuresToDissolve.push(adjacentFeature);
311
                                                //geometriesToDissolve.add(jtsGeo);
312
                                                //List toDissolve = dissolve(featuresToDissolve);
313
                                                //geometriesToDissolve.addAll(toDissolve);
314
                                }// if touches
315
                        } catch (DriverException e) {
316
                                throw new VisitException(
317
                                                "Error al cargar los pol?gonos adyacentes durante un dissolve");
318
                        }
319
                }// visit
320
321
                public void stop(FLayer layer) {
322
                }
323
324
                // FIXME Create an abstract FeatureVisitor
325
                public boolean start(FLayer layer) {
326
                        try {
327
                                recordset = ((AlphanumericData) layer).getRecordset();
328
                        } catch (com.iver.cit.gvsig.fmap.DriverException e) {
329
                                return false;
330
                        }
331
                        return true;
332
                }
333
334
                public void setProcessedFeature(DissolvedFeature feature2) {
335
                        this.feature = feature2;
336
337
                }
338
        }// IndividualDissolve
339
340
        private List dissolve(Stack toDissol)
341
                        throws com.iver.cit.gvsig.fmap.DriverException, VisitException {
342
                List solution = new ArrayList();
343
                while (toDissol.size() != 0) {
344
                        DissolvedFeature feature = (DissolvedFeature) toDissol.pop();
345
                        // flags this idx (to not to process in future)
346
                        dissolvedGeometries.set(feature.getIndex());
347
                        if (visitor == null) {
348
                                visitor = new IndividualGeometryDissolveVisitor(feature,
349
                                                dissolvedGeometries, toDissol);
350
                        } else {
351
                                visitor.setProcessedFeature(feature);
352
                        }
353
                        solution.add(feature.getJtsGeometry());
354
                        Rectangle2D bounds = feature.getGeometry().getBounds2D();
355
                        double xmin = bounds.getMinX();
356
                        double ymin = bounds.getMinY();
357
                        double xmax = bounds.getMaxX();
358
                        double ymax = bounds.getMaxY();
359
                        double magnify = 15d;
360
                        Rectangle2D query = new Rectangle2D.Double(xmin - magnify, ymin
361
                                        - magnify, (xmax - xmin) + magnify, (ymax - ymin) + magnify);
362
363
                        strategy.process(visitor, query);
364
                }// while
365
                return solution;
366
        }
367
368
        public void stop(FLayer layer) {
369
                this.featureProcessor.finish();
370
        }
371
372
        public boolean start(FLayer layer) {
373
                if (layer instanceof AlphanumericData && layer instanceof VectorialData) {
374
                        try {
375
                                dissolvedLayer = (FLyrVect) layer;
376
                                recordset = ((AlphanumericData) layer).getRecordset();
377
                                featureProcessor.start();
378
                        } catch (com.iver.cit.gvsig.fmap.DriverException e) {
379
                                return false;
380
                        } catch (EditionException e) {
381
                                return false;
382
                        }
383
                        return true;
384
                }
385
                return false;
386
        }
387
388
        public void setStrategy(Strategy strategy) {
389
                this.strategy = strategy;
390
        }
391
392
        public String getProcessDescription() {
393
                return "Dissolving polygons of a layer";
394
        }
395
396
397
}