001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/io/shpapi/ShapeFile.java $ 002 /*---------------- FILE HEADER ------------------------------------------ 003 004 This file is part of deegree. 005 Copyright (C) 2001-2007 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: aschmitz $ 094 * 095 * @version $Revision: 7883 $, $Date: 2007-08-01 13:36:29 +0200 (Mi, 01 Aug 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 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 public void writeShape( FeatureCollection fc ) 709 throws Exception { 710 711 int nbyte = 0; 712 int geotype = -1; 713 byte shptype = -1; 714 int typ_ = getGeometryType( fc, 0 ); 715 byte[] bytearray = null; 716 IndexRecord record = null; 717 SHPEnvelope mbr = null; 718 // mbr of the whole shape file 719 SHPEnvelope shpmbr = new SHPEnvelope(); 720 721 // Set the Offset to the end of the fileHeader 722 int offset = ShapeConst.SHAPE_FILE_HEADER_LENGTH; 723 724 // initialize the dbasefile associated with the shapefile 725 initDBaseFile( fc ); 726 727 // loop throug the Geometries of the feature collection anf write them 728 // to a bytearray 729 for ( int i = 0; i < getGeometryCount( fc ); i++ ) { 730 if ( i % 1000 == 0 ) { 731 System.gc(); 732 } 733 // write i'th features properties to a ArrayList 734 PropertyType[] ftp = fc.getFeature( 0 ).getFeatureType().getProperties(); 735 ArrayList<Object> vec = new ArrayList<Object>( ftp.length ); 736 for ( int j = 0; j < ftp.length; j++ ) { 737 if ( ftp[j].getType() == Types.GEOMETRY ) 738 continue; 739 FeatureProperty fp = fc.getFeature( i ).getDefaultProperty( ftp[j].getName() ); 740 Object obj = null; 741 if ( fp != null ) { 742 obj = fp.getValue(); 743 } 744 745 if ( obj instanceof Object[] ) { 746 obj = ( (Object[]) obj )[0]; 747 } 748 749 if ( ( ftp[j].getType() == Types.INTEGER ) || ( ftp[j].getType() == Types.BIGINT ) 750 || ( ftp[j].getType() == Types.SMALLINT ) || ( ftp[j].getType() == Types.CHAR ) 751 || ( ftp[j].getType() == Types.FLOAT ) || ( ftp[j].getType() == Types.DOUBLE ) 752 || ( ftp[j].getType() == Types.NUMERIC ) || ( ftp[j].getType() == Types.VARCHAR ) 753 || ( ftp[j].getType() == Types.DATE ) ) { 754 vec.add( obj ); 755 } 756 757 } 758 759 // write the ArrayList (properties) to the dbase file 760 try { 761 dbf.setRecord( vec ); 762 } catch ( DBaseException db ) { 763 db.printStackTrace(); 764 throw new Exception( db.toString() ); 765 } 766 767 // Get Geometry Type of i'th feature 768 geotype = getGeometryType( fc, i ); 769 770 if ( geotype < 0 ) { 771 continue; 772 } 773 774 if ( ( typ_ == 0 ) || ( typ_ == 3 ) ) { 775 if ( ( geotype != 0 ) && ( geotype != 3 ) ) { 776 throw new Exception( "Not a homogen FeatureCollection" ); 777 } 778 } 779 780 if ( ( typ_ == 1 ) || ( typ_ == 4 ) ) { 781 if ( ( geotype != 1 ) && ( geotype != 4 ) ) { 782 throw new Exception( "Not a homogen FeatureCollection" ); 783 } 784 } 785 786 if ( ( typ_ == 2 ) || ( typ_ == 5 ) ) { 787 if ( ( geotype != 2 ) && ( geotype != 5 ) ) { 788 throw new Exception( "Not a homogen FeatureCollection" ); 789 } 790 } 791 792 // get wks geometrie for feature (i) and write it to a file 793 if ( geotype == 0 ) { 794 // Geometrie Type = Point 795 Point wks = (Point) getFeatureAsGeometry( fc, i ); 796 SHPPoint shppoint = new SHPPoint( wks.getPosition() ); 797 nbyte = shppoint.size(); 798 bytearray = new byte[nbyte + ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH]; 799 shppoint.writeSHPPoint( bytearray, ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH ); 800 mbr = new SHPEnvelope( shppoint, shppoint ); 801 802 if ( i == 0 ) { 803 shpmbr = mbr; 804 } 805 806 shptype = 1; 807 } else if ( geotype == 1 ) { 808 // Geometrie Type = LineString 809 Curve[] wks = new Curve[1]; 810 wks[0] = (Curve) getFeatureAsGeometry( fc, i ); 811 812 SHPPolyLine shppolyline = new SHPPolyLine( wks ); 813 nbyte = shppolyline.size(); 814 bytearray = new byte[nbyte + ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH]; 815 shppolyline.writeSHPPolyLine( bytearray, ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH ); 816 mbr = shppolyline.getEnvelope(); 817 818 if ( i == 0 ) { 819 shpmbr = mbr; 820 } 821 822 shptype = 3; 823 } else if ( geotype == 2 ) { 824 // Geometrie Type = Polygon 825 Surface[] wks = new Surface[1]; 826 wks[0] = (Surface) getFeatureAsGeometry( fc, i ); 827 validateOrientation( wks ); 828 829 SHPPolygon shppolygon = new SHPPolygon( wks ); 830 nbyte = shppolygon.size(); 831 bytearray = new byte[nbyte + ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH]; 832 shppolygon.writeSHPPolygon( bytearray, ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH ); 833 mbr = shppolygon.getEnvelope(); 834 835 if ( i == 0 ) { 836 shpmbr = mbr; 837 } 838 839 shptype = 5; 840 } else if ( geotype == 3 ) { 841 // Geometrie Type = MultiPoint 842 MultiPoint wks = (MultiPoint) getFeatureAsGeometry( fc, i ); 843 SHPMultiPoint shpmultipoint = new SHPMultiPoint( wks ); 844 nbyte = shpmultipoint.size(); 845 bytearray = new byte[nbyte + ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH]; 846 shpmultipoint.writeSHPMultiPoint( bytearray, ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH ); 847 mbr = shpmultipoint.getEnvelope(); 848 shptype = 8; 849 } else if ( geotype == 4 ) { 850 // Geometrie Type = MultiLineString 851 MultiCurve wks = (MultiCurve) getFeatureAsGeometry( fc, i ); 852 SHPPolyLine shppolyline = new SHPPolyLine( wks.getAllCurves() ); 853 nbyte = shppolyline.size(); 854 bytearray = new byte[nbyte + ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH]; 855 shppolyline.writeSHPPolyLine( bytearray, ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH ); 856 mbr = shppolyline.getEnvelope(); 857 858 if ( i == 0 ) { 859 shpmbr = mbr; 860 } 861 862 shptype = 3; 863 } else if ( geotype == 5 ) { 864 // Geometrie Type = MultiPolygon 865 MultiSurface wks = (MultiSurface) getFeatureAsGeometry( fc, i ); 866 SHPPolygon shppolygon = new SHPPolygon( wks.getAllSurfaces() ); 867 nbyte = shppolygon.size(); 868 bytearray = new byte[nbyte + ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH]; 869 shppolygon.writeSHPPolygon( bytearray, ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH ); 870 mbr = shppolygon.getEnvelope(); 871 872 if ( i == 0 ) { 873 shpmbr = mbr; 874 } 875 876 shptype = 5; 877 } 878 879 // write bytearray to the shape file 880 record = new IndexRecord( offset / 2, nbyte / 2 ); 881 882 // write recordheader to the bytearray 883 ByteUtils.writeBEInt( bytearray, 0, i ); 884 ByteUtils.writeBEInt( bytearray, 4, nbyte / 2 ); 885 886 // write record (bytearray) including recordheader to the shape file 887 shp.write( bytearray, record, mbr ); 888 889 // actualise shape file minimum boundary rectangle 890 if ( mbr.west < shpmbr.west ) { 891 shpmbr.west = mbr.west; 892 } 893 894 if ( mbr.east > shpmbr.east ) { 895 shpmbr.east = mbr.east; 896 } 897 898 if ( mbr.south < shpmbr.south ) { 899 shpmbr.south = mbr.south; 900 } 901 902 if ( mbr.north > shpmbr.north ) { 903 shpmbr.north = mbr.north; 904 } 905 906 // icrement offset for pointing at the end of the file 907 offset += ( nbyte + ShapeConst.SHAPE_FILE_RECORD_HEADER_LENGTH ); 908 909 bytearray = null; 910 } 911 912 dbf.writeAllToFile(); 913 914 // write shape header 915 shp.writeHeader( offset, shptype, shpmbr ); 916 917 } 918 919 private void validateOrientation( Surface[] wks ) 920 throws GeometryException { 921 com.vividsolutions.jts.geom.Geometry jts = JTSAdapter.export( wks[0] ); 922 CGAlgorithms.isCCW( jts.getCoordinates() ); 923 if ( CGAlgorithms.isCCW( jts.getCoordinates() ) ) { 924 Position[] pos = wks[0].getSurfaceBoundary().getExteriorRing().getPositions(); 925 Position[] pos2 = new Position[pos.length]; 926 for ( int j = 0; j < pos2.length; j++ ) { 927 pos2[j] = pos[pos.length - j - 1]; 928 } 929 Position[][] iPos = null; 930 if ( wks[0].getSurfaceBoundary().getInteriorRings() != null ) { 931 Ring[] rings = wks[0].getSurfaceBoundary().getInteriorRings(); 932 iPos = new Position[rings.length][]; 933 for ( int j = 0; j < rings.length; j++ ) { 934 pos = rings[j].getPositions(); 935 iPos[j] = new Position[pos.length]; 936 for ( int k = 0; k < pos.length; k++ ) { 937 iPos[j][k] = pos[pos.length - k - 1]; 938 } 939 } 940 } 941 wks[0] = GeometryFactory.createSurface( pos2, iPos, new SurfaceInterpolationImpl(), 942 wks[0].getCoordinateSystem() ); 943 } 944 } 945 }