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 }