svn-gvsig-desktop / trunk / libraries / libFMap / src / com / iver / cit / gvsig / fmap / drivers / featureiterators / SpatialQueryFeatureIterator.java @ 28367
History | View | Annotate | Download (12 KB)
1 | 11879 | azabala | /*
|
---|---|---|---|
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$
|
||
47 | * $Log$
|
||
48 | 13873 | azabala | * 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 | 12048 | azabala | * refactor of BOUND FACTOR
|
53 | *
|
||
54 | * Revision 1.2 2007/06/06 18:03:03 azabala
|
||
55 | 12037 | azabala | * bug fixed
|
56 | *
|
||
57 | * Revision 1.1 2007/05/29 19:08:11 azabala
|
||
58 | 11879 | azabala | * 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 | 16395 | vcaballero | |
89 | 11879 | azabala | /**
|
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 | 16395 | vcaballero | |
99 | 26361 | vcaballero | private boolean isUsedNext=true; |
100 | private boolean hasNext=false; |
||
101 | |||
102 | 12048 | azabala | /**
|
103 | * Proportion of query extent area and layer extent area
|
||
104 | * to use boundedshapes capability (reading bounds without
|
||
105 | 16395 | vcaballero | * reading its geometry)
|
106 | *
|
||
107 | 12048 | azabala | */
|
108 | public static final double BOUNDED_SHAPES_FACTOR = 4.0d; |
||
109 | 11879 | azabala | |
110 | /**
|
||
111 | * Constructor.
|
||
112 | 16395 | vcaballero | *
|
113 | 11879 | azabala | * @param source
|
114 | * vectorial data source
|
||
115 | * @param sourceProj
|
||
116 | * original projection of the data (it could be null)
|
||
117 | * @param targetProj
|
||
118 | * projection of the returned features
|
||
119 | * @param fieldNames
|
||
120 | * @param spatialQuery
|
||
121 | * @throws ReadDriverException
|
||
122 | */
|
||
123 | public SpatialQueryFeatureIterator(ReadableVectorial source,
|
||
124 | IProjection sourceProj, IProjection targetProj, |
||
125 | String[] fieldNames, Rectangle2D spatialQuery, boolean fastIteration) |
||
126 | throws ReadDriverException {
|
||
127 | super(source, sourceProj, targetProj, fieldNames);
|
||
128 | setRect(spatialQuery); |
||
129 | if(fastIteration)
|
||
130 | spatialChecker = new FastSpatialCheck();
|
||
131 | else
|
||
132 | spatialChecker = new PrecisseSpatialCheck();
|
||
133 | } |
||
134 | |||
135 | public Rectangle2D getRect() { |
||
136 | return rect;
|
||
137 | } |
||
138 | |||
139 | public void setRect(Rectangle2D rect) { |
||
140 | //by design, rect Rectangle2D must be in the target reprojection
|
||
141 | //if targetReprojection != sourceReprojection, we are going to reproject
|
||
142 | //rect to the source reprojection (is faster).
|
||
143 | //once spatial check is made, result features will be reprojected in the inverse direction
|
||
144 | 16395 | vcaballero | if(this.targetProjection != null && |
145 | 11879 | azabala | this.sourceProjection != null && |
146 | this.targetProjection.getAbrev() != this.sourceProjection.getAbrev()){ |
||
147 | ICoordTrans trans = targetProjection.getCT(sourceProjection); |
||
148 | 28367 | vcaballero | try{
|
149 | rect = trans.convert(rect); |
||
150 | }catch (NullPointerException e) { |
||
151 | // TODO: handle exception
|
||
152 | } |
||
153 | 11879 | azabala | } |
154 | this.rect = rect;
|
||
155 | } |
||
156 | |||
157 | public boolean hasNext() throws ReadDriverException { |
||
158 | 26361 | vcaballero | if(!isUsedNext){
|
159 | return hasNext;
|
||
160 | } |
||
161 | 11879 | azabala | try {
|
162 | while(true){ |
||
163 | if(currentFeature >= source.getShapeCount())
|
||
164 | 26361 | vcaballero | return (hasNext=false); |
165 | 11879 | azabala | if(spatialChecker.intersects(rect, currentFeature)){
|
166 | 12037 | azabala | //we only update the counter if spatialChecker could read the geometry
|
167 | 16395 | vcaballero | //if it is boundedshape it doesnt read the geometry, so we need to read
|
168 | //currentFeature again
|
||
169 | 13873 | azabala | // if(spatialChecker.returnShapes())
|
170 | // currentFeature++;
|
||
171 | 11879 | azabala | break;
|
172 | 13873 | azabala | } |
173 | else
|
||
174 | 11879 | azabala | currentFeature++; |
175 | 13873 | azabala | }//while
|
176 | 16395 | vcaballero | |
177 | |||
178 | 11879 | azabala | //here, currentFeature intersects with the rectangle2d filter
|
179 | IGeometry geom = null;
|
||
180 | IFeature feat = null;
|
||
181 | if(spatialChecker.returnShapes()){
|
||
182 | geom = spatialChecker.getLastGeometry(); |
||
183 | }else{
|
||
184 | geom = source.getShape(currentFeature); |
||
185 | 13873 | azabala | // currentFeature++;
|
186 | 11879 | azabala | reprojectIfNecessary(geom); |
187 | } |
||
188 | Value[] regAtt = getValues(currentFeature);
|
||
189 | feat = new DefaultFeature(geom, regAtt, currentFeature + ""); |
||
190 | nextFeature = feat; |
||
191 | 13873 | azabala | currentFeature++; |
192 | 26361 | vcaballero | return (hasNext = true); |
193 | 11879 | azabala | } catch (ExpansionFileReadException e) {
|
194 | throw new ReadDriverException( |
||
195 | "Error accediendo a datos del driver durante la iteracion",
|
||
196 | e); |
||
197 | } |
||
198 | } |
||
199 | |||
200 | public IFeature next() throws ReadDriverException { |
||
201 | 26361 | vcaballero | isUsedNext=true;
|
202 | 11879 | azabala | return nextFeature;
|
203 | } |
||
204 | |||
205 | /**
|
||
206 | * Classes that chekcs if a specified feature intersects with a given Rectangle2D must
|
||
207 | * implement this interface.
|
||
208 | 16395 | vcaballero | *
|
209 | 11879 | azabala | * This interface is necessary because there are many approach to do that
|
210 | * (an exact intersection, an intersection based in bounds2d, etc)
|
||
211 | 16395 | vcaballero | *
|
212 | *
|
||
213 | 11879 | azabala | * @author azabala
|
214 | *
|
||
215 | */
|
||
216 | interface ISpatialCheck { |
||
217 | public boolean intersects(Rectangle2D extent, int featureIndex) |
||
218 | throws ExpansionFileReadException, ReadDriverException;
|
||
219 | 16395 | vcaballero | |
220 | 11879 | azabala | /**
|
221 | * Tells if this spatialcheck could return the geometry of the features
|
||
222 | * (or if it is boundedshapes based, needs another driver access operation)
|
||
223 | * @return
|
||
224 | */
|
||
225 | public boolean returnShapes(); |
||
226 | /**
|
||
227 | * Returns the last readed geometry (if the spatialcheck do that)
|
||
228 | * @return
|
||
229 | */
|
||
230 | public IGeometry getLastGeometry();
|
||
231 | 16395 | vcaballero | |
232 | 11879 | azabala | /**
|
233 | * Return the index of the last readed geometry
|
||
234 | * @return
|
||
235 | */
|
||
236 | public int getIndexOfLast(); |
||
237 | |||
238 | 16395 | vcaballero | |
239 | 11879 | azabala | } |
240 | |||
241 | 16395 | vcaballero | |
242 | |||
243 | |||
244 | 11879 | azabala | /**
|
245 | * All classes that return the bounds Rectangle2D of a feature must
|
||
246 | * implement this interface.
|
||
247 | 16395 | vcaballero | *
|
248 | 11879 | azabala | * @author azabala
|
249 | 16395 | vcaballero | *
|
250 | 11879 | azabala | */
|
251 | interface BoundsProvider { |
||
252 | /**
|
||
253 | * Returns the bound of the specified feature index
|
||
254 | 16395 | vcaballero | *
|
255 | 11879 | azabala | * @param featureIndex
|
256 | * @return
|
||
257 | * @throws ExpansionFileReadException
|
||
258 | * @throws ReadDriverException
|
||
259 | */
|
||
260 | public Rectangle2D getBounds(int featureIndex) |
||
261 | throws ExpansionFileReadException, ReadDriverException;
|
||
262 | |||
263 | /**
|
||
264 | * Tells if this boundsProvider could returns shapes
|
||
265 | 16395 | vcaballero | *
|
266 | 11879 | azabala | * @return
|
267 | */
|
||
268 | public boolean returnShapes(); |
||
269 | |||
270 | /**
|
||
271 | * Returns the last geometry readed, if the boundsProvider could do that
|
||
272 | 16395 | vcaballero | *
|
273 | 11879 | azabala | * @return
|
274 | */
|
||
275 | public IGeometry getLastGeometry();
|
||
276 | } |
||
277 | |||
278 | 16395 | vcaballero | |
279 | |||
280 | |||
281 | 11879 | azabala | /**
|
282 | * BoundsProvider that uses a BoundedShapes (faster than others)
|
||
283 | 16395 | vcaballero | *
|
284 | 11879 | azabala | * @author azabala
|
285 | 16395 | vcaballero | *
|
286 | 11879 | azabala | */
|
287 | class BoundedShapesProvider implements BoundsProvider { |
||
288 | BoundedShapes boundedShapes; |
||
289 | |||
290 | BoundedShapesProvider(BoundedShapes boundedShapes) { |
||
291 | this.boundedShapes = boundedShapes;
|
||
292 | } |
||
293 | |||
294 | public Rectangle2D getBounds(int featureIndex) |
||
295 | throws ExpansionFileReadException, ReadDriverException {
|
||
296 | return boundedShapes.getShapeBounds(featureIndex);
|
||
297 | } |
||
298 | |||
299 | public boolean returnShapes() { |
||
300 | return false; |
||
301 | } |
||
302 | |||
303 | public IGeometry getLastGeometry() {
|
||
304 | return null; |
||
305 | } |
||
306 | |||
307 | } |
||
308 | |||
309 | 16395 | vcaballero | |
310 | |||
311 | |||
312 | 11879 | azabala | /**
|
313 | * BoundsProvider that returns feature bounds from the feature geometry
|
||
314 | 16395 | vcaballero | *
|
315 | 11879 | azabala | * @author azabala
|
316 | 16395 | vcaballero | *
|
317 | 11879 | azabala | */
|
318 | class IGeometryBoundProvider implements BoundsProvider { |
||
319 | /**
|
||
320 | * Adapter of a given driver from which we read the features bounds.
|
||
321 | */
|
||
322 | ReadableVectorial source; |
||
323 | |||
324 | IGeometry lastGeometry; |
||
325 | |||
326 | IGeometryBoundProvider(ReadableVectorial sourceOfFeatures) { |
||
327 | this.source = sourceOfFeatures;
|
||
328 | } |
||
329 | |||
330 | public Rectangle2D getBounds(int featureIndex) |
||
331 | throws ExpansionFileReadException, ReadDriverException {
|
||
332 | lastGeometry = source.getShape(featureIndex); |
||
333 | 16395 | vcaballero | if (lastGeometry ==null) |
334 | return new Rectangle2D.Double(0,0,0,0); |
||
335 | 18424 | vcaballero | // reprojectIfNecessary(this.lastGeometry);
|
336 | 11879 | azabala | //bounds2D is in the original projection
|
337 | Rectangle2D solution = lastGeometry.getBounds2D();
|
||
338 | //the readed geometry in the specified projection
|
||
339 | 17204 | vcaballero | // reprojectIfNecessary(this.lastGeometry);
|
340 | 11879 | azabala | return solution;
|
341 | } |
||
342 | |||
343 | public IGeometry getLastGeometry() {
|
||
344 | 18424 | vcaballero | reprojectIfNecessary(this.lastGeometry);
|
345 | 11879 | azabala | return lastGeometry;
|
346 | } |
||
347 | |||
348 | public boolean returnShapes() { |
||
349 | return true; |
||
350 | } |
||
351 | |||
352 | } |
||
353 | |||
354 | 16395 | vcaballero | |
355 | |||
356 | |||
357 | 11879 | azabala | /**
|
358 | * Checks if the specified features intersects with rectangle2D instances in
|
||
359 | * a rough but fast manner.
|
||
360 | 16395 | vcaballero | *
|
361 | 11879 | azabala | */
|
362 | class FastSpatialCheck implements ISpatialCheck { |
||
363 | BoundsProvider boundProvider; |
||
364 | int lastIndex;
|
||
365 | 16395 | vcaballero | |
366 | |||
367 | 26361 | vcaballero | public FastSpatialCheck() {
|
368 | 12037 | azabala | try {
|
369 | 16395 | vcaballero | if(isBoundedShapesNecessary()){
|
370 | 12037 | azabala | if (source instanceof BoundedShapes){ |
371 | boundProvider = new BoundedShapesProvider(
|
||
372 | (BoundedShapes) source); |
||
373 | }else if (source.getDriver() instanceof BoundedShapes){ |
||
374 | boundProvider = new BoundedShapesProvider(
|
||
375 | (BoundedShapes) source.getDriver()); |
||
376 | 16792 | vcaballero | }else{
|
377 | boundProvider = new IGeometryBoundProvider(source);
|
||
378 | 12037 | azabala | } |
379 | }else{
|
||
380 | boundProvider = new IGeometryBoundProvider(source);
|
||
381 | } |
||
382 | } catch (ExpansionFileReadException e) {
|
||
383 | // TODO Auto-generated catch block
|
||
384 | e.printStackTrace(); |
||
385 | 11879 | azabala | boundProvider = new IGeometryBoundProvider(source);
|
386 | 12037 | azabala | } catch (ReadDriverException e) {
|
387 | // TODO Auto-generated catch block
|
||
388 | e.printStackTrace(); |
||
389 | boundProvider = new IGeometryBoundProvider(source);
|
||
390 | } |
||
391 | 11879 | azabala | } |
392 | 16395 | vcaballero | |
393 | |||
394 | 12037 | azabala | protected boolean isBoundedShapesNecessary() throws ReadDriverException, ExpansionFileReadException { |
395 | Rectangle2D driverExtent = source.getFullExtent();
|
||
396 | double areaExtent = rect.getWidth() * rect.getHeight();
|
||
397 | double areaFullExtent = driverExtent.getWidth() *
|
||
398 | driverExtent.getHeight(); |
||
399 | return areaExtent < (areaFullExtent / BOUNDED_SHAPES_FACTOR);
|
||
400 | 11879 | azabala | |
401 | 12037 | azabala | } |
402 | |||
403 | 11879 | azabala | public boolean intersects(Rectangle2D extent, int featureIndex) |
404 | throws ExpansionFileReadException, ReadDriverException {
|
||
405 | this.lastIndex = featureIndex;
|
||
406 | Rectangle2D featureBounds = boundProvider.getBounds(featureIndex);
|
||
407 | return XRectangle2D.intersectInclusive(extent, featureBounds);
|
||
408 | } |
||
409 | |||
410 | public BoundsProvider getBoundsProvider() {
|
||
411 | return boundProvider;
|
||
412 | } |
||
413 | |||
414 | public boolean returnShapes() { |
||
415 | return boundProvider.returnShapes();
|
||
416 | } |
||
417 | |||
418 | public IGeometry getLastGeometry() {
|
||
419 | return boundProvider.getLastGeometry();
|
||
420 | } |
||
421 | |||
422 | public int getIndexOfLast() { |
||
423 | return lastIndex;
|
||
424 | } |
||
425 | |||
426 | }// FastSpatialCheck
|
||
427 | |||
428 | 16395 | vcaballero | |
429 | 11879 | azabala | /**
|
430 | * Checks if the specified features intersect with rectangle2D instances in
|
||
431 | * a precisse manner
|
||
432 | 16395 | vcaballero | *
|
433 | 11879 | azabala | * @author azabala
|
434 | 16395 | vcaballero | *
|
435 | 11879 | azabala | */
|
436 | |||
437 | class PrecisseSpatialCheck implements ISpatialCheck { |
||
438 | IGeometry lastGeometry; |
||
439 | int lastIndex;
|
||
440 | |||
441 | 26361 | vcaballero | public PrecisseSpatialCheck() {
|
442 | 11879 | azabala | } |
443 | |||
444 | public boolean intersects(Rectangle2D extent, int featureIndex) |
||
445 | throws ExpansionFileReadException, ReadDriverException {
|
||
446 | this.lastIndex = featureIndex;
|
||
447 | this.lastGeometry = source.getShape(lastIndex);
|
||
448 | //the spatial check is made in the original projection
|
||
449 | boolean solution = lastGeometry.fastIntersects(rect.getMinX(),
|
||
450 | rect.getMinY(), rect.getWidth(), rect.getHeight()); |
||
451 | //but the solution is returned in the new projection (if applies)
|
||
452 | reprojectIfNecessary(lastGeometry); |
||
453 | return solution;
|
||
454 | } |
||
455 | |||
456 | public boolean returnShapes() { |
||
457 | return true; |
||
458 | } |
||
459 | |||
460 | public IGeometry getLastGeometry() {
|
||
461 | return lastGeometry;
|
||
462 | } |
||
463 | |||
464 | public int getIndexOfLast() { |
||
465 | return lastIndex;
|
||
466 | } |
||
467 | } |
||
468 | } |