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