001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/io/shpapi/ShapeFile.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 53177 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.shpapi; 045 046 import java.io.ByteArrayInputStream; 047 import java.io.IOException; 048 import java.util.ArrayList; 049 import java.util.Enumeration; 050 import java.util.Hashtable; 051 import java.util.List; 052 import java.util.Map; 053 054 import org.deegree.datatypes.Types; 055 import org.deegree.io.dbaseapi.DBaseException; 056 import org.deegree.io.dbaseapi.DBaseFile; 057 import org.deegree.io.dbaseapi.DBaseIndex; 058 import org.deegree.io.dbaseapi.DBaseIndexException; 059 import org.deegree.io.dbaseapi.FieldDescriptor; 060 import org.deegree.io.rtree.HyperBoundingBox; 061 import org.deegree.io.rtree.HyperPoint; 062 import org.deegree.io.rtree.RTree; 063 import org.deegree.io.rtree.RTreeException; 064 import org.deegree.model.feature.Feature; 065 import org.deegree.model.feature.FeatureCollection; 066 import org.deegree.model.feature.FeatureFactory; 067 import org.deegree.model.feature.FeatureProperty; 068 import org.deegree.model.feature.schema.FeatureType; 069 import org.deegree.model.feature.schema.GeometryPropertyType; 070 import org.deegree.model.feature.schema.PropertyType; 071 import org.deegree.model.spatialschema.ByteUtils; 072 import org.deegree.model.spatialschema.Curve; 073 import org.deegree.model.spatialschema.Envelope; 074 import org.deegree.model.spatialschema.Geometry; 075 import org.deegree.model.spatialschema.GeometryException; 076 import org.deegree.model.spatialschema.GeometryFactory; 077 import org.deegree.model.spatialschema.JTSAdapter; 078 import org.deegree.model.spatialschema.MultiCurve; 079 import org.deegree.model.spatialschema.MultiPoint; 080 import org.deegree.model.spatialschema.MultiSurface; 081 import org.deegree.model.spatialschema.Point; 082 import org.deegree.model.spatialschema.Position; 083 import org.deegree.model.spatialschema.Ring; 084 import org.deegree.model.spatialschema.Surface; 085 import org.deegree.model.spatialschema.SurfaceInterpolationImpl; 086 087 import com.vividsolutions.jts.algorithm.CGAlgorithms; 088 089 /** 090 * Class representing an ESRI Shape File. 091 * 092 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a> 093 * @author last edited by: $Author: apoth $ 094 * 095 * @version $Revision: 9342 $, $Date: 2007-12-27 13:32:57 +0100 (Do, 27 Dez 2007) $ 096 */ 097 public class ShapeFile { 098 099 private DBaseFile dbf = null; 100 101 private SHP2WKS shpwks = new SHP2WKS(); 102 103 /* 104 * contains the dBase indexes 105 */ 106 private Hashtable<String, DBaseIndex> dBaseIndexes = new Hashtable<String, DBaseIndex>( 50 ); 107 108 /* 109 * aggregated Instance-variables 110 */ 111 private MainFile shp = null; 112 113 private RTree rti = null; 114 115 private String fileName = null; 116 117 /* 118 * indicates if a dBase-file is associated to the shape-file 119 */ 120 private boolean hasDBaseFile = true; 121 122 /* 123 * indicates if an R-tree index is associated to the shape-file 124 */ 125 private boolean hasRTreeIndex = true; 126 127 /** 128 * constructor: <BR> 129 * Construct a ShapeFile from a file name.<BR> 130 */ 131 public ShapeFile( String fileName ) throws IOException { 132 this.fileName = fileName; 133 /* 134 * initialize the MainFile 135 */ 136 shp = new MainFile( fileName ); 137 138 /* 139 * initialize the DBaseFile 140 */ 141 try { 142 dbf = new DBaseFile( fileName ); 143 } catch ( IOException e ) { 144 hasDBaseFile = false; 145 } 146 147 /* 148 * initialize the RTreeIndex 149 */ 150 try { 151 rti = new RTree( fileName + ".rti" ); 152 } catch ( RTreeException e ) { 153 hasRTreeIndex = false; 154 } 155 156 if ( hasDBaseFile ) { 157 String[] s = null; 158 try { 159 s = getProperties(); 160 } catch ( Exception e ) { 161 e.printStackTrace(); 162 } 163 for ( int i = 0; i < s.length; i++ ) { 164 try { 165 dBaseIndexes.put( s[i], new DBaseIndex( fileName + "$" + s[i] ) ); 166 } catch ( IOException e ) { 167 } 168 } 169 } 170 } 171 172 /** 173 * constructor: <BR> 174 * Construct a ShapeFile from a file name.<BR> 175 */ 176 public ShapeFile( String url, String rwflag ) throws IOException { 177 this.fileName = url; 178 shp = new MainFile( url, rwflag ); 179 // TODO: initialize dbf, rti 180 hasDBaseFile = false; 181 hasRTreeIndex = false; 182 } 183 184 /** 185 * 186 */ 187 public void close() { 188 189 shp.close(); 190 dbf.close(); 191 192 if ( rti != null ) { 193 try { 194 rti.close(); 195 } catch ( Exception e ) { 196 // should never happen 197 e.printStackTrace(); 198 } 199 } 200 201 for ( Enumeration e = dBaseIndexes.elements(); e.hasMoreElements(); ) { 202 DBaseIndex index = (DBaseIndex) e.nextElement(); 203 204 try { 205 index.close(); 206 } catch ( Exception ex ) { 207 } 208 } 209 } 210 211 /** 212 * Overrides the default feature type (which is generated from all columns in the dbase file) to 213 * allow customized naming and ordering of properties. 214 * 215 * @param ft 216 * @param ftMapping 217 */ 218 public void setFeatureType( FeatureType ft, Map<PropertyType, String> ftMapping ) { 219 dbf.setFeatureType( ft, ftMapping ); 220 } 221 222 /** 223 * returns true if a column is indexed 224 */ 225 public boolean hasDBaseIndex( String column ) { 226 DBaseIndex index = dBaseIndexes.get( column ); 227 return index != null; 228 } 229 230 /** 231 * returns true if a dBase-file is associated to the shape-file<BR> 232 */ 233 public boolean hasDBaseFile() { 234 return this.hasDBaseFile; 235 } 236 237 /** 238 * returns true if an R-tree index is associated to the shape-file<BR> 239 */ 240 public boolean hasRTreeIndex() { 241 return this.hasRTreeIndex; 242 } 243 244 /** 245 * returns the number of records within a shape-file<BR> 246 */ 247 public int getRecordNum() { 248 return shp.getRecordNum(); 249 } 250 251 /** 252 * returns the minimum bounding rectangle of all geometries<BR> 253 * within the shape-file 254 */ 255 public Envelope getFileMBR() { 256 double xmin = shp.getFileMBR().west; 257 double xmax = shp.getFileMBR().east; 258 double ymin = shp.getFileMBR().south; 259 double ymax = shp.getFileMBR().north; 260 261 return GeometryFactory.createEnvelope( xmin, ymin, xmax, ymax, null ); 262 } 263 264 /** 265 * returns the minimum bound rectangle of RecNo'th Geometrie<BR> 266 */ 267 public Envelope getMBRByRecNo( int recNo ) 268 throws IOException { 269 SHPEnvelope shpenv = shp.getRecordMBR( recNo ); 270 double xmin = shpenv.west; 271 double xmax = shpenv.east; 272 double ymin = shpenv.south; 273 double ymax = shpenv.north; 274 275 return GeometryFactory.createEnvelope( xmin, ymin, xmax, ymax, null ); 276 } 277 278 /** 279 * Returns the given entry of the shape file as a {@link Feature} instance. 280 * <p> 281 * This contains the geometry as well as the attributes stored into the dbase file. The geometry 282 * property will use a default name (app:GEOM). 283 * 284 * @param RecNo 285 * @return the given entry of the shape file as a Feature instance 286 * @throws IOException 287 * @throws HasNoDBaseFileException 288 * @throws DBaseException 289 */ 290 public Feature getFeatureByRecNo( int RecNo ) 291 throws IOException, HasNoDBaseFileException, DBaseException { 292 293 if ( !hasDBaseFile ) { 294 throw new HasNoDBaseFileException( "Exception: there is no dBase-file " + "associated to this shape-file" ); 295 } 296 297 // get feature (without geometry property) from DBaseFile 298 Feature feature = dbf.getFRow( RecNo ); 299 300 // exchange null geometries with real geometry 301 Geometry geo = getGeometryByRecNo( RecNo ); 302 GeometryPropertyType[] geoPTs = feature.getFeatureType().getGeometryProperties(); 303 for ( int i = 0; i < geoPTs.length; i++ ) { 304 FeatureProperty[] geoProp = feature.getProperties( geoPTs[i].getName() ); 305 for ( int j = 0; j < geoProp.length; j++ ) { 306 geoProp[j].setValue( geo ); 307 } 308 } 309 310 return feature; 311 } 312 313 /** 314 * returns RecNo'th Geometrie<BR> 315 */ 316 public Geometry getGeometryByRecNo( int recNo ) 317 throws IOException { 318 Geometry geom = null; 319 320 int shpType = getShapeTypeByRecNo( recNo ); 321 322 if ( shpType == ShapeConst.SHAPE_TYPE_POINT ) { 323 SHPPoint shppoint = (SHPPoint) shp.getByRecNo( recNo ); 324 geom = shpwks.transformPoint( null, shppoint ); 325 } else if ( shpType == ShapeConst.SHAPE_TYPE_MULTIPOINT ) { 326 SHPMultiPoint shpmultipoint = (SHPMultiPoint) shp.getByRecNo( recNo ); 327 Point[] points = shpwks.transformMultiPoint( null, shpmultipoint ); 328 if ( points != null ) { 329 MultiPoint mp = GeometryFactory.createMultiPoint( points ); 330 geom = mp; 331 } else { 332 geom = null; 333 } 334 } else if ( shpType == ShapeConst.SHAPE_TYPE_POLYLINE ) { 335 SHPPolyLine shppolyline = (SHPPolyLine) shp.getByRecNo( recNo ); 336 Curve[] curves = shpwks.transformPolyLine( null, shppolyline ); 337 if ( ( curves != null ) && ( curves.length > 1 ) ) { 338 // create multi curve 339 MultiCurve mc = GeometryFactory.createMultiCurve( curves ); 340 geom = mc; 341 } else if ( ( curves != null ) && ( curves.length == 1 ) ) { 342 // single curve 343 geom = curves[0]; 344 } else { 345 geom = null; 346 } 347 } else if ( shpType == ShapeConst.SHAPE_TYPE_POLYGON ) { 348 SHPPolygon shppoly = (SHPPolygon) shp.getByRecNo( recNo ); 349 Surface[] polygons = shpwks.transformPolygon( null, shppoly ); 350 if ( ( polygons != null ) && ( polygons.length > 1 ) ) { 351 // create multi surface 352 MultiSurface ms = GeometryFactory.createMultiSurface( polygons ); 353 geom = ms; 354 } else if ( ( polygons != null ) && ( polygons.length == 1 ) ) { 355 geom = polygons[0]; 356 } else { 357 geom = null; 358 } 359 } else if ( shpType == ShapeConst.SHAPE_TYPE_POLYGONZ ) { 360 361 SHPPolygon3D shppoly = (SHPPolygon3D) shp.getByRecNo( recNo ); 362 363 Surface[] polygons = shpwks.transformPolygon( null, shppoly ); 364 365 if ( ( polygons != null ) && ( polygons.length > 1 ) ) { 366 // create multi surface 367 MultiSurface ms = GeometryFactory.createMultiSurface( polygons ); 368 geom = ms; 369 } else if ( ( polygons != null ) && ( polygons.length == 1 ) ) { 370 geom = polygons[0]; 371 } else { 372 geom = null; 373 } 374 } 375 376 return geom; 377 } 378 379 /** 380 * returns the type of the RecNo'th Geometrie<BR> 381 * per definition a shape file contains onlay one shape type<BR> 382 * but null shapes are possible too!<BR> 383 */ 384 public int getShapeTypeByRecNo( int RecNo ) 385 throws IOException { 386 return shp.getShapeTypeByRecNo( RecNo ); 387 } 388 389 /** 390 * returns a int array that containts all the record numbers that matches the search operation 391 */ 392 public int[] getGeoNumbersByAttribute( String column, Comparable value ) 393 throws IOException, DBaseIndexException { 394 DBaseIndex index = dBaseIndexes.get( column ); 395 396 if ( index == null ) { 397 return null; 398 } 399 400 return index.search( value ); 401 } 402 403 /** 404 * returns a ArrayList that contains all geomeries of the shape file<BR> 405 * which mbr's are completly or partly within the rectangle r<BR> 406 * only Points, MultiPoints, PolyLines and Polygons are handled<BR> 407 */ 408 public int[] getGeoNumbersByRect( Envelope r ) 409 throws IOException { 410 SHPPoint geom = null; 411 int[] num = null; 412 int numRecs = getRecordNum(); 413 414 Envelope mbr = getFileMBR(); 415 416 if ( !mbr.intersects( r ) ) { 417 return null; 418 } 419 420 if ( hasRTreeIndex ) { 421 try { 422 // translate envelope (deegree) to bounding box (rtree) 423 HyperBoundingBox box = new HyperBoundingBox( new HyperPoint( r.getMin().getAsArray() ), 424 new HyperPoint( r.getMax().getAsArray() ) ); 425 Object[] iNumbers = rti.intersects( box ); 426 num = new int[iNumbers.length]; 427 428 for ( int i = 0; i < iNumbers.length; i++ ) 429 num[i] = ( (Integer) iNumbers[i] ).intValue(); 430 431 return num; 432 } catch ( Exception e ) { 433 e.printStackTrace(); 434 } 435 } 436 437 // for every geometry (record) within the shape file 438 // check if it's inside the search-rectangle r 439 List<Integer> numbers = new ArrayList<Integer>( 500 ); 440 for ( int i = 0; i < numRecs; i++ ) { 441 if ( getShapeTypeByRecNo( i + 1 ) == ShapeConst.SHAPE_TYPE_NULL ) { 442 } else if ( getShapeTypeByRecNo( i + 1 ) == ShapeConst.SHAPE_TYPE_POINT ) { 443 geom = (SHPPoint) shp.getByRecNo( i + 1 ); 444 445 // is the Point within the seach rectangle? 446 Position pos = GeometryFactory.createPosition( geom.x, geom.y ); 447 448 if ( r.contains( pos ) == true ) { 449 numbers.add( new Integer( i + 1 ) ); 450 } 451 } else { 452 // get minimum bounding rectangle of the i'th record 453 mbr = getMBRByRecNo( i + 1 ); 454 455 // is the i'th record a geometrie having a mbr 456 // (only for PolyLines, Polygons and MultiPoints mbrs are defined) 457 if ( mbr != null ) { 458 // if the tested rectangles are not disjunct the number of the 459 // actual record is added to the ArrayList 460 if ( mbr.intersects( r ) ) { 461 numbers.add( new Integer( i + 1 ) ); 462 } 463 } 464 } 465 } 466 467 if ( numbers.size() > 0 ) { 468 num = new int[numbers.size()]; 469 470 // put all numbers within numbers to an array 471 for ( int i = 0; i < numbers.size(); i++ ) { 472 num[i] = numbers.get( i ); 473 } 474 } 475 476 return num; 477 } // end of getGeoNumbersByRect 478 479 /** 480 * is a property unique? 481 */ 482 public boolean isUnique( String property ) { 483 DBaseIndex index = dBaseIndexes.get( property ); 484 485 if ( index == null ) { 486 return false; 487 } 488 489 return index.isUnique(); 490 } 491 492 /** 493 * returns the properties (column headers) of the dBase-file<BR> 494 * associated to the shape-file<BR> 495 */ 496 public String[] getProperties() 497 throws HasNoDBaseFileException, DBaseException { 498 if ( !hasDBaseFile ) { 499 throw new HasNoDBaseFileException( "Exception: there is no dBase-file " + "associated to this shape-file" ); 500 } 501 502 return dbf.getProperties(); 503 } 504 505 /** 506 * returns the datatype of each column of the database file<BR> 507 * associated to the shape-file<BR> 508 */ 509 public String[] getDataTypes() 510 throws HasNoDBaseFileException, DBaseException { 511 if ( !hasDBaseFile ) { 512 throw new HasNoDBaseFileException( "Exception: there is no dBase-file " + "associated to this shape-file" ); 513 } 514 515 return dbf.getDataTypes(); 516 } 517 518 /** 519 * 520 * 521 * @return 522 * 523 * @throws HasNoDBaseFileException 524 * @throws DBaseException 525 */ 526 public int[] getDataLengths() 527 throws HasNoDBaseFileException, DBaseException { 528 String[] properties = getProperties(); 529 int[] retval = new int[properties.length]; 530 531 for ( int i = 0; i < properties.length; i++ ) { 532 retval[i] = dbf.getDataLength( properties[i] ); 533 } 534 535 return retval; 536 } 537 538 /** 539 * returns the datatype of each column of the dBase associated<BR> 540 * to the shape-file specified by fields<BR> 541 */ 542 public String[] getDataTypes( String[] fields ) 543 throws HasNoDBaseFileException, DBaseException { 544 if ( !hasDBaseFile ) { 545 throw new HasNoDBaseFileException( "Exception: there is no dBase-file " + "associated to this shape-file" ); 546 } 547 548 return dbf.getDataTypes( fields ); 549 } 550 551 /** 552 * returns the number of geometries within a feature collection<BR> 553 * 554 * @param fc : 555 * featurecollection which is checked for the number geomtries<BR> 556 */ 557 private int getGeometryCount( FeatureCollection fc ) { 558 return fc.size(); 559 } 560 561 /** 562 * returns the type of the n'th feature in a featurecollection 563 * 564 * @param fc 565 * FeatureCollection. must not be empty. 566 * @param n 567 * number of the feature which should be examined starts with 0 568 */ 569 private int getGeometryType( FeatureCollection fc, int n ) { 570 Feature feature = null; 571 572 feature = fc.getFeature( n ); 573 574 Geometry[] g = feature.getGeometryPropertyValues(); 575 576 if ( ( g == null ) || ( g.length == 0 ) ) { 577 return -1; 578 } 579 580 if ( g[0] instanceof Point ) { 581 return 0; 582 } 583 584 if ( g[0] instanceof Curve ) { 585 return 1; 586 } 587 588 if ( g[0] instanceof Surface ) { 589 return 2; 590 } 591 592 if ( g[0] instanceof MultiPoint ) { 593 return 3; 594 } 595 596 if ( g[0] instanceof MultiCurve ) { 597 return 4; 598 } 599 600 if ( g[0] instanceof MultiSurface ) { 601 return 5; 602 } 603 604 return -1; 605 } 606 607 /** 608 * returns the n'th feature of a featurecollection as a Geometry<BR> 609 * 610 * @param fc : 611 * FeatureCollection<BR> 612 * @param n : 613 * number of the feature which should be returned<BR> 614 */ 615 private Geometry getFeatureAsGeometry( FeatureCollection fc, int n ) { 616 Feature feature = null; 617 618 feature = fc.getFeature( n ); 619 620 return feature.getGeometryPropertyValues()[0]; 621 } 622 623 /** 624 */ 625 public FeatureProperty[] getFeatureProperties( FeatureCollection fc, int n ) { 626 Feature feature = null; 627 628 feature = fc.getFeature( n ); 629 630 PropertyType[] ftp = feature.getFeatureType().getProperties(); 631 FeatureProperty[] fp = new FeatureProperty[ftp.length]; 632 FeatureProperty[] fp_ = feature.getProperties(); 633 634 for ( int i = 0; i < ftp.length; i++ ) { 635 fp[i] = FeatureFactory.createFeatureProperty( ftp[i].getName(), fp_[i].getValue() ); 636 } 637 638 return fp; 639 } 640 641 /** 642 */ 643 private void initDBaseFile( FeatureCollection fc ) 644 throws DBaseException { 645 FieldDescriptor[] fieldDesc = null; 646 647 // get feature properties 648 FeatureProperty[] pairs = getFeatureProperties( fc, 0 ); 649 650 // count regular fields 651 int cnt = 0; 652 FeatureType featT = fc.getFeature( 0 ).getFeatureType(); 653 PropertyType[] ftp = featT.getProperties(); 654 for ( int i = 0; i < pairs.length; i++ ) { 655 Object obj = pairs[i].getValue(); 656 657 if ( obj instanceof Object[] ) { 658 obj = ( (Object[]) obj )[0]; 659 } 660 if ( !( obj instanceof ByteArrayInputStream ) && !( obj instanceof Geometry ) ) { 661 cnt++; 662 } 663 } 664 665 // allocate memory for fielddescriptors 666 fieldDesc = new FieldDescriptor[cnt]; 667 668 // get properties names and types and create a FieldDescriptor 669 // for each properties except the geometry-property 670 cnt = 0; 671 for ( int i = 0; i < ftp.length; i++ ) { 672 int pos = ftp[i].getName().getLocalName().lastIndexOf( '.' ); 673 if ( pos < 0 ) { 674 pos = -1; 675 } 676 String s = ftp[i].getName().getLocalName().substring( pos + 1 ); 677 if ( ftp[i].getType() == Types.INTEGER ) { 678 fieldDesc[cnt++] = new FieldDescriptor( s, "N", (byte) 20, (byte) 0 ); 679 } else if ( ftp[i].getType() == Types.BIGINT ) { 680 fieldDesc[cnt++] = new FieldDescriptor( s, "N", (byte) 30, (byte) 0 ); 681 } else if ( ftp[i].getType() == Types.SMALLINT ) { 682 fieldDesc[cnt++] = new FieldDescriptor( s, "N", (byte) 4, (byte) 0 ); 683 } else if ( ftp[i].getType() == Types.CHAR ) { 684 fieldDesc[cnt++] = new FieldDescriptor( s, "C", (byte) 1, (byte) 0 ); 685 } else if ( ftp[i].getType() == Types.FLOAT ) { 686 fieldDesc[cnt++] = new FieldDescriptor( s, "N", (byte) 30, (byte) 10 ); 687 } else if ( ftp[i].getType() == Types.DOUBLE || ftp[i].getType() == Types.NUMERIC ) { 688 fieldDesc[cnt++] = new FieldDescriptor( s, "N", (byte) 30, (byte) 10 ); 689 } else if ( ftp[i].getType() == Types.VARCHAR ) { 690 fieldDesc[cnt++] = new FieldDescriptor( s, "C", (byte) 127, (byte) 0 ); 691 } else if ( ftp[i].getType() == Types.DATE ) { 692 fieldDesc[cnt++] = new FieldDescriptor( s, "D", (byte) 12, (byte) 0 ); 693 } 694 } 695 696 // initialize/create DBaseFile 697 try { 698 dbf = new DBaseFile( fileName, fieldDesc ); 699 } catch ( DBaseException e ) { 700 hasDBaseFile = false; 701 } 702 } 703 704 /** 705 * writes a OGC FeatureCollection to a ESRI shape file.<BR> 706 * all features in the collection must have the same properties.<BR> 707 * 708 * @param fc 709 * FeatureCollection. must not be null or empty. 710 * @throws Exception 711 */ 712 public void writeShape( FeatureCollection fc ) 713 throws Exception { 714 715 int nbyte = 0; 716 int geotype = -1; 717 byte shptype = -1; 718 int typ_ = getGeometryType( fc, 0 ); 719 byte[] bytearray = null; 720 IndexRecord record = null; 721 SHPEnvelope mbr = null; 722 // mbr of the whole shape file 723 SHPEnvelope shpmbr = new SHPEnvelope(); 724 725 // Set the Offset to the end of the fileHeader 726 int offset = ShapeConst.SHAPE_FILE_HEADER_LENGTH; 727 728 // initialize the dbasefile associated with the shapefile 729 initDBaseFile( fc ); 730 731 // loop throug the Geometries of the feature collection anf write them 732 // to a bytearray 733 for ( int i = 0; i < getGeometryCount( fc ); i++ ) { 734 if ( i % 1000 == 0 ) { 735 System.gc(); 736 } 737 // write i'th features properties to a ArrayList 738 PropertyType[] ftp = fc.getFeature( 0 ).getFeatureType().getProperties(); 739 ArrayList<Object> vec = new ArrayList<Object>( ftp.length ); 740 for ( int j = 0; j < ftp.length; j++ ) { 741 if ( ftp[j].getType() == Types.GEOMETRY ) 742 continue; 743 FeatureProperty fp = fc.getFeature( i ).getDefaultProperty( ftp[j].getName() ); 744 Object obj = null; 745 if ( fp != null ) { 746 obj = fp.getValue(); 747 } 748 749 if ( obj instanceof Object[] ) { 750 obj = ( (Object[]) obj )[0]; 751 } 752 753 if ( ( ftp[j].getType() == Types.INTEGER ) || ( ftp[j].getType() == Types.BIGINT ) 754 || ( ftp[j].getType() == Types.SMALLINT ) || ( ftp[j].getType() == Types.CHAR ) 755 || ( ftp[j].getType() == Types.FLOAT ) || ( ftp[j].getType() == Types.DOUBLE ) 756 || ( ftp[j].getType() == Types.NUMERIC ) || ( ftp[j].getType() == Types.VARCHAR ) 757 || ( ftp[j].getType() == Types.DATE ) ) { 758 vec.add( obj ); 759 } 760 761 } 762 763 // write the ArrayList (properties) to the dbase file 764 try { 765 dbf.setRecord( vec ); 766 } catch ( DBaseException db ) { 767 db.printStackTrace(); 768 throw new Exception( db.toString() ); 769 } 770 771 // Get Geometry Type of i'th feature 772 geotype = getGeometryType( fc, i ); 773 774 if ( geotype < 0 ) { 775 continue; 776 } 777 778 if ( ( typ_ == 0 ) || ( typ_ == 3 ) ) { 779 if ( ( geotype != 0 ) && ( geotype != 3 ) ) { 780 throw new Exception( "Not a homogen FeatureCollection" ); 781 } 782 } 783 784 if ( ( typ_ == 1 ) || ( typ_ == 4 ) ) { 785 if ( ( geotype != 1 ) && ( geotype != 4 ) ) { 786 throw new Exception( "Not a homogen FeatureCollection" ); 787 } 788 } 789 790 if ( ( typ_ == 2 ) || ( typ_ == 5 ) ) { 791 if ( ( geotype != 2 ) && ( geotype != 5 ) ) { 792 throw new Exception( "Not a homogen FeatureCollection" ); 793 } 794 } 795 796 // get wks geometrie for feature (i) and write it to a file 797 if ( geotype == 0 ) { 798 // Geometrie Type = Point 799 Point wks = (Point) getFeatureAsGeometry( fc, i ); 800 SHPPoint shppoint = new SHPPoint( wks.getPosition() ); 801 nbyte = shppoint.size(); 802 bytearray = new byte[nbyte + ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH]; 803 shppoint.writeSHPPoint( bytearray, ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH ); 804 mbr = new SHPEnvelope( shppoint, shppoint ); 805 806 if ( i == 0 ) { 807 shpmbr = mbr; 808 } 809 810 shptype = 1; 811 } else if ( geotype == 1 ) { 812 // Geometrie Type = LineString 813 Curve[] wks = new Curve[1]; 814 wks[0] = (Curve) getFeatureAsGeometry( fc, i ); 815 816 SHPPolyLine shppolyline = new SHPPolyLine( wks ); 817 nbyte = shppolyline.size(); 818 bytearray = new byte[nbyte + ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH]; 819 shppolyline.writeSHPPolyLine( bytearray, ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH ); 820 mbr = shppolyline.getEnvelope(); 821 822 if ( i == 0 ) { 823 shpmbr = mbr; 824 } 825 826 shptype = 3; 827 } else if ( geotype == 2 ) { 828 // Geometrie Type = Polygon 829 Surface[] wks = new Surface[1]; 830 wks[0] = (Surface) getFeatureAsGeometry( fc, i ); 831 validateOrientation( wks ); 832 833 SHPPolygon shppolygon = new SHPPolygon( wks ); 834 nbyte = shppolygon.size(); 835 bytearray = new byte[nbyte + ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH]; 836 shppolygon.writeSHPPolygon( bytearray, ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH ); 837 mbr = shppolygon.getEnvelope(); 838 839 if ( i == 0 ) { 840 shpmbr = mbr; 841 } 842 843 shptype = 5; 844 } else if ( geotype == 3 ) { 845 // Geometrie Type = MultiPoint 846 MultiPoint wks = (MultiPoint) getFeatureAsGeometry( fc, i ); 847 SHPMultiPoint shpmultipoint = new SHPMultiPoint( wks ); 848 nbyte = shpmultipoint.size(); 849 bytearray = new byte[nbyte + ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH]; 850 shpmultipoint.writeSHPMultiPoint( bytearray, ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH ); 851 mbr = shpmultipoint.getEnvelope(); 852 shptype = 8; 853 } else if ( geotype == 4 ) { 854 // Geometrie Type = MultiLineString 855 MultiCurve wks = (MultiCurve) getFeatureAsGeometry( fc, i ); 856 SHPPolyLine shppolyline = new SHPPolyLine( wks.getAllCurves() ); 857 nbyte = shppolyline.size(); 858 bytearray = new byte[nbyte + ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH]; 859 shppolyline.writeSHPPolyLine( bytearray, ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH ); 860 mbr = shppolyline.getEnvelope(); 861 862 if ( i == 0 ) { 863 shpmbr = mbr; 864 } 865 866 shptype = 3; 867 } else if ( geotype == 5 ) { 868 // Geometrie Type = MultiPolygon 869 MultiSurface wks = (MultiSurface) getFeatureAsGeometry( fc, i ); 870 SHPPolygon shppolygon = new SHPPolygon( wks.getAllSurfaces() ); 871 nbyte = shppolygon.size(); 872 bytearray = new byte[nbyte + ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH]; 873 shppolygon.writeSHPPolygon( bytearray, ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH ); 874 mbr = shppolygon.getEnvelope(); 875 876 if ( i == 0 ) { 877 shpmbr = mbr; 878 } 879 880 shptype = 5; 881 } 882 883 // write bytearray to the shape file 884 record = new IndexRecord( offset / 2, nbyte / 2 ); 885 886 // write recordheader to the bytearray 887 ByteUtils.writeBEInt( bytearray, 0, i ); 888 ByteUtils.writeBEInt( bytearray, 4, nbyte / 2 ); 889 890 // write record (bytearray) including recordheader to the shape file 891 shp.write( bytearray, record, mbr ); 892 893 // actualise shape file minimum boundary rectangle 894 if ( mbr.west < shpmbr.west ) { 895 shpmbr.west = mbr.west; 896 } 897 898 if ( mbr.east > shpmbr.east ) { 899 shpmbr.east = mbr.east; 900 } 901 902 if ( mbr.south < shpmbr.south ) { 903 shpmbr.south = mbr.south; 904 } 905 906 if ( mbr.north > shpmbr.north ) { 907 shpmbr.north = mbr.north; 908 } 909 910 // icrement offset for pointing at the end of the file 911 offset += ( nbyte + ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH ); 912 913 bytearray = null; 914 } 915 916 dbf.writeAllToFile(); 917 918 // write shape header 919 shp.writeHeader( offset, shptype, shpmbr ); 920 921 } 922 923 private void validateOrientation( Surface[] wks ) 924 throws GeometryException { 925 com.vividsolutions.jts.geom.Geometry jts = JTSAdapter.export( wks[0] ); 926 CGAlgorithms.isCCW( jts.getCoordinates() ); 927 if ( CGAlgorithms.isCCW( jts.getCoordinates() ) ) { 928 Position[] pos = wks[0].getSurfaceBoundary().getExteriorRing().getPositions(); 929 Position[] pos2 = new Position[pos.length]; 930 for ( int j = 0; j < pos2.length; j++ ) { 931 pos2[j] = pos[pos.length - j - 1]; 932 } 933 Position[][] iPos = null; 934 if ( wks[0].getSurfaceBoundary().getInteriorRings() != null ) { 935 Ring[] rings = wks[0].getSurfaceBoundary().getInteriorRings(); 936 iPos = new Position[rings.length][]; 937 for ( int j = 0; j < rings.length; j++ ) { 938 pos = rings[j].getPositions(); 939 iPos[j] = new Position[pos.length]; 940 for ( int k = 0; k < pos.length; k++ ) { 941 iPos[j][k] = pos[pos.length - k - 1]; 942 } 943 } 944 } 945 wks[0] = GeometryFactory.createSurface( pos2, iPos, new SurfaceInterpolationImpl(), 946 wks[0].getCoordinateSystem() ); 947 } 948 } 949 }