Statistics
| Revision:

root / trunk / libraries / libFMap / src / com / iver / cit / gvsig / fmap / drivers / featureiterators / SpatialQueryFeatureIterator.java @ 18424

History | View | Annotate | Download (11.7 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 18424 2008-02-04 07:37:14Z vcaballero $
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.IFeature;
78
import com.iver.cit.gvsig.fmap.core.IGeometry;
79
import com.iver.cit.gvsig.fmap.drivers.BoundedShapes;
80
import com.iver.cit.gvsig.fmap.layers.ReadableVectorial;
81

    
82
public class SpatialQueryFeatureIterator extends DefaultFeatureIterator {
83

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

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

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

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

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

    
132
        public Rectangle2D getRect() {
133
                return rect;
134
        }
135

    
136
        public void setRect(Rectangle2D rect) {
137
                //by design, rect Rectangle2D must be in the target reprojection
138
                //if targetReprojection != sourceReprojection, we are going to reproject
139
                //rect to the source reprojection (is faster).
140
                //once spatial check is made, result features will be reprojected in the inverse direction
141
                if(this.targetProjection != null &&
142
                   this.sourceProjection != null &&
143
                   this.targetProjection.getAbrev() != this.sourceProjection.getAbrev()){
144
                        ICoordTrans trans = targetProjection.getCT(sourceProjection);
145
                        rect = trans.convert(rect);
146
                }
147
                this.rect = rect;
148
        }
149

    
150
        public boolean hasNext() throws ReadDriverException {
151
                try {
152
                        while(true){
153
                                if(currentFeature >= source.getShapeCount())
154
                                        return false;
155
                                if(spatialChecker.intersects(rect, currentFeature)){
156
                                        //we only update the counter if spatialChecker could read the geometry
157
                                        //if it is boundedshape it doesnt read the geometry, so we need to read
158
                                        //currentFeature again
159
//                                        if(spatialChecker.returnShapes())
160
//                                                currentFeature++;
161
                                        break;
162
                                }
163
                                else
164
                                        currentFeature++;
165
                        }//while
166

    
167

    
168
                        //here, currentFeature intersects with the rectangle2d filter
169
                        IGeometry geom = null;
170
                        IFeature feat = null;
171
                        if(spatialChecker.returnShapes()){
172
                                geom = spatialChecker.getLastGeometry();
173
                        }else{
174
                                geom = source.getShape(currentFeature);
175
//                                currentFeature++;
176
                                reprojectIfNecessary(geom);
177
                        }
178
                        Value[] regAtt = getValues(currentFeature);
179
                        feat = new DefaultFeature(geom, regAtt, currentFeature + "");
180
                        nextFeature = feat;
181
                        currentFeature++;
182
                        return true;
183
                } catch (ExpansionFileReadException e) {
184
                        throw new ReadDriverException(
185
                                        "Error accediendo a datos del driver durante la iteracion",
186
                                        e);
187
                }
188
        }
189

    
190
        public IFeature next() throws ReadDriverException {
191
                return nextFeature;
192
        }
193

    
194
        /**
195
         * Classes that chekcs if a specified feature intersects with a given Rectangle2D must
196
         * implement this interface.
197
         *
198
         * This interface is necessary because there are many approach to do that
199
         * (an exact intersection, an intersection based in bounds2d, etc)
200
         *
201
         *
202
         * @author azabala
203
         *
204
         */
205
        interface ISpatialCheck {
206
                public boolean intersects(Rectangle2D extent, int featureIndex)
207
                                throws ExpansionFileReadException, ReadDriverException;
208

    
209
                /**
210
                 * Tells if this spatialcheck could return the geometry of the features
211
                 * (or if it is boundedshapes based, needs another driver access operation)
212
                 * @return
213
                 */
214
                public boolean returnShapes();
215
                /**
216
                 * Returns the last readed geometry (if the spatialcheck do that)
217
                 * @return
218
                 */
219
                public IGeometry getLastGeometry();
220

    
221
                /**
222
                 * Return the index of the last readed geometry
223
                 * @return
224
                 */
225
                public int getIndexOfLast();
226

    
227

    
228
        }
229

    
230

    
231

    
232

    
233
        /**
234
         * All classes that return the bounds Rectangle2D of a feature must
235
         * implement this interface.
236
         *
237
         * @author azabala
238
         *
239
         */
240
        interface BoundsProvider {
241
                /**
242
                 * Returns the bound of the specified feature index
243
                 *
244
                 * @param featureIndex
245
                 * @return
246
                 * @throws ExpansionFileReadException
247
                 * @throws ReadDriverException
248
                 */
249
                public Rectangle2D getBounds(int featureIndex)
250
                                throws ExpansionFileReadException, ReadDriverException;
251

    
252
                /**
253
                 * Tells if this boundsProvider could returns shapes
254
                 *
255
                 * @return
256
                 */
257
                public boolean returnShapes();
258

    
259
                /**
260
                 * Returns the last geometry readed, if the boundsProvider could do that
261
                 *
262
                 * @return
263
                 */
264
                public IGeometry getLastGeometry();
265
        }
266

    
267

    
268

    
269

    
270
        /**
271
         * BoundsProvider that uses a BoundedShapes (faster than others)
272
         *
273
         * @author azabala
274
         *
275
         */
276
        class BoundedShapesProvider implements BoundsProvider {
277
                BoundedShapes boundedShapes;
278

    
279
                BoundedShapesProvider(BoundedShapes boundedShapes) {
280
                        this.boundedShapes = boundedShapes;
281
                }
282

    
283
                public Rectangle2D getBounds(int featureIndex)
284
                                throws ExpansionFileReadException, ReadDriverException {
285
                        return boundedShapes.getShapeBounds(featureIndex);
286
                }
287

    
288
                public boolean returnShapes() {
289
                        return false;
290
                }
291

    
292
                public IGeometry getLastGeometry() {
293
                        return null;
294
                }
295

    
296
        }
297

    
298

    
299

    
300

    
301
        /**
302
         * BoundsProvider that returns feature bounds from the feature geometry
303
         *
304
         * @author azabala
305
         *
306
         */
307
        class IGeometryBoundProvider implements BoundsProvider {
308
                /**
309
                 * Adapter of a given driver from which we read the features bounds.
310
                 */
311
                ReadableVectorial source;
312

    
313
                IGeometry lastGeometry;
314

    
315
                IGeometryBoundProvider(ReadableVectorial sourceOfFeatures) {
316
                        this.source = sourceOfFeatures;
317
                }
318

    
319
                public Rectangle2D getBounds(int featureIndex)
320
                                throws ExpansionFileReadException, ReadDriverException {
321
                        lastGeometry = source.getShape(featureIndex);
322
                        if (lastGeometry ==null)
323
                                return new Rectangle2D.Double(0,0,0,0);
324
//                        reprojectIfNecessary(this.lastGeometry);
325
                        //bounds2D is in the original projection
326
                        Rectangle2D solution = lastGeometry.getBounds2D();
327
                        //the readed geometry in the specified projection
328
//                        reprojectIfNecessary(this.lastGeometry);
329
                        return solution;
330
                }
331

    
332
                public IGeometry getLastGeometry() {
333
                        reprojectIfNecessary(this.lastGeometry);
334
                        return lastGeometry;
335
                }
336

    
337
                public boolean returnShapes() {
338
                        return true;
339
                }
340

    
341
        }
342

    
343

    
344

    
345

    
346
        /**
347
         * Checks if the specified features intersects with rectangle2D instances in
348
         * a rough but fast manner.
349
         *
350
         */
351
        class FastSpatialCheck implements ISpatialCheck {
352
                BoundsProvider boundProvider;
353
                int lastIndex;
354

    
355

    
356
                FastSpatialCheck() {
357
                        try {
358
                                if(isBoundedShapesNecessary()){
359
                                        if (source instanceof BoundedShapes){
360
                                                boundProvider = new BoundedShapesProvider(
361
                                                                (BoundedShapes) source);
362
                                        }else if (source.getDriver() instanceof BoundedShapes){
363
                                                boundProvider = new BoundedShapesProvider(
364
                                                                (BoundedShapes) source.getDriver());
365
                                    }else{
366
                                            boundProvider = new IGeometryBoundProvider(source);
367
                                    }
368
                                }else{
369
                                                boundProvider = new IGeometryBoundProvider(source);
370
                                }
371
                        } catch (ExpansionFileReadException e) {
372
                                // TODO Auto-generated catch block
373
                                e.printStackTrace();
374
                                boundProvider = new IGeometryBoundProvider(source);
375
                        } catch (ReadDriverException e) {
376
                                // TODO Auto-generated catch block
377
                                e.printStackTrace();
378
                                boundProvider = new IGeometryBoundProvider(source);
379
                        }
380
                }
381

    
382

    
383
                protected boolean isBoundedShapesNecessary() throws ReadDriverException, ExpansionFileReadException {
384
                        Rectangle2D driverExtent = source.getFullExtent();
385
                        double areaExtent = rect.getWidth() * rect.getHeight();
386
                        double areaFullExtent = driverExtent.getWidth() *
387
                                                         driverExtent.getHeight();
388
                        return areaExtent < (areaFullExtent / BOUNDED_SHAPES_FACTOR);
389

    
390
                }
391

    
392
                public boolean intersects(Rectangle2D extent, int featureIndex)
393
                                throws ExpansionFileReadException, ReadDriverException {
394
                        this.lastIndex = featureIndex;
395
                        Rectangle2D featureBounds = boundProvider.getBounds(featureIndex);
396
                        return XRectangle2D.intersectInclusive(extent, featureBounds);
397
                }
398

    
399
                public BoundsProvider getBoundsProvider() {
400
                        return boundProvider;
401
                }
402

    
403
                public boolean returnShapes() {
404
                        return boundProvider.returnShapes();
405
                }
406

    
407
                public IGeometry getLastGeometry() {
408
                        return boundProvider.getLastGeometry();
409
                }
410

    
411
                public int getIndexOfLast() {
412
                        return lastIndex;
413
                }
414

    
415
        }// FastSpatialCheck
416

    
417

    
418
        /**
419
         * Checks if the specified features intersect with rectangle2D instances in
420
         * a precisse manner
421
         *
422
         * @author azabala
423
         *
424
         */
425

    
426
        class PrecisseSpatialCheck implements ISpatialCheck {
427
                IGeometry lastGeometry;
428
                int lastIndex;
429

    
430
                PrecisseSpatialCheck() {
431
                }
432

    
433
                public boolean intersects(Rectangle2D extent, int featureIndex)
434
                                throws ExpansionFileReadException, ReadDriverException {
435
                        this.lastIndex = featureIndex;
436
                        this.lastGeometry = source.getShape(lastIndex);
437
                        //the spatial check is made in the original projection
438
                        boolean solution = lastGeometry.fastIntersects(rect.getMinX(),
439
                                        rect.getMinY(), rect.getWidth(), rect.getHeight());
440
                        //but the solution is returned in the new projection (if applies)
441
                        reprojectIfNecessary(lastGeometry);
442
                        return solution;
443
                }
444

    
445
                public boolean returnShapes() {
446
                        return true;
447
                }
448

    
449
                public IGeometry getLastGeometry() {
450
                        return lastGeometry;
451
                }
452

    
453
                public int getIndexOfLast() {
454
                        return lastIndex;
455
                }
456
        }
457
}