Statistics
| Revision:

svn-gvsig-desktop / trunk / libraries / libFMap / src / com / iver / cit / gvsig / fmap / drivers / featureiterators / SpatialQueryFeatureIterator.java @ 30109

History | View | Annotate | Download (12.5 KB)

1
/*
2
 * Created on 12-abr-2007
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: SpatialQueryFeatureIterator.java 30109 2009-07-23 10:34:02Z jpiera $
47
 * $Log$
48
 * Revision 1.4  2007-09-19 16:02:29  azabala
49
 * bug solved (feature returned by iterator has the attributes of the next record)
50
 *
51
 * Revision 1.3  2007/06/07 09:30:09  azabala
52
 * refactor of BOUND FACTOR
53
 *
54
 * Revision 1.2  2007/06/06 18:03:03  azabala
55
 * bug fixed
56
 *
57
 * Revision 1.1  2007/05/29 19:08:11  azabala
58
 * first version in cvs
59
 *
60
 * Revision 1.1  2007/04/19 17:27:58  azabala
61
 * first version in cvs
62
 *
63
 *
64
 */
65
package com.iver.cit.gvsig.fmap.drivers.featureiterators;
66

    
67
import java.awt.geom.Rectangle2D;
68

    
69
import org.cresques.cts.ICoordTrans;
70
import org.cresques.cts.IProjection;
71
import org.geotools.resources.geometry.XRectangle2D;
72

    
73
import com.hardcode.gdbms.driver.exceptions.ReadDriverException;
74
import com.hardcode.gdbms.engine.values.Value;
75
import com.iver.cit.gvsig.exceptions.expansionfile.ExpansionFileReadException;
76
import com.iver.cit.gvsig.fmap.core.DefaultFeature;
77
import com.iver.cit.gvsig.fmap.core.FNullGeometry;
78
import com.iver.cit.gvsig.fmap.core.IFeature;
79
import com.iver.cit.gvsig.fmap.core.IGeometry;
80
import com.iver.cit.gvsig.fmap.drivers.BoundedShapes;
81
import com.iver.cit.gvsig.fmap.layers.ReadableVectorial;
82

    
83
public class SpatialQueryFeatureIterator extends DefaultFeatureIterator {
84

    
85
        /**
86
         * region for which features we are iterating
87
         */
88
        protected Rectangle2D rect;
89

    
90
        /**
91
         * Checks feature geometries to see if intersects the given extent filter
92
         */
93
        protected ISpatialCheck spatialChecker;
94

    
95
        /**
96
         * Feature which will be returned the next time
97
         */
98
        IFeature nextFeature = null;
99

    
100
        private boolean isUsedNext=true;
101
        private boolean hasNext=false;
102

    
103
        /**
104
         * Proportion of query extent area and layer extent area
105
         * to use boundedshapes capability (reading bounds without
106
         * reading its geometry)
107
         *
108
         */
109
        public static final double BOUNDED_SHAPES_FACTOR = 4.0d;
110

    
111
        /**
112
         * Constructor.
113
         *
114
         * @param source
115
         *            vectorial data source
116
         * @param sourceProj
117
         *            original projection of the data (it could be null)
118
         * @param targetProj
119
         *            projection of the returned features
120
         * @param fieldNames
121
         * @param spatialQuery
122
         * @throws ReadDriverException
123
         */
124
        public SpatialQueryFeatureIterator(ReadableVectorial source,
125
                        IProjection sourceProj, IProjection targetProj,
126
                        String[] fieldNames, Rectangle2D spatialQuery, boolean fastIteration)
127
                        throws ReadDriverException {
128
                super(source, sourceProj, targetProj, fieldNames);
129
                setRect(spatialQuery);
130
                if(fastIteration)
131
                        spatialChecker = new FastSpatialCheck();
132
                else
133
                        spatialChecker = new PrecisseSpatialCheck();
134
        }
135

    
136
        public Rectangle2D getRect() {
137
                return rect;
138
        }
139

    
140
        public void setRect(Rectangle2D rect) {
141
                //by design, rect Rectangle2D must be in the target reprojection
142
                //if targetReprojection != sourceReprojection, we are going to reproject
143
                //rect to the source reprojection (is faster).
144
                //once spatial check is made, result features will be reprojected in the inverse direction
145
                if(this.targetProjection != null &&
146
                   this.sourceProjection != null &&
147
                   this.targetProjection.getAbrev() != this.sourceProjection.getAbrev()){
148
                        ICoordTrans trans = targetProjection.getCT(sourceProjection);
149
                        try{
150
                                rect = trans.convert(rect);
151
                        } catch (Exception e) {
152
                                // if we get an exception during re-projection, that usually
153
                                // means that the provided rectangle coordinates exceed the
154
                                // logical limits of the target CRS. In this case we can fallback
155
                                // to the full layer extent
156
                                try {
157
                                        this.rect = source.getFullExtent();
158
                                } catch (ExpansionFileReadException e1) {
159
                                        throw new RuntimeException(e1);
160
                                } catch (ReadDriverException e1) {
161
                                        throw new RuntimeException(e1);
162
                                }
163
                        }
164
                }
165
                this.rect = rect;
166
        }
167

    
168
        public boolean hasNext() throws ReadDriverException {
169
                if(!isUsedNext){
170
                        return hasNext;
171
                }
172
                try {
173
                        while(true){
174
                                if(currentFeature >= source.getShapeCount())
175
                                        return (hasNext=false);
176
                                if(spatialChecker.intersects(rect, currentFeature)){
177
                                        //we only update the counter if spatialChecker could read the geometry
178
                                        //if it is boundedshape it doesnt read the geometry, so we need to read
179
                                        //currentFeature again
180
//                                        if(spatialChecker.returnShapes())
181
//                                                currentFeature++;
182
                                        break;
183
                                }
184
                                else
185
                                        currentFeature++;
186
                        }//while
187

    
188

    
189
                        //here, currentFeature intersects with the rectangle2d filter
190
                        IGeometry geom = null;
191
                        IFeature feat = null;
192
                        if(spatialChecker.returnShapes()){
193
                                geom = spatialChecker.getLastGeometry();
194
                        }else{
195
                                geom = chekIfCloned(source.getShape(currentFeature));
196
//                                currentFeature++;
197
                                reprojectIfNecessary(geom);
198
                        }
199
                        Value[] regAtt = getValues(currentFeature);
200
                        if (geom==null) {
201
                                geom = new FNullGeometry();
202
                        }
203
                        feat = new DefaultFeature(geom, regAtt, currentFeature + "");
204
                        nextFeature = feat;
205
                        currentFeature++;
206
                        return (hasNext = true);
207
                } catch (ExpansionFileReadException e) {
208
                        throw new ReadDriverException(
209
                                        "Error accediendo a datos del driver durante la iteracion",
210
                                        e);
211
                }
212
        }
213

    
214
        public IFeature next() throws ReadDriverException {
215
                isUsedNext=true;
216
                return nextFeature;
217
        }
218

    
219
        /**
220
         * Classes that chekcs if a specified feature intersects with a given Rectangle2D must
221
         * implement this interface.
222
         *
223
         * This interface is necessary because there are many approach to do that
224
         * (an exact intersection, an intersection based in bounds2d, etc)
225
         *
226
         *
227
         * @author azabala
228
         *
229
         */
230
        interface ISpatialCheck {
231
                public boolean intersects(Rectangle2D extent, int featureIndex)
232
                                throws ExpansionFileReadException, ReadDriverException;
233

    
234
                /**
235
                 * Tells if this spatialcheck could return the geometry of the features
236
                 * (or if it is boundedshapes based, needs another driver access operation)
237
                 * @return
238
                 */
239
                public boolean returnShapes();
240
                /**
241
                 * Returns the last readed geometry (if the spatialcheck do that)
242
                 * @return
243
                 */
244
                public IGeometry getLastGeometry();
245

    
246
                /**
247
                 * Return the index of the last readed geometry
248
                 * @return
249
                 */
250
                public int getIndexOfLast();
251

    
252

    
253
        }
254

    
255

    
256

    
257

    
258
        /**
259
         * All classes that return the bounds Rectangle2D of a feature must
260
         * implement this interface.
261
         *
262
         * @author azabala
263
         *
264
         */
265
        interface BoundsProvider {
266
                /**
267
                 * Returns the bound of the specified feature index
268
                 *
269
                 * @param featureIndex
270
                 * @return
271
                 * @throws ExpansionFileReadException
272
                 * @throws ReadDriverException
273
                 */
274
                public Rectangle2D getBounds(int featureIndex)
275
                                throws ExpansionFileReadException, ReadDriverException;
276

    
277
                /**
278
                 * Tells if this boundsProvider could returns shapes
279
                 *
280
                 * @return
281
                 */
282
                public boolean returnShapes();
283

    
284
                /**
285
                 * Returns the last geometry readed, if the boundsProvider could do that
286
                 *
287
                 * @return
288
                 */
289
                public IGeometry getLastGeometry();
290
        }
291

    
292

    
293

    
294

    
295
        /**
296
         * BoundsProvider that uses a BoundedShapes (faster than others)
297
         *
298
         * @author azabala
299
         *
300
         */
301
        class BoundedShapesProvider implements BoundsProvider {
302
                BoundedShapes boundedShapes;
303

    
304
                BoundedShapesProvider(BoundedShapes boundedShapes) {
305
                        this.boundedShapes = boundedShapes;
306
                }
307

    
308
                public Rectangle2D getBounds(int featureIndex)
309
                                throws ExpansionFileReadException, ReadDriverException {
310
                        return boundedShapes.getShapeBounds(featureIndex);
311
                }
312

    
313
                public boolean returnShapes() {
314
                        return false;
315
                }
316

    
317
                public IGeometry getLastGeometry() {
318
                        return null;
319
                }
320

    
321
        }
322

    
323

    
324

    
325

    
326
        /**
327
         * BoundsProvider that returns feature bounds from the feature geometry
328
         *
329
         * @author azabala
330
         *
331
         */
332
        class IGeometryBoundProvider implements BoundsProvider {
333
                /**
334
                 * Adapter of a given driver from which we read the features bounds.
335
                 */
336
                ReadableVectorial source;
337

    
338
                IGeometry lastGeometry;
339

    
340
                IGeometryBoundProvider(ReadableVectorial sourceOfFeatures) {
341
                        this.source = sourceOfFeatures;
342
                }
343

    
344
                public Rectangle2D getBounds(int featureIndex)
345
                                throws ExpansionFileReadException, ReadDriverException {
346
                        lastGeometry = chekIfCloned(source.getShape(featureIndex));
347
                        if (lastGeometry ==null)
348
                                return new Rectangle2D.Double(0,0,0,0);
349
//                        reprojectIfNecessary(this.lastGeometry);
350
                        //bounds2D is in the original projection
351
                        Rectangle2D solution = lastGeometry.getBounds2D();
352
                        //the readed geometry in the specified projection
353
//                        reprojectIfNecessary(this.lastGeometry);
354
                        return solution;
355
                }
356

    
357
                public IGeometry getLastGeometry() {
358
                        reprojectIfNecessary(this.lastGeometry);
359
                        return lastGeometry;
360
                }
361

    
362
                public boolean returnShapes() {
363
                        return true;
364
                }
365

    
366
        }
367

    
368

    
369

    
370

    
371
        /**
372
         * Checks if the specified features intersects with rectangle2D instances in
373
         * a rough but fast manner.
374
         *
375
         */
376
        class FastSpatialCheck implements ISpatialCheck {
377
                BoundsProvider boundProvider;
378
                int lastIndex;
379

    
380

    
381
                public FastSpatialCheck() {
382
                        try {
383
                                if(isBoundedShapesNecessary()){
384
                                        if (source instanceof BoundedShapes){
385
                                                boundProvider = new BoundedShapesProvider(
386
                                                                (BoundedShapes) source);
387
                                        }else if (source.getDriver() instanceof BoundedShapes){
388
                                                boundProvider = new BoundedShapesProvider(
389
                                                                (BoundedShapes) source.getDriver());
390
                                    }else{
391
                                            boundProvider = new IGeometryBoundProvider(source);
392
                                    }
393
                                }else{
394
                                                boundProvider = new IGeometryBoundProvider(source);
395
                                }
396
                        } catch (ExpansionFileReadException e) {
397
                                // TODO Auto-generated catch block
398
                                e.printStackTrace();
399
                                boundProvider = new IGeometryBoundProvider(source);
400
                        } catch (ReadDriverException e) {
401
                                // TODO Auto-generated catch block
402
                                e.printStackTrace();
403
                                boundProvider = new IGeometryBoundProvider(source);
404
                        }
405
                }
406

    
407

    
408
                protected boolean isBoundedShapesNecessary() throws ReadDriverException, ExpansionFileReadException {
409
                        Rectangle2D driverExtent = source.getFullExtent();
410
                        double areaExtent = rect.getWidth() * rect.getHeight();
411
                        double areaFullExtent = driverExtent.getWidth() *
412
                                                         driverExtent.getHeight();
413
                        return areaExtent < (areaFullExtent / BOUNDED_SHAPES_FACTOR);
414

    
415
                }
416

    
417
                public boolean intersects(Rectangle2D extent, int featureIndex)
418
                                throws ExpansionFileReadException, ReadDriverException {
419
                        this.lastIndex = featureIndex;
420
                        Rectangle2D featureBounds = boundProvider.getBounds(featureIndex);
421
                        return XRectangle2D.intersectInclusive(extent, featureBounds);
422
                }
423

    
424
                public BoundsProvider getBoundsProvider() {
425
                        return boundProvider;
426
                }
427

    
428
                public boolean returnShapes() {
429
                        return boundProvider.returnShapes();
430
                }
431

    
432
                public IGeometry getLastGeometry() {
433
                        return boundProvider.getLastGeometry();
434
                }
435

    
436
                public int getIndexOfLast() {
437
                        return lastIndex;
438
                }
439

    
440
        }// FastSpatialCheck
441

    
442

    
443
        /**
444
         * Checks if the specified features intersect with rectangle2D instances in
445
         * a precisse manner
446
         *
447
         * @author azabala
448
         *
449
         */
450

    
451
        class PrecisseSpatialCheck implements ISpatialCheck {
452
                IGeometry lastGeometry;
453
                int lastIndex;
454

    
455
                public PrecisseSpatialCheck() {
456
                }
457

    
458
                public boolean intersects(Rectangle2D extent, int featureIndex)
459
                                throws ExpansionFileReadException, ReadDriverException {
460
                        this.lastIndex = featureIndex;
461
                        this.lastGeometry = chekIfCloned(source.getShape(lastIndex));
462
                        //the spatial check is made in the original projection
463
                        boolean solution = lastGeometry.fastIntersects(rect.getMinX(),
464
                                        rect.getMinY(), rect.getWidth(), rect.getHeight());
465
                        //but the solution is returned in the new projection (if applies)
466
                        reprojectIfNecessary(lastGeometry);
467
                        return solution;
468
                }
469

    
470
                public boolean returnShapes() {
471
                        return true;
472
                }
473

    
474
                public IGeometry getLastGeometry() {
475
                        return lastGeometry;
476
                }
477

    
478
                public int getIndexOfLast() {
479
                        return lastIndex;
480
                }
481
        }
482
}