Statistics
| Revision:

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

History | View | Annotate | Download (14 KB)

1
/*
2
 * Created on 28-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: SpatialJoinGeoprocess.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:09:47  azabala
52
 * primera version en cvs despues de refactoring orientado a crear un framework extensible de geoprocessing
53
 *
54
 * Revision 1.10  2006/05/08 15:38:05  azabala
55
 * added nn spatial join with rtree
56
 *
57
 * Revision 1.9  2006/05/02 18:57:33  azabala
58
 * added a new implementation of nearest neighbour finder, based in RTree spatial index
59
 *
60
 * Revision 1.8  2006/05/01 19:09:23  azabala
61
 * Intento de optimizar el spatial join por vecino mas proximo (no funciona)
62
 *
63
 * Revision 1.7  2006/03/21 19:29:36  azabala
64
 * *** empty log message ***
65
 *
66
 * Revision 1.6  2006/03/17 19:53:43  azabala
67
 * *** empty log message ***
68
 *
69
 * Revision 1.5  2006/03/15 18:34:31  azabala
70
 * *** empty log message ***
71
 *
72
 * Revision 1.4  2006/03/14 18:32:46  fjp
73
 * Cambio con LayerDefinition para que sea compatible con la definici?n de tablas tambi?n.
74
 *
75
 * Revision 1.3  2006/03/07 21:01:33  azabala
76
 * *** empty log message ***
77
 *
78
 * Revision 1.2  2006/03/06 19:48:39  azabala
79
 * *** empty log message ***
80
 *
81
 * Revision 1.1  2006/03/05 19:59:32  azabala
82
 * *** empty log message ***
83
 *
84
 *
85
 */
86
package com.iver.cit.gvsig.geoprocess.spatialjoin.fmap;
87

    
88
import java.util.Map;
89

    
90
import com.iver.andami.PluginServices;
91
import com.iver.cit.gvsig.fmap.DriverException;
92
import com.iver.cit.gvsig.fmap.core.IGeometry;
93
import com.iver.cit.gvsig.fmap.drivers.DriverIOException;
94
import com.iver.cit.gvsig.fmap.drivers.ILayerDefinition;
95
import com.iver.cit.gvsig.fmap.edition.EditionException;
96
import com.iver.cit.gvsig.fmap.layers.FBitSet;
97
import com.iver.cit.gvsig.fmap.layers.FLyrVect;
98
import com.iver.cit.gvsig.fmap.layers.ReadableVectorial;
99
import com.iver.cit.gvsig.fmap.operations.strategies.Strategy;
100
import com.iver.cit.gvsig.fmap.operations.strategies.StrategyManager;
101
import com.iver.cit.gvsig.fmap.operations.strategies.VisitException;
102
import com.iver.cit.gvsig.fmap.spatialindex.INearestNeighbourFinder;
103
import com.iver.cit.gvsig.fmap.spatialindex.ISpatialIndex;
104
import com.iver.cit.gvsig.fmap.spatialindex.RTreeJsi;
105
import com.iver.cit.gvsig.geoprocess.core.fmap.AbstractGeoprocess;
106
import com.iver.cit.gvsig.geoprocess.core.fmap.FeaturePersisterProcessor2;
107
import com.iver.cit.gvsig.geoprocess.core.fmap.GeoprocessException;
108
import com.iver.cit.gvsig.geoprocess.core.fmap.ITwoLayersGeoprocess;
109
import com.iver.cit.gvsig.geoprocess.core.fmap.XTypes;
110
import com.iver.utiles.swing.threads.CancellableMonitorable;
111
import com.iver.utiles.swing.threads.DefaultCancellableMonitorable;
112
import com.iver.utiles.swing.threads.IMonitorableTask;
113
/**
114
 * This geoprocess implements Spatial Join operation.
115
 * A spatial join is a join where the criteria to link
116
 * a feature of layer A to one (or many) features of layer B
117
 * is a spatial critera.<br>
118
 * We can do two types of spatial join:
119
 * <ul>
120
 * <li><b>Intersect spatial join (1->N).</b> Given a feature, looks for
121
 * all intersecting features, and apply a sumarization function
122
 * to all of them. The result is a feature where all numeric features
123
 * of layer B have been grouped by one or many sumarization functions.
124
 * </li>
125
 * <li><b>Nearest spatial join (1->N)</b>Given a feature, looks
126
 * for the nearest feature of layer B, and tooks its fields.
127
 * </li>
128
 * </ul>
129
 * @author azabala
130
 *
131
 */
132
public class SpatialJoinGeoprocess extends AbstractGeoprocess
133
                                                                implements ITwoLayersGeoprocess {
134
        /**
135
         * overlay layer
136
         */
137
        private FLyrVect secondLayer;
138
        /**
139
         * Relates each numeric field of target layer with
140
         * many sumarization functions
141
         */
142
        private Map fields_sumFunctions;
143

    
144
        /**
145
         * flag to only clip selection of input layer
146
         */
147
        private boolean onlyFirstLayerSelection = false;
148

    
149
        /**
150
         * flag to only clip with selection of clipping layer
151
         */
152
        private boolean onlySecondLayerSelection = false;
153

    
154
        /**
155
         * flag to apply a nearest spatial join (1 to 1 join) or a intersect spatial
156
         * join (1 to m join)
157
         */
158
        private boolean nearestSpatialJoin = true;
159
        
160
        /**
161
         * Visitor that will do the process
162
         */
163
        private SpatialJoinVisitor visitor = null;
164
        /**
165
         * It will process results of joined features
166
         */
167
        private FeaturePersisterProcessor2 processor;
168
        
169

    
170
        public SpatialJoinGeoprocess(FLyrVect inputLayer) {
171
                setFirstOperand(inputLayer);
172
        }
173

    
174
        public void setSecondOperand(FLyrVect overlayLayer) {
175
                this.secondLayer = overlayLayer;
176

    
177
        }
178

    
179
        public void setFirstOperand(FLyrVect firstLayer) {
180
                this.firstLayer = firstLayer;
181
                
182
        }
183

    
184
        /**
185
         * PRECONDITION: We must setResultLayerProperties before
186
         * to call setParameters.
187
         * FIXME
188
         */
189
        public void setParameters(Map params) throws GeoprocessException {
190
                Boolean firstLayerSelection = (Boolean) params
191
                                .get("firstlayerselection");
192
                if (firstLayerSelection != null)
193
                        this.onlyFirstLayerSelection = firstLayerSelection.booleanValue();
194

    
195
                Boolean secondLayerSelection = (Boolean) params
196
                                .get("secondlayerselection");
197
                if (secondLayerSelection != null)
198
                        this.onlySecondLayerSelection = secondLayerSelection.booleanValue();
199

    
200
                if(writer == null)
201
                        throw new GeoprocessException("Hay que hacer setResultLayerProperties antes de hacer setParameters en el spatial join");
202
                processor =
203
                        new FeaturePersisterProcessor2(writer);
204
                
205
                processor = new FeaturePersisterProcessor2(writer);
206
                
207
                Boolean nearest = (Boolean) params
208
                                .get("nearest");
209
                if (nearest != null)
210
                        this.nearestSpatialJoin = nearest.booleanValue();
211
                if(nearestSpatialJoin){
212
                        try {
213
//                                ISpatialIndex spatialIndex = secondLayer.getISpatialIndex();
214
//                                if(spatialIndex != null && 
215
//                                                (spatialIndex instanceof INearestNeighbourFinder))
216
//                                {
217
                                        visitor = new SpatiallyIndexedSpatialJoinVisitor(this.firstLayer, 
218
                                                        this.secondLayer,
219
                                                        processor);
220
//                                }else{
221
//                                        
222
//                                        visitor = new NearestSpatialJoinVisitor(this.firstLayer, 
223
//                                                        this.secondLayer,
224
//                                                        processor);
225
//                                }
226
                        } catch (DriverException e) {
227
                                throw new GeoprocessException("Error preparando el procesado de las capas a enlazar");
228
                        }
229
                        
230
                }else{
231
                        try {
232
                                visitor = new IntersectSpatialJoinVisitor(this.firstLayer,
233
                                                                                                this.secondLayer,
234
                                                                this.fields_sumFunctions, 
235
                                                                processor);
236
                        } catch (DriverException e) {
237
                                throw new GeoprocessException("Error preparando el procesado de las capas a enlazar");
238
                        }
239
                }
240
                visitor.setFeatureProcessor(processor);
241

    
242
        }
243

    
244
        public void checkPreconditions() throws GeoprocessException {
245
                if (firstLayer == null)
246
                        throw new GeoprocessException(
247
                                        "Spatial Join: capa de entrada a null");
248
                if (secondLayer == null)
249
                        throw new GeoprocessException("Spatial Join: 2? capa a null");
250
                if (this.writer == null || this.schemaManager == null) {
251
                        throw new GeoprocessException(
252
                                        "Operacion spatial join sin especificar capa de resultados");
253
                }
254
                try {
255
                        int firstLayerType = firstLayer.getShapeType();
256
                        int secondLayerType = secondLayer.getShapeType();
257
                        if(firstLayerType == XTypes.POINT 
258
                                        && secondLayerType == XTypes.POINT 
259
                                        && (!nearestSpatialJoin)){
260
                                throw new GeoprocessException(
261
                                "No est? permitido el spatial join M:N entre puntos");
262
                        }
263
                } catch (com.iver.cit.gvsig.fmap.DriverException e) {
264
                        throw new GeoprocessException(
265
                                        "Error al tratar de chequear la geometria de las capas a enlazar espacialmente");
266
                }
267
        }
268

    
269
        public void process() throws GeoprocessException {
270
                Strategy strategy =
271
                        StrategyManager.getStrategy(firstLayer);
272
                
273
                Strategy secondLyrStrategy =
274
                        StrategyManager.getStrategy(secondLayer);
275
                visitor.setCancelableStrategy(secondLyrStrategy);
276
                visitor.setOnlySecondLyrSelection(onlySecondLayerSelection);
277
                try {
278
                        if(this.onlyFirstLayerSelection){
279
                                strategy.process(visitor, 
280
                                                firstLayer.getRecordset().
281
                                                getSelection());
282
                                
283
                        }else{
284
                                strategy.process(visitor);
285
                        }
286
                        
287
                } catch (DriverException e) {
288
                        throw new GeoprocessException("Error al acceder a los datos durante un spatial join");
289
                } catch (VisitException e) {
290
                        throw new GeoprocessException("Error al procesar los datos durante un spatial join");
291
                }
292

    
293
        }
294

    
295
        //FIXME to throw an edition exception in 
296
        //schema manager
297
        public void cancel() {
298
                try {
299
                        this.schemaManager.drop();
300
                } catch (EditionException e) {
301
                        e.printStackTrace();
302
                }
303
        }
304

    
305
        /**
306
         * FIXME Lanzar una excepcion si esto se llama antes
307
         * que el setParameters (esto me lo aseguro obligando a meter
308
         * los datos en el constructor)
309
         * 
310
         * FIXME A?adir, en las relaciones 1-N, el n?mero de features
311
         * que participan en la relaci?n de la parte de N
312
         */
313
        public ILayerDefinition createLayerDefinition() {
314
                ILayerDefinition solution = null;
315
                try {
316
                        solution =  visitor.getResultLayerDefinition();
317
                } catch (GeoprocessException e) {
318
                        // TODO Auto-generated catch block
319
                        e.printStackTrace();
320
                }
321
                return solution;
322
        }
323

    
324
        public Map getFields_sumFunctions() {
325
                return fields_sumFunctions;
326
        }
327

    
328
        public void setFields_sumFunctions(Map fields_sumFunctions) {
329
                this.fields_sumFunctions = fields_sumFunctions;
330
        }
331

    
332
        public IMonitorableTask createTask() {
333
                try {
334
                        return new SpatialJoinMonitorableTask();
335
                } catch (Exception e) {
336
                        return null;
337
                }
338
        }
339
        
340
        class SpatialJoinMonitorableTask implements IMonitorableTask {
341
                private CancellableMonitorable cancelMonitor = null;
342
                String SPJOIN_MESSAGE = PluginServices.getText(this, "Mensaje_enlace_espacial");
343
                String SPJOIN_NOTE = PluginServices.getText(this, "Mensaje_procesando_enlace_espacial");
344
                String OF =  PluginServices.getText(this, "De");
345
                
346
                private boolean finished = false;
347

    
348
                SpatialJoinMonitorableTask() throws DriverIOException, DriverException {
349
                        initialize();
350
                }
351
                void initialize() throws DriverIOException, DriverException {
352
                        cancelMonitor = createCancelMonitor();
353
                }
354

    
355
                private CancellableMonitorable createCancelMonitor()
356
                                throws DriverIOException, DriverException {
357
                        DefaultCancellableMonitorable monitor = new 
358
                                                        DefaultCancellableMonitorable();
359
                        monitor.setInitialStep(0);
360
                        monitor.setDeterminatedProcess(true);
361
                        int numSteps = 0;
362
                        if (onlyFirstLayerSelection) {
363
                                FBitSet selection = firstLayer.getRecordset().getSelection();
364
                                numSteps += (2 * selection.cardinality());
365
                        } else {
366
                                numSteps += 2 * firstLayer.getSource().getShapeCount();
367
                        }
368
                        monitor.setFinalStep(numSteps);
369
                        return monitor;
370
                }
371

    
372
                public int getInitialStep() {
373
                        return cancelMonitor.getInitialStep();
374
                }
375

    
376
                public int getFinishStep() {
377
                        return cancelMonitor.getFinalStep();
378
                }
379

    
380
                public int getCurrentStep() {
381
                        return cancelMonitor.getCurrentStep();
382
                }
383

    
384
                public String getStatusMessage() {
385
                        return SPJOIN_MESSAGE;
386
                }
387

    
388
                public String getNote() {
389
                        return SPJOIN_NOTE + " " +
390
                        getCurrentStep() + " "+
391
                        OF + " " + getFinishStep();
392
                }
393

    
394
                public void cancel() {
395
                        ((DefaultCancellableMonitorable) cancelMonitor).setCanceled(true);
396
                        SpatialJoinGeoprocess.this.cancel();
397
                }
398

    
399
                public void run() throws GeoprocessException {
400
                        ISpatialIndex oldSptIdx = null;
401
                        if(nearestSpatialJoin){
402
                                oldSptIdx = secondLayer.getISpatialIndex();
403
                                if(oldSptIdx == null || ! (oldSptIdx instanceof INearestNeighbourFinder)){
404
                                        RTreeJsi newSptIdx = new RTreeJsi();
405
                                        newSptIdx.create();
406
                                        ReadableVectorial source = secondLayer.getSource();
407
                                        try {
408
                                                int numFeatures = source.getShapeCount();
409
                                                for(int i = 0; i < numFeatures; i++){
410
                                                        IGeometry geometry = source.getShape(i);
411
                                                        newSptIdx.insert(geometry.getBounds2D(), i);
412
                                                }
413
                                        } catch (DriverIOException e) {
414
                                                throw new GeoprocessException("Error intentando indexar para busqueda por mas proximo");
415
                                        }
416
                                        secondLayer.setISpatialIndex(newSptIdx);
417
                                }//if oldSptIdx
418
                        }//if nearest
419
                        
420
                        if(visitor instanceof SpatiallyIndexedSpatialJoinVisitor)
421
                        {
422
                                //here checks for Nearest Neighbour capabilitie
423
                                ((SpatiallyIndexedSpatialJoinVisitor)visitor).initialize();
424
                        }
425
                        
426
                        Strategy strategy =
427
                                StrategyManager.getStrategy(firstLayer);
428
                        Strategy secondLyrStrategy =
429
                                StrategyManager.getStrategy(secondLayer);
430
                        visitor.setCancelableStrategy(secondLyrStrategy);
431
                        visitor.setOnlySecondLyrSelection(onlySecondLayerSelection);
432
                        try {
433
                                if(onlyFirstLayerSelection){
434
                                        strategy.process(visitor, 
435
                                                        firstLayer.getRecordset().
436
                                                        getSelection(),
437
                                                        cancelMonitor);
438
                                        
439
                                }else{
440
                                        strategy.process(visitor, cancelMonitor);
441
                                }
442
                                
443
//                                If we changed spatial index to allow Nearest Neighbour queries,
444
                                //recover the old spatial index
445
                                if(oldSptIdx != null)
446
                                        secondLayer.setISpatialIndex(oldSptIdx);
447
                                
448
                        } catch (DriverException e) {
449
                                throw new GeoprocessException("Error al acceder a los datos durante un spatial join");
450
                        } catch (VisitException e) {
451
                                throw new GeoprocessException("Error al procesar los datos durante un spatial join");
452
                        }
453
                        finally{
454
                                finished = true;
455
                        }
456
                }
457

    
458
                public boolean isDefined() {
459
                        return cancelMonitor.isDeterminatedProcess();
460
                }
461

    
462
                public boolean isCanceled() {
463
                        return cancelMonitor.isCanceled();
464
                }
465

    
466
                public boolean isFinished() {
467
                        return finished;
468
                }
469
        }
470
        
471

    
472
}