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