001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/io/shpapi/shape_new/ShapePolygon.java $
002    /*----------------    FILE HEADER  ------------------------------------------
003     This file is part of deegree.
004     Copyright (C) 2001-2008 by:
005     Department of Geography, University of Bonn
006     http://www.giub.uni-bonn.de/deegree/
007     lat/lon GmbH
008     http://www.lat-lon.de
009    
010     This library is free software; you can redistribute it and/or
011     modify it under the terms of the GNU Lesser General Public
012     License as published by the Free Software Foundation; either
013     version 2.1 of the License, or (at your option) any later version.
014    
015     This library is distributed in the hope that it will be useful,
016     but WITHOUT ANY WARRANTY; without even the implied warranty of
017     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
018     Lesser General Public License for more details.
019    
020     You should have received a copy of the GNU Lesser General Public
021     License along with this library; if not, write to the Free Software
022     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
023    
024     Contact:
025    
026     Andreas Poth
027     lat/lon GmbH
028     Aennchenstr. 19
029     53177 Bonn
030     Germany
031     E-Mail: poth@lat-lon.de
032    
033     Prof. Dr. Klaus Greve
034     Department of Geography
035     University of Bonn
036     Meckenheimer Allee 166
037     53115 Bonn
038     Germany
039     E-Mail: greve@giub.uni-bonn.de
040    
041     ---------------------------------------------------------------------------*/
042    package org.deegree.io.shpapi.shape_new;
043    
044    import java.util.Arrays;
045    import java.util.Collections;
046    import java.util.List;
047    
048    import org.deegree.model.spatialschema.ByteUtils;
049    import org.deegree.model.spatialschema.Curve;
050    import org.deegree.model.spatialschema.Geometry;
051    import org.deegree.model.spatialschema.GeometryException;
052    import org.deegree.model.spatialschema.GeometryFactory;
053    import org.deegree.model.spatialschema.Position;
054    
055    import com.vividsolutions.jts.algorithm.CGAlgorithms;
056    import com.vividsolutions.jts.geom.Coordinate;
057    
058    /**
059     * <code>ShapePolygon</code> corresponds to the Polygon, PolygonM and PolygonZ shapes of the
060     * shapefile spec.
061     * 
062     * @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a>
063     * @author last edited by: $Author: apoth $
064     * 
065     * @version $Revision: 9342 $, $Date: 2007-12-27 13:32:57 +0100 (Do, 27 Dez 2007) $
066     */
067    public class ShapePolygon extends ShapePolyline {
068    
069        private boolean normalized;
070    
071        /**
072         * Creates a new Polygon/M/Z.
073         * 
074         * @param z
075         * @param m
076         */
077        public ShapePolygon( boolean z, boolean m ) {
078            super( z, m );
079        }
080    
081        /**
082         * Creates a PolygonZ from deegree Curves.
083         * 
084         * @param curves
085         */
086        public ShapePolygon( List<Curve> curves ) {
087            super( curves );
088        }
089    
090        private Coordinate[] getAsCoordinates( ShapePoint[] ps ) {
091            Coordinate[] cs = new Coordinate[ps.length];
092    
093            for ( int i = 0; i < cs.length; ++i ) {
094                cs[i] = ps[i].export();
095            }
096    
097            return cs;
098        }
099    
100        /**
101         * Normalizes the orientation etc. of this polygon as required by the shapefile spec. This means
102         * (to my understanding), that the outer ring runs clockwise while the inner rings run
103         * counter-clockwise.
104         */
105        public void normalize() {
106            if ( normalized ) {
107                return;
108            }
109    
110            normalized = true;
111    
112            Coordinate[] cs = getAsCoordinates( points[0] );
113    
114            if ( CGAlgorithms.isCCW( cs ) ) {
115                List<ShapePoint> list = Arrays.asList( points[0] );
116                Collections.reverse( list );
117                points[0] = list.toArray( new ShapePoint[points[0].length] );
118            }
119    
120            for ( int i = 1; i < points.length; ++i ) {
121                cs = getAsCoordinates( points[i] );
122                if ( !CGAlgorithms.isCCW( cs ) ) {
123                    List<ShapePoint> list = Arrays.asList( points[i] );
124                    Collections.reverse( list );
125                    points[i] = list.toArray( new ShapePoint[points[i].length] );
126                }
127            }
128        }
129    
130        /*
131         * (non-Javadoc)
132         * 
133         * @see org.deegree.io.shpapi.Shape#read(byte[], int)
134         */
135        @Override
136        public int read( byte[] bytes, int offset ) {
137            int off = offset;
138    
139            int type = ByteUtils.readLEInt( bytes, off );
140            off += 4;
141    
142            if ( type == ShapeFile.NULL ) {
143                return off;
144            }
145    
146            if ( type == ShapeFile.POLYGON ) {
147                return readPolyline( bytes, off );
148            }
149    
150            if ( type == ShapeFile.POLYGONZ ) {
151                return readPolylineZ( bytes, off );
152            }
153    
154            if ( type == ShapeFile.POLYGONM ) {
155                return readPolylineM( bytes, off );
156            }
157    
158            return -1;
159        }
160    
161        /**
162         * Note that the normalize method will be called automatically, if it has not been called
163         * before.
164         * 
165         * @see org.deegree.io.shpapi.shape_new.Shape#write(byte[], int)
166         */
167        @Override
168        public int write( byte[] bytes, int offset ) {
169            if ( !normalized ) {
170                normalize();
171            }
172            if ( isZ ) {
173                ByteUtils.writeLEInt( bytes, offset, ShapeFile.POLYGONZ );
174                return writePolylineZ( bytes, offset + 4 );
175            }
176            if ( isM ) {
177                ByteUtils.writeLEInt( bytes, offset, ShapeFile.POLYGONM );
178                return writePolylineM( bytes, offset + 4 );
179            }
180            ByteUtils.writeLEInt( bytes, offset, ShapeFile.POLYGON );
181            return writePolyline( bytes, offset + 4 );
182        }
183    
184        /*
185         * (non-Javadoc)
186         * 
187         * @see org.deegree.io.shpapi.shape_new.Shape#getType()
188         */
189        @Override
190        public int getType() {
191            if ( isZ ) {
192                return ShapeFile.POLYGONZ;
193            }
194            if ( isM ) {
195                return ShapeFile.POLYGONM;
196            }
197            return ShapeFile.POLYGON;
198        }
199    
200        /**
201         * This creates a Surface object.
202         */
203        @Override
204        public Geometry getGeometry()
205                                throws ShapeGeometryException {
206            if ( points == null ) {
207                return null;
208            }
209            try {
210                Position[] outer = new Position[points[0].length];
211    
212                Position[][] inner = new Position[points.length - 1][];
213    
214                for ( int i = 0; i < points[0].length; ++i ) {
215                    if ( isZ ) {
216                        outer[i] = GeometryFactory.createPosition( points[0][i].x, points[0][i].y, points[0][i].z );
217                    } else {
218                        outer[i] = GeometryFactory.createPosition( points[0][i].x, points[0][i].y );
219                    }
220                }
221    
222                for ( int k = 1; k < points.length; ++k ) {
223                    inner[k - 1] = new Position[points[k].length];
224                    for ( int i = 0; i < points[k].length; ++i ) {
225                        if ( isZ ) {
226                            inner[k - 1][i] = GeometryFactory.createPosition( points[k][i].x, points[k][i].y,
227                                                                              points[k][i].z );
228                        } else {
229                            inner[k - 1][i] = GeometryFactory.createPosition( points[k][i].x, points[k][i].y );
230                        }
231                    }
232                }
233    
234                // not sure if interpolation can be null
235                return GeometryFactory.createSurface( outer, inner, null, null );
236            } catch ( GeometryException e ) {
237                throw new ShapeGeometryException( "Surface could not be constructed from Polygon.", e );
238            }
239        }
240    
241    }