001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/io/sdeapi/SpatialQuery.java $
002 /*---------------- FILE HEADER ------------------------------------------
003
004 This file is part of deegree.
005 Copyright (C) 2001-2008 by:
006 EXSE, Department of Geography, University of Bonn
007 http://www.giub.uni-bonn.de/deegree/
008 lat/lon GmbH
009 http://www.lat-lon.de
010
011 This library is free software; you can redistribute it and/or
012 modify it under the terms of the GNU Lesser General Public
013 License as published by the Free Software Foundation; either
014 version 2.1 of the License, or (at your option) any later version.
015
016 This library is distributed in the hope that it will be useful,
017 but WITHOUT ANY WARRANTY; without even the implied warranty of
018 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
019 Lesser General Public License for more details.
020
021 You should have received a copy of the GNU Lesser General Public
022 License along with this library; if not, write to the Free Software
023 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
024
025 Contact:
026
027 Andreas Poth
028 lat/lon GmbH
029 Aennchenstr. 19
030 53115 Bonn
031 Germany
032 E-Mail: poth@lat-lon.de
033
034 Prof. Dr. Klaus Greve
035 Department of Geography
036 University of Bonn
037 Meckenheimer Allee 166
038 53115 Bonn
039 Germany
040 E-Mail: greve@giub.uni-bonn.de
041
042
043 ---------------------------------------------------------------------------*/
044 package org.deegree.io.sdeapi;
045
046 import java.io.ByteArrayInputStream;
047 import java.util.ArrayList;
048 import java.util.Vector;
049
050 import org.deegree.datatypes.Types;
051 import org.deegree.framework.log.ILogger;
052 import org.deegree.framework.log.LoggerFactory;
053 import org.deegree.framework.util.StringTools;
054 import org.deegree.model.spatialschema.Curve;
055 import org.deegree.model.spatialschema.Envelope;
056 import org.deegree.model.spatialschema.Geometry;
057 import org.deegree.model.spatialschema.GeometryFactory;
058 import org.deegree.model.spatialschema.MultiCurve;
059 import org.deegree.model.spatialschema.MultiPoint;
060 import org.deegree.model.spatialschema.MultiSurface;
061 import org.deegree.model.spatialschema.Point;
062 import org.deegree.model.spatialschema.Position;
063 import org.deegree.model.spatialschema.Surface;
064 import org.deegree.model.spatialschema.SurfaceInterpolation;
065 import org.deegree.model.spatialschema.SurfaceInterpolationImpl;
066 import org.deegree.model.table.DefaultTable;
067 import org.deegree.model.table.Table;
068 import org.deegree.model.table.TableException;
069
070 import com.esri.sde.sdk.client.SDEPoint;
071 import com.esri.sde.sdk.client.SeColumnDefinition;
072 import com.esri.sde.sdk.client.SeConnection;
073 import com.esri.sde.sdk.client.SeException;
074 import com.esri.sde.sdk.client.SeExtent;
075 import com.esri.sde.sdk.client.SeFilter;
076 import com.esri.sde.sdk.client.SeLayer;
077 import com.esri.sde.sdk.client.SeQuery;
078 import com.esri.sde.sdk.client.SeRow;
079 import com.esri.sde.sdk.client.SeShape;
080 import com.esri.sde.sdk.client.SeShapeFilter;
081 import com.esri.sde.sdk.client.SeSqlConstruct;
082 import com.esri.sde.sdk.client.SeTable;
083
084 /**
085 * This class handles a complete ArcSDE request: If instanciated, the class can open a
086 * connection/instance of the specified ArcSDE server, set a bounding box as a spatial filter to
087 * query the defined layer. The resultset of the query contains the geometries as well as the
088 * tabular data associated with them. The table is stored as a deegree Table object whereas the
089 * geometries are stored as an array of deegree GM_Objects. Depending on the datatype of the
090 * geometries, the array of GM_Objects might be GM_Point, GM_Curve etc.
091 * <p>
092 * Some bits of sample code to create a query:
093 * <p>
094 * <code>
095 * SpatialQuery sq = new SpatialQuery();<br>
096 * try {<br>
097 * sq.openConnection(server, instance, database, user, password);<br>
098 * sq.setLayer(layer);<br>
099 * sq.setSpatialFilter(minX, minY, maxX, maxY);<br>
100 * sp.runSpatialQuery();<br>
101 * GM_Object[] deegree_gm_obj = sq.getGeometries();<br>
102 * Table deegree_table = sq.getTable();<br>
103 * sq.closeConnection();<br>
104 * } catch ( SeException sexp ) {<br>
105 * }<br>
106 * </code>
107 *
108 * @author <a href="mailto:bedel@giub.uni-bonn.de">Markus Bedel</a>
109 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
110 * @version $Revision: 9342 $ $Date: 2007-12-27 13:32:57 +0100 (Do, 27 Dez 2007) $
111 */
112 public class SpatialQuery {
113
114 private static final ILogger LOG = LoggerFactory.getLogger( SpatialQuery.class );
115
116 // Connection to SDE
117 private SeConnection conn = null;
118
119 // Currently opened Layer and associated Table
120 private SeLayer layer = null;
121
122 // Current Spatial Filter - a BoundingBox
123 private SeShape spatialFilter = null;
124
125 private SeTable table = null;
126
127 // The Query ResultObjects
128 private Geometry[] deegreeGeom = null;
129
130 /**
131 * Creates a new SpatialQuery object.
132 *
133 * @param server
134 * @param port
135 * @param database
136 * @param user
137 * @param password
138 *
139 * @throws SeException
140 */
141 public SpatialQuery( String server, int port, String database, String user, String password ) throws SeException {
142 openConnection( server, port, database, user, password );
143 }
144
145 /**
146 * Connect to the ArcSDE server <br>
147 * throws SeException
148 */
149 public void openConnection( String server, int port, String database, String user, String password )
150 throws SeException {
151 conn = new SeConnection( server, port, database, user, password );
152 }
153
154 /**
155 * Close the current connection to the ArcSDE server <br>
156 * throws SeException
157 */
158 public void closeConnection()
159 throws SeException {
160 conn.close();
161 }
162
163 /**
164 * Set a SDE layer to work on and appropriate table <br>
165 * throws SeException
166 */
167 public void setLayer( String layername )
168 throws SeException {
169 Vector layerList = conn.getLayers();
170 String spatialCol = null;
171
172 for ( int i = 0; i < layerList.size(); i++ ) {
173 SeLayer layer = (SeLayer) layerList.elementAt( i );
174 if ( layer.getQualifiedName().trim().equalsIgnoreCase( layername ) ) {
175 spatialCol = layer.getSpatialColumn();
176 break;
177 }
178 }
179
180 layer = new SeLayer( conn, layername, spatialCol );
181 table = new SeTable( conn, layer.getQualifiedName() );
182
183 }
184
185 /**
186 * Get the current SDE layer <br>
187 * returns null if it not yet set.
188 */
189 public SeLayer getLayer() {
190 return layer;
191 }
192
193 /**
194 * Set a SpatialFilter to Query (BoundingBox) <br>
195 * throws SeException
196 */
197 public void setSpatialFilter( double minx, double miny, double maxx, double maxy )
198 throws SeException {
199
200 Envelope layerBBox = GeometryFactory.createEnvelope( layer.getExtent().getMinX(), layer.getExtent().getMinY(),
201 layer.getExtent().getMaxX(), layer.getExtent().getMaxY(),
202 null );
203
204 Envelope query = GeometryFactory.createEnvelope( minx, miny, maxx, maxy, null );
205 query = query.createIntersection( layerBBox );
206
207 if ( query != null ) {
208 spatialFilter = new SeShape( layer.getCoordRef() );
209 SeExtent extent = new SeExtent( query.getMin().getX(), query.getMin().getY(), query.getMax().getX(),
210 query.getMax().getY() );
211 spatialFilter.generateRectangle( extent );
212 } else {
213 spatialFilter = null;
214 }
215
216 }
217
218 /**
219 * Get the current Spatial Filter <br>
220 * returns null if it not yet set.
221 */
222 public SeShape getSpatialFilter() {
223 return spatialFilter;
224 }
225
226 /**
227 * Get GM_Object[] containing the queried Geometries <br>
228 * returns null if no query has been done yet.
229 */
230 public Geometry[] getGeometries() {
231 return deegreeGeom;
232 }
233
234 /**
235 * Runs a spatial query against the opened layer using the specified spatial filter. <br>
236 * throws SeException
237 */
238 public Table runSpatialQuery( String[] cols )
239 throws SeException, DeegreeSeException {
240
241 Table deegreeTable = null;
242 if ( spatialFilter != null ) {
243 SeShapeFilter[] filters = new SeShapeFilter[1];
244
245 filters[0] = new SeShapeFilter( layer.getQualifiedName(), layer.getSpatialColumn(), spatialFilter,
246 SeFilter.METHOD_ENVP );
247
248 SeColumnDefinition[] tableDef = table.describe();
249 if ( cols == null || cols.length == 0 ) {
250 cols = new String[tableDef.length];
251 for ( int i = 0; i < tableDef.length; i++ ) {
252 cols[i] = tableDef[i].getName();
253 }
254 }
255
256 SeSqlConstruct sqlCons = new SeSqlConstruct( layer.getQualifiedName() );
257 SeQuery spatialQuery = new SeQuery( conn, cols, sqlCons );
258
259 spatialQuery.prepareQuery();
260 spatialQuery.setSpatialConstraints( SeQuery.SE_OPTIMIZE, false, filters );
261 spatialQuery.execute();
262
263 SeRow row = spatialQuery.fetch();
264
265 int numRows = 0;
266 if ( row != null ) {
267 int numCols = row.getNumColumns();
268 // Fetch all the features that satisfied the query
269 deegreeTable = initTable( row );
270
271 ArrayList<Geometry> list = new ArrayList<Geometry>( 20000 );
272 Object[] tableObj = null;
273
274 while ( row != null ) {
275 int colNum = 0;
276 tableObj = new Object[deegreeTable.getColumnCount()];
277
278 for ( int i = 0; i < numCols; i++ ) {
279 SeColumnDefinition colDef = row.getColumnDef( i );
280
281 if ( row.getIndicator( (short) i ) != SeRow.SE_IS_NULL_VALUE ) {
282 switch ( colDef.getType() ) {
283 case SeColumnDefinition.TYPE_INT16:
284 tableObj[colNum++] = row.getShort( i );
285 break;
286 case SeColumnDefinition.TYPE_INT32:
287 tableObj[colNum++] = row.getInteger( i );
288 break;
289 case SeColumnDefinition.TYPE_FLOAT32:
290 tableObj[colNum++] = row.getFloat( i );
291 break;
292 case SeColumnDefinition.TYPE_FLOAT64:
293 tableObj[colNum++] = row.getDouble( i );
294 break;
295 case SeColumnDefinition.TYPE_STRING:
296 tableObj[colNum++] = row.getString( i );
297 break;
298 case SeColumnDefinition.TYPE_BLOB:
299 ByteArrayInputStream bis = (ByteArrayInputStream) row.getObject( i );
300 tableObj[colNum++] = bis;
301 break;
302 case SeColumnDefinition.TYPE_DATE:
303 tableObj[colNum++] = row.getTime( i ).getTime();
304 break;
305 case SeColumnDefinition.TYPE_RASTER:
306 LOG.logInfo( colDef.getName() + " : Cant handle this" );
307 break;
308 case SeColumnDefinition.TYPE_SHAPE:
309 SeShape spVal = row.getShape( i );
310 createGeometry( spVal, list );
311 break;
312 default:
313 LOG.logInfo( "Unknown Table DataType" );
314 break;
315 } // End switch(type)
316 } // End if
317 } // End for
318
319 numRows++;
320
321 try {
322 deegreeTable.appendRow( tableObj );
323 } catch ( TableException tex ) {
324 throw new DeegreeSeException( tex.toString() );
325 }
326
327 row = spatialQuery.fetch();
328 } // End while
329 spatialQuery.close();
330
331 deegreeGeom = list.toArray( new Geometry[list.size()] );
332 } else {
333 try {
334 deegreeTable = new DefaultTable( layer.getQualifiedName(), new String[] { "NONE" },
335 new int[] { Types.VARCHAR }, 2 );
336 } catch ( Exception e ) {
337 e.printStackTrace();
338 }
339 deegreeGeom = new Geometry[0];
340 }
341 } else {
342 try {
343 deegreeTable = new DefaultTable( layer.getQualifiedName(), new String[] { "NONE" },
344 new int[] { Types.VARCHAR }, 2 );
345 } catch ( Exception e ) {
346 e.printStackTrace();
347 }
348 deegreeGeom = new Geometry[0];
349 }
350
351 return deegreeTable;
352 } // End method runSpatialQuery
353
354 /**
355 * Initialize Table object - used with first row of the SpatialQuery This method sets the
356 * TableName, TableColumnNames and their DataTypes <br>
357 * throws SeException
358 */
359 private Table initTable( SeRow row )
360 throws SeException, DeegreeSeException {
361 ArrayList<String> colNames = new ArrayList<String>( 50 );
362 ArrayList<Integer> colTypes = new ArrayList<Integer>( 50 );
363 Table deegreeTable = null;
364 SeColumnDefinition colDef = null;
365
366 for ( int i = 0; i < row.getNumColumns(); i++ ) {
367 try {
368 colDef = row.getColumnDef( i );
369 } catch ( SeException sexp ) {
370 sexp.printStackTrace();
371 throw new DeegreeSeException( sexp.toString() );
372 }
373
374 switch ( colDef.getType() ) {
375 case SeColumnDefinition.TYPE_INT16:
376 colNames.add( colDef.getName().toUpperCase() );
377 colTypes.add( new Integer( Types.SMALLINT ) );
378 break;
379 case SeColumnDefinition.TYPE_INT32:
380 colNames.add( colDef.getName().toUpperCase() );
381 colTypes.add( new Integer( Types.INTEGER ) );
382 break;
383 case SeColumnDefinition.TYPE_FLOAT32:
384 colNames.add( colDef.getName().toUpperCase() );
385 colTypes.add( new Integer( Types.FLOAT ) );
386 break;
387 case SeColumnDefinition.TYPE_FLOAT64:
388 colNames.add( colDef.getName().toUpperCase() );
389 colTypes.add( new Integer( Types.DOUBLE ) );
390 break;
391 case SeColumnDefinition.TYPE_STRING:
392 colNames.add( colDef.getName().toUpperCase() );
393 colTypes.add( new Integer( Types.VARCHAR ) );
394 break;
395 case SeColumnDefinition.TYPE_BLOB:
396 // there is an open issue with fetching blobs,
397 // look at this document:
398 // "ArcSDE 8.1 Java API - BLOB columns"
399 // http://support.esri.com/Search/KbDocument.asp?dbid=17068
400 colNames.add( colDef.getName().toUpperCase() );
401 colTypes.add( new Integer( Types.ARRAY ) );
402 break;
403 case SeColumnDefinition.TYPE_DATE:
404 colNames.add( colDef.getName().toUpperCase() );
405 colTypes.add( new Integer( Types.DATE ) );
406 break;
407 default:
408 break;
409 }
410 }
411
412 String[] colN = new String[colNames.size()];
413 colN = colNames.toArray( colN );
414
415 int[] colT = new int[colTypes.size()];
416 for ( int i = 0; i < colT.length; i++ ) {
417 colT[i] = colTypes.get( i ).intValue();
418 }
419
420 try {
421 deegreeTable = new DefaultTable( layer.getQualifiedName(), colN, colT, 20000 );
422 } catch ( TableException tex ) {
423 tex.printStackTrace();
424 throw new DeegreeSeException( tex.toString() );
425 }
426 return deegreeTable;
427 } // End Method initTable
428
429 /**
430 * CreateGeometry - used with every row of the SpatialQuery Depending on the layers' geometries
431 * datatype different operations are made to create the appropriate object. <br>
432 * Available ArcSDE ShapeTypes: <br>
433 * TYPE_POINT (impl) <br>
434 * TYPE_MULTI_POINT (impl) <br>
435 * TYPE_SIMPLE_LINE (impl) <br>
436 * TYPE_MULTI_SIMPLE_LINE (impl) <br>
437 * TYPE_LINE (impl) <br>
438 * TYPE_MULTI_LINE (impl) <br>
439 * TYPE_POLYGON (impl) <br>
440 * TYPE_MULTI_POLYGON (impl) <br>
441 * TYPE_NIL (impl)
442 *
443 * <br>
444 * throws SeException
445 */
446 private void createGeometry( SeShape shape, ArrayList<Geometry> list )
447 throws SeException, DeegreeSeException {
448
449 int shptype = shape.getType();
450
451 ArrayList al = shape.getAllPoints( SeShape.TURN_DEFAULT, true );
452 // Retrieve the array of SDEPoints
453 SDEPoint[] points = (SDEPoint[]) al.get( 0 );
454 // Retrieve the part offsets array.
455 int[] partOffset = (int[]) al.get( 1 );
456 // Retrieve the sub-part offsets array.
457 int[] subPartOffset = (int[]) al.get( 2 );
458
459 int numPoints = shape.getNumOfPoints();
460
461 int numParts = shape.getNumParts();
462
463 switch ( shptype ) {
464 // a single point
465 case SeShape.TYPE_NIL:
466 Point gmPoint = GeometryFactory.createPoint( -9E9, -9E9, null );
467 list.add( gmPoint );
468 LOG.logInfo( "Found SeShape.TYPE_NIL." );
469 LOG.logInfo( "The queried layer does not have valid geometries" );
470 break;
471 // a single point
472 case SeShape.TYPE_POINT:
473 gmPoint = GeometryFactory.createPoint( points[0].getX(), points[0].getY(), null );
474 list.add( gmPoint );
475 break;
476 // an array of points
477 case SeShape.TYPE_MULTI_POINT:
478 Point[] gmPoints = new Point[numPoints];
479
480 for ( int pt = 0; pt < numPoints; pt++ ) {
481 gmPoints[pt] = GeometryFactory.createPoint( points[pt].getX(), points[pt].getY(), null );
482 }
483
484 try {
485 MultiPoint gmMultiPoint = GeometryFactory.createMultiPoint( gmPoints );
486 list.add( gmMultiPoint );
487 } catch ( Exception gme ) {
488 gme.printStackTrace();
489 throw new DeegreeSeException( gme.toString() );
490 }
491
492 break;
493 // a single line, simple as it does not intersect itself
494 case SeShape.TYPE_SIMPLE_LINE:
495 // or a single, non-simple line
496 case SeShape.TYPE_LINE:
497
498 Position[] gmSimpleLinePosition = new Position[numPoints];
499
500 for ( int pt = 0; pt < numPoints; pt++ ) {
501 gmSimpleLinePosition[pt] = GeometryFactory.createPosition( points[pt].getX(), points[pt].getY() );
502 }
503
504 try {
505 Curve gmCurve = GeometryFactory.createCurve( gmSimpleLinePosition, null );
506 list.add( gmCurve );
507 } catch ( Exception gme ) {
508 gme.printStackTrace();
509 throw new DeegreeSeException( gme.toString() );
510 }
511
512 break;
513 // an array of lines, simple as they do not intersect with themself
514 case SeShape.TYPE_MULTI_SIMPLE_LINE:
515 // or an array of non-simple lines
516 case SeShape.TYPE_MULTI_LINE:
517
518 Curve[] gmCurves = new Curve[numParts];
519
520 for ( int partNo = 0; partNo < numParts; partNo++ ) {
521 int lastPoint = shape.getNumPoints( partNo + 1, 1 ) + partOffset[partNo];
522 Position[] gmMultiSimpleLinePosition = new Position[shape.getNumPoints( partNo + 1, 1 )];
523 int i = 0;
524
525 for ( int pt = partOffset[partNo]; pt < lastPoint; pt++ ) {
526 gmMultiSimpleLinePosition[i] = GeometryFactory.createPosition( points[pt].getX(), points[pt].getY() );
527 i++;
528 }
529
530 try {
531 gmCurves[partNo] = GeometryFactory.createCurve( gmMultiSimpleLinePosition, null );
532 } catch ( Exception gme ) {
533 gme.printStackTrace();
534 throw new DeegreeSeException( gme.toString() );
535 }
536 }
537
538 try {
539 MultiCurve gmMultiCurve = GeometryFactory.createMultiCurve( gmCurves );
540 list.add( gmMultiCurve );
541 } catch ( Exception gme ) {
542 gme.printStackTrace();
543 throw new DeegreeSeException( gme.toString() );
544 }
545
546 break;
547 // a single polygon which might contain islands
548 case SeShape.TYPE_POLYGON:
549
550 int numSubParts = shape.getNumSubParts( 1 );
551 Position[] gmPolygonExteriorRing = new Position[shape.getNumPoints( 1, 1 )];
552
553 int kk = shape.getNumPoints( 1, 1 );
554 for ( int pt = 0; pt < kk; pt++ ) {
555 gmPolygonExteriorRing[pt] = GeometryFactory.createPosition( points[pt].getX(), points[pt].getY() );
556 }
557
558 Position[][] gmPolygonInteriorRings = null;
559
560 // if it is a donut create inner rings
561 if ( numSubParts > 1 ) {
562 gmPolygonInteriorRings = new Position[numSubParts - 1][];
563
564 int j = 0;
565
566 for ( int subPartNo = 1; subPartNo < numSubParts; subPartNo++ ) {
567 int lastPoint = shape.getNumPoints( 1, subPartNo + 1 ) + subPartOffset[subPartNo];
568 Position[] gmPolygonPosition = new Position[shape.getNumPoints( 1, subPartNo + 1 )];
569 int i = 0;
570
571 for ( int pt = subPartOffset[subPartNo]; pt < lastPoint; pt++ ) {
572 gmPolygonPosition[i] = GeometryFactory.createPosition( points[pt].getX(), points[pt].getY() );
573 i++;
574 }
575
576 gmPolygonInteriorRings[j] = gmPolygonPosition;
577 j++;
578 }
579 }
580
581 try {
582 Surface gmSurface = GeometryFactory.createSurface( gmPolygonExteriorRing, gmPolygonInteriorRings,
583 new SurfaceInterpolationImpl(), null );
584 list.add( gmSurface );
585 } catch ( Exception gme ) {
586 gme.printStackTrace();
587 throw new DeegreeSeException( gme.toString() );
588 }
589
590 break;
591 // an array of polygons which might contain islands
592 case SeShape.TYPE_MULTI_POLYGON:
593
594 Surface[] gmMultiPolygonSurface = getMultiPolygon( shape, points, partOffset, subPartOffset );
595
596 try {
597 MultiSurface gmMultiSurface = GeometryFactory.createMultiSurface( gmMultiPolygonSurface );
598 list.add( gmMultiSurface );
599 } catch ( Exception gme ) {
600 gme.printStackTrace();
601 throw new DeegreeSeException( gme.toString() );
602 }
603
604 break;
605 default:
606 LOG.logInfo( "Unknown GeometryType - ID: " + shape.getType() );
607 break;
608 } // End of switch
609 } // End Method createGeometry
610
611 /**
612 * @param shape
613 * @param points
614 * @param partOffset
615 * @param subPartOffset
616 * @param numParts
617 * @return
618 * @throws SeException
619 * @throws SeWarningException
620 */
621 private Surface[] getMultiPolygon( SeShape shape, SDEPoint[] points, int[] partOffset, int[] subPartOffset )
622 throws SeException, DeegreeSeException {
623 Surface[] surfaces = new Surface[partOffset.length];
624 int hh = 0;
625 for ( int i = 0; i < partOffset.length; i++ ) {
626 // cnt = number of all rings of the current polygon (part)
627 int cnt = shape.getNumSubParts( i + 1 );
628
629 // exterior ring
630 int count = shape.getNumPoints( i + 1, 1 );
631 Position[] ex = new Position[count];
632 int off = subPartOffset[hh];
633 for ( int j = 0; j < count; j++ ) {
634 ex[j] = GeometryFactory.createPosition( points[j + off].getX(), points[j + off].getY() );
635 }
636
637 // interior ring
638 Position[][] inn = null;
639 if ( cnt > 1 ) {
640 inn = new Position[cnt - 1][];
641 }
642 hh++;
643 for ( int j = 1; j < cnt; j++ ) {
644 inn[j - 1] = new Position[shape.getNumPoints( i + 1, j + 1 )];
645 off = subPartOffset[hh];
646 for ( int k = 0; k < inn[j - 1].length; k++ ) {
647 inn[j - 1][k] = GeometryFactory.createPosition( points[j + off - 1].getX(),
648 points[j + off - 1].getY() );
649 }
650 hh++;
651 }
652
653 try {
654 SurfaceInterpolation si = new SurfaceInterpolationImpl();
655 surfaces[i] = GeometryFactory.createSurface( ex, inn, si, null );
656 } catch ( Exception e ) {
657 throw new DeegreeSeException( StringTools.stackTraceToString( e ) );
658 }
659 }
660
661 return surfaces;
662 }
663
664 } // End Class SpatialQueryEx