Statistics
| Revision:

root / trunk / extensions / extGeoProcessing / src / com / iver / cit / gvsig / geoprocess / impl / spatialjoin / fmap / NearestHeuristicSpatialJoinVisitor.java @ 13874

History | View | Annotate | Download (10.4 KB)

1
/*
2
 * Created on 25-abr-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: NearestHeuristicSpatialJoinVisitor.java 13874 2007-09-19 16:12:18Z jaume $
47
* $Log$
48
* Revision 1.3  2007-09-19 16:08:13  jaume
49
* ReadExpansionFileException removed from this context
50
*
51
* Revision 1.2  2007/03/06 16:47:58  caballero
52
* Exceptions
53
*
54
* Revision 1.1  2006/06/20 18:20:45  azabala
55
* first version in cvs
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:09:47  azabala
61
* primera version en cvs despues de refactoring orientado a crear un framework extensible de geoprocessing
62
*
63
* Revision 1.1  2006/05/01 19:09:09  azabala
64
* Intento de optimizar el spatial join por vecino mas proximo (no funciona)
65
*
66
*
67
*/
68
package com.iver.cit.gvsig.geoprocess.impl.spatialjoin.fmap;
69

    
70
import java.awt.geom.Rectangle2D;
71
import java.util.Stack;
72

    
73
import com.hardcode.gdbms.driver.exceptions.ReadDriverException;
74
import com.hardcode.gdbms.engine.data.driver.DriverException;
75
import com.iver.cit.gvsig.exceptions.expansionfile.ExpansionFileReadException;
76
import com.iver.cit.gvsig.exceptions.visitors.ProcessVisitorException;
77
import com.iver.cit.gvsig.exceptions.visitors.StartVisitorException;
78
import com.iver.cit.gvsig.exceptions.visitors.VisitorException;
79
import com.iver.cit.gvsig.fmap.core.IFeature;
80
import com.iver.cit.gvsig.fmap.core.IGeometry;
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.operations.strategies.FeatureVisitor;
85
import com.iver.cit.gvsig.geoprocess.core.fmap.FeatureProcessor;
86
import com.vividsolutions.jts.geom.Envelope;
87
import com.vividsolutions.jts.geom.Geometry;
88
import com.vividsolutions.jts.geom.GeometryFactory;
89
import com.vividsolutions.jts.operation.distance.DistanceOp;
90
/**
91
 * This visitor does nearest feature spatial join by applying an heuristic
92
 * strategy (in constract with NearestSpatialJoinVisitor, that does a
93
 * secuential scanning).
94
 * <br>
95
 * Which heuristic does this visitor apply?
96
 * It obtains second layer (target layer in spatial join) full extent,
97
 * and subdivide it in 4 envelopes. After that, it computes the distance
98
 * of the geometry we want to join with second layer, and computes
99
 * 4 distances with each one of the envelopes. Then, it recursively subdivide
100
 * the Envelope at the shortest distance.
101
 * <br>
102
 * This process is repeated recursively until we obtain a nearest envelope
103
 * of a parametrized dimension. After that, it makes a spatial query with
104
 * this envelope on the layer B. If this query doesnt return, repeat the
105
 * spatial query with the envelope that originated this envelope (we take
106
 * the parent node in the quad-tree structure).
107
 * <br>
108
 * A critical aspect is optimization of the number of levels of quad-tree.
109
 *
110
 * If we take very few levels, the spatial query will return a lot of
111
 * candidates to nearest, so we wont get advantage of this stuff.
112
 *
113
 * If we take a lot of levels, we wont get result in the spatial queries,
114
 * and we'll have to do a lot of querys.
115
 *
116
 * @author azabala
117
 *
118
 * FIXME EL ALGORITMO FALLA!!!!!!!! EL QUADTREE ES UNA ESTRUCTURA
119
 * BUENA PARA RECTANGULOS, PERO PARA PUNTOS CREO QUE NO FUNCIONA.
120
 * NO TIENE EN CUENTA LOS EXTREMOS DE LOS RECTANGULOS
121
 *
122
 */
123
public class NearestHeuristicSpatialJoinVisitor extends NearestSpatialJoinVisitor {
124

    
125
        private QuadTreeUtil quadTree = new QuadTreeUtil();
126
        /**
127
         * Full extent of the layer where we are looking for
128
         * features to join by spatial criteria
129
         */
130
        private Envelope targetLayerEnv = null;
131
        /**
132
         *
133
         * @param sourceLayer
134
         * @param targetLayer
135
         * @param processor
136
         * @throws DriverException
137
         */
138
        public NearestHeuristicSpatialJoinVisitor(FLyrVect sourceLayer,
139
                        FLyrVect targetLayer,
140
                        FeatureProcessor processor) throws ReadDriverException {
141
                super(sourceLayer, targetLayer, processor);
142

    
143
                Rectangle2D rect;
144
                try {
145
                        rect = targetLayer.getFullExtent();
146
                } catch (ExpansionFileReadException e) {
147
                        throw new ReadDriverException(sourceLayer.getName(),e);
148
                }
149
                targetLayerEnv = new Envelope(rect.getMinX(),
150
                                rect.getMaxX(),
151
                                rect.getMinY(),
152
                                rect.getMaxY());
153

    
154
        }
155
        //        TODO If we need a class to look for nearest feature to a given
156
        //feature, move to a public class
157
        class LookForNearest implements FeatureVisitor{
158
                /**
159
                 * Index of the nearest processed feature to the given geometry
160
                 */
161
                int nearestFeatureIndex = -1;
162
                /**
163
                 * min distance of the features processed in the search of
164
                 * nearest feature
165
                 */
166
                double minDistance = Double.MAX_VALUE;
167
                /**
168
                 * Geometry whose nearest feature we want to locate
169
                 */
170
                Geometry firstG;
171
                /**
172
                 * It this selectin is != null, in our search we will only
173
                 * consideer features selected.
174
                 */
175
                FBitSet selection;
176

    
177
                public boolean hasFoundShortest(){
178
                        return nearestFeatureIndex != -1;
179
                }
180

    
181
                public int getNearestFeatureIndex(){
182
                        return nearestFeatureIndex;
183
                }
184

    
185
                public void setSelection(FBitSet bitSet){
186
                        this.selection = bitSet;
187
                }
188

    
189
                public void setGeometry(Geometry firstG){
190
                        this.firstG = firstG;
191
                }
192

    
193
                public void visit(IGeometry g, int index) throws VisitorException, ProcessVisitorException {
194
                        if(selection != null){
195
                                if(! selection.get(index)){
196
                                        return;
197
                                }
198
                        }
199
                        double dist = firstG.distance(g.toJTSGeometry());
200
                        if(dist < minDistance){
201
                                minDistance = dist;
202
                                nearestFeatureIndex = index;
203
                        }//if
204
                }
205
                public String getProcessDescription() {
206
                        return "";
207
                }
208
                public void stop(FLayer layer) throws VisitorException {
209
                }
210
                public boolean start(FLayer layer) throws StartVisitorException {
211
                        return true;
212
                }
213
        };
214
        /**
215
         * Processes a Feature of source layer, looking for its nearest feature of
216
         * target layer and taking attributes from it
217
         */
218
        public void visit(IGeometry g, int sourceIndex) throws VisitorException, ProcessVisitorException {
219
                if(g == null)
220
                        return;
221
                final Geometry geometry = g.toJTSGeometry();
222
                Stack stackOfEnvelopes = quadTree.getNearestEnvelopeOfIdealDimension(geometry,
223
                                targetLayerEnv);
224
                LookForNearest visitor = new LookForNearest();
225
                visitor.setGeometry(geometry);
226
                while((stackOfEnvelopes.size() > 0) ) {
227
                        Envelope envelope = (Envelope) stackOfEnvelopes.pop();
228
                        Rectangle2D.Double rect = new Rectangle2D.Double(envelope.getMinX(),
229
                                        envelope.getMinY(),
230
                                        envelope.getWidth(),
231
                                        envelope.getHeight());
232
                        try {
233
                                if(onlySecondLayerSelection){
234
                                        visitor.setSelection(targetRecordset.getSelection());
235
                                }
236
                                strategy.process(visitor, rect);
237
                                if(visitor.hasFoundShortest()){
238
                                        int targetIndex = visitor.getNearestFeatureIndex();
239
                                        IFeature joinedFeature = createFeature(g,
240
                                                                                                                sourceIndex,
241
                                                                                                                targetIndex, -1);
242
                                        this.featureProcessor.processFeature(joinedFeature);
243
                                        return;
244
                                }
245
                        } catch (ReadDriverException e) {
246
                                throw new ProcessVisitorException(targetRecordset.getName(),e,"Error accediendo a los datos buscando el feature mas proximo");
247
                        } 
248
                }//while
249

    
250
        }
251

    
252
        /**
253
         * FIXME Refinar muy mucho, pero de momento me vale para hacer la busqueda
254
         * de la geometria mas proxima a una dada mediante subdivisi?n del espacio.
255
         *
256
         * @author azabala
257
         *
258
         */
259
        class QuadTreeUtil{
260
                double DEFAULT_IDEAL_DIMENSION = 500d;
261

    
262
                double idealDimension = DEFAULT_IDEAL_DIMENSION;
263

    
264
                public void setIdealDimension(double idealDimension){
265
                        this.idealDimension = idealDimension;
266
                }
267

    
268
                public double distance(Geometry geo, Envelope rect){
269
                        GeometryFactory geoFact = new GeometryFactory();
270
                        Geometry poly = geoFact.toGeometry(rect);
271
                        return DistanceOp.distance(geo, poly);
272
                }
273

    
274
                public double getMaxDimension(Envelope env){
275
                        double w = env.getWidth();
276
                        double h = env.getHeight();
277
                        return (w > h ? w : h);
278
                }
279

    
280

    
281
                public Stack getNearestEnvelopeOfIdealDimension(Geometry nearest,
282
                                Envelope originalEnvelope){
283
                        //stack with all the hierarchical envelopes of the solution quad
284
                        Stack solution = new Stack();
285
                        Envelope firstsolution = originalEnvelope;
286
                        //the last try will be the full extent
287
                        solution.push(firstsolution);
288
                        double maxDimension = getMaxDimension(originalEnvelope);
289
                        while(maxDimension > idealDimension){
290
                                Envelope[] quads = getNextQtreeLevel(firstsolution);
291
                                double d0 = distance(nearest, quads[0]);
292
                                double d1 = distance(nearest, quads[1]);
293
                                double d2 = distance(nearest, quads[2]);
294
                                double d3 = distance(nearest, quads[3]);
295
                                if(d0 <= d1 && d0 <= d2 && d0 <= d3 )
296
                                        firstsolution = quads[0];
297
                                else if(d1 <= d0 && d1 <= d2 && d1 <= d3)
298
                                        firstsolution = quads[1];
299
                                else if(d2 <= d0 && d2 <= d1 && d2 <= d3)
300
                                        firstsolution = quads[2];
301
                                else
302
                                        firstsolution = quads[3];
303
                                solution.push(firstsolution);
304
                                maxDimension = getMaxDimension(firstsolution);
305
                        }
306
                        return solution;
307

    
308
                }
309

    
310
                public  Envelope[] getNextQtreeLevel(Envelope rect){
311
                        Envelope[] solution = new Envelope[4];
312
                        int SW = 0;
313
                        int SE = 1;
314
                        int NW = 2;
315
                        int NE = 3;
316
                        double xMin = rect.getMinX();
317
                        double xMax = rect.getMaxX();
318
                        double yMin = rect.getMinY();
319
                        double yMax = rect.getMaxY();
320
                        double xCenter = (xMin + xMax) / 2d;
321
                        double yCenter = (yMin + yMax) / 2d;
322
                        Envelope r1 = new Envelope(xMin, xCenter, yMin, yCenter);
323
                        Envelope r2 = new Envelope(xCenter, xMax, yMin, yCenter);
324
                        Envelope r3 = new Envelope(xMin, xCenter, yCenter, yMax);
325
                        Envelope r4 = new Envelope(xCenter, xMax, yCenter, yMax);
326
                        solution[SW] = r1;
327
                        solution[SE] = r2;
328
                        solution[NW] = r3;
329
                        solution[NE] = r4;
330
                        return solution;
331
                }
332
        }
333

    
334
}
335