001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/io/shpapi/SHP2WKS.java $
002    /*----------------    FILE HEADER  ------------------------------------------
003    
004     This file is part of deegree.
005     Copyright (C) 2001-2008 by:
006     EXSE, Department of Geography, University of Bonn
007     http://www.giub.uni-bonn.de/deegree/
008     lat/lon GmbH
009     http://www.lat-lon.de
010    
011     This library is free software; you can redistribute it and/or
012     modify it under the terms of the GNU Lesser General Public
013     License as published by the Free Software Foundation; either
014     version 2.1 of the License, or (at your option) any later version.
015    
016     This library is distributed in the hope that it will be useful,
017     but WITHOUT ANY WARRANTY; without even the implied warranty of
018     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
019     Lesser General Public License for more details.
020    
021     You should have received a copy of the GNU Lesser General Public
022     License along with this library; if not, write to the Free Software
023     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
024    
025     Contact:
026    
027     Andreas Poth
028     lat/lon GmbH
029     Aennchenstr. 19
030     53177 Bonn
031     Germany
032     E-Mail: poth@lat-lon.de
033    
034     Prof. Dr. Klaus Greve
035     Department of Geography
036     University of Bonn
037     Meckenheimer Allee 166
038     53115 Bonn
039     Germany
040     E-Mail: greve@giub.uni-bonn.de
041    
042     
043     ---------------------------------------------------------------------------*/
044    package org.deegree.io.shpapi;
045    
046    import java.util.ArrayList;
047    
048    import org.deegree.model.crs.CoordinateSystem;
049    import org.deegree.model.spatialschema.Curve;
050    import org.deegree.model.spatialschema.CurveSegment;
051    import org.deegree.model.spatialschema.GeometryFactory;
052    import org.deegree.model.spatialschema.Point;
053    import org.deegree.model.spatialschema.Position;
054    import org.deegree.model.spatialschema.Surface;
055    import org.deegree.model.spatialschema.SurfaceInterpolation;
056    import org.deegree.model.spatialschema.SurfaceInterpolationImpl;
057    
058    /**
059     * the class SHP2WKS transforms a polygon structure read from a shape-file<BR>
060     * into a WKSLinearPolygon specified by the sf-specifications<BR>
061     * 
062     * @version $Revision: 9342 $
063     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
064     * @author last edited by: $Author: apoth $
065     *
066     * @version 1.0. $Revision: 9342 $, $Date: 2007-12-27 13:32:57 +0100 (Do, 27 Dez 2007) $
067     *
068     * @since 2.0
069     */
070    public class SHP2WKS {
071    
072        /**
073         * method: Point transformPoint(CS_CoordinateSystem srs,<BR>
074         *                                 SHPPoint shppoint))<BR>
075         * transforms a SHPPoint to a WKSGeometry<BR>
076         * gets a point that should be transformed<BR>
077         */
078        public Point transformPoint( CoordinateSystem crs, SHPPoint shppoint ) {
079            return GeometryFactory.createPoint( shppoint.x, shppoint.y, crs );
080        }
081    
082        /**
083         * method: Point[] transformMultiPoint(CS_CoordinateSystem srs,<BR>
084         *                                        SHPMultiPoint shpmultipoint))<BR>
085         * transforms a SHPMultiPoint to a WKSGeometry<BR>
086         * gets a multipoint that should be transformed<BR>
087         */
088        public Point[] transformMultiPoint( CoordinateSystem srs, SHPMultiPoint shpmultipoint ) {
089            Point[] gm_points = new Point[shpmultipoint.numPoints];
090    
091            for ( int i = 0; i < shpmultipoint.numPoints; i++ )
092                gm_points[i] = GeometryFactory.createPoint( shpmultipoint.points[i].x,
093                                                            shpmultipoint.points[i].y, srs );
094    
095            return gm_points;
096        }
097    
098        /**
099         * method: Point[][] transformPolyLine(CS_CoordinateSystem srs,<BR>
100         *                                        SHPPolyLine shppolyline))<BR>
101         * transforms a SHPPolyLine to a WKSGeometry<BR>
102         * gets a polyline that should be transformed<BR>
103         */
104        public Curve[] transformPolyLine( CoordinateSystem crs, SHPPolyLine shppolyline ) {
105            Curve[] curve = new Curve[shppolyline.numParts];
106    
107            try {
108                for ( int j = 0; j < shppolyline.numParts; j++ ) {
109                    Position[] gm_points = new Position[shppolyline.points[j].length];
110    
111                    for ( int i = 0; i < shppolyline.points[j].length; i++ ) {
112                        gm_points[i] = GeometryFactory.createPosition( shppolyline.points[j][i].x,
113                                                                       shppolyline.points[j][i].y );
114                    }
115    
116                    CurveSegment cs = GeometryFactory.createCurveSegment( gm_points, crs );
117                    curve[j] = GeometryFactory.createCurve( cs );
118                }
119            } catch ( Exception e ) {
120                e.printStackTrace();
121            }
122    
123            return curve;
124        }
125    
126        /**
127         * method: private boolean isInsideRing(Point[] ring, Point point)<BR>
128         * checks if a point is inside a polygon. the algorithm is taken from:<BR>
129         * http://www.ics.uci.edu/~eppstein/161/960307.html#intest<BR>
130         */
131        private boolean isInsideRing( Position[] ring, Position point ) {
132            int crossings = 0;
133    
134            for ( int i = 0; i < ring.length; i++ ) {
135                int z = i + 1;
136    
137                if ( ( i + 1 ) >= ring.length ) {
138                    z = 0;
139                }
140    
141                //check if point.x is between x of vertex i and z of ring
142                if ( ( ring[i].getX() < point.getX() && point.getX() < ring[z].getX() )
143                     || ( ring[i].getX() > point.getX() && point.getX() > ring[z].getX() ) ) {
144                    double t = ( point.getX() - ring[z].getX() ) / ( ring[i].getX() - ring[z].getX() );
145    
146                    double cy = ( t * ring[i].getY() ) + ( ( 1 - t ) * ring[z].getY() );
147    
148                    if ( point.getY() == cy ) { //point is on border of ring
149                        return false;
150                    } else if ( point.getY() > cy ) { //downwards vertical line through point crosses ring
151                        crossings++;
152                    }
153                }
154    
155                //check if point.x equals x of vertex i of ring while point.y > ring[i].y
156                if ( ( ring[i].getX() == point.getX() ) && ( ring[i].getY() <= point.getY() ) ) {
157    
158                    if ( ring[i].getY() == point.getY() ) { //point is on border of ring
159                        return false;
160                    }
161    
162                    //find next point on ring with different x
163                    // (adjacent points in shapefile can have equal x&y)
164                    while ( ring[z].getX() == point.getX() ) {
165                        if ( z == i ) {
166                            return false;
167                        }
168                        z += 1;
169                        if ( z == ring.length ) {
170                            z = 0;
171                        }
172                    }
173    
174                    //find previous point on ring with different x
175                    int zz = i - 1;
176                    if ( zz < 0 ) {
177                        zz = ring.length - 1;
178                    }
179                    while ( ring[zz].getX() == point.getX() ) {
180                        if ( zz == i ) {
181                            return false;
182                        }
183                        zz -= 1;
184                        if ( zz < 0 ) {
185                            zz = ring.length - 1;
186                        }
187                    }
188    
189                    //if point.x between previous and next x then crossing
190                    if ( ring[z].getX() < point.getX() && point.getX() < ring[zz].getX()
191                         || ring[z].getX() > point.getX() && point.getX() > ring[zz].getX() ) {
192                        crossings++;
193                    }
194    
195                }
196            }
197    
198            if ( ( crossings % 2 ) != 0 ) {
199                return true;
200            }
201            return false;
202        }
203    
204        /**
205         * transforms the SHPPolygon to a WKSGeometry<BR>
206         * gets the polygon that should be transformed<BR>
207         */
208        public Surface[] transformPolygon( CoordinateSystem crs, SHPPolygon shppolygon ) {
209            ArrayList<Position[]> all_rings = new ArrayList<Position[]>( shppolygon.numRings );
210            ArrayList<Position[]> outer_rings = new ArrayList<Position[]>( shppolygon.numRings );
211            ArrayList<Position[]> inner_rings = new ArrayList<Position[]>( shppolygon.numRings );
212    
213            for ( int i = 0; i < shppolygon.numRings; i++ ) {
214    
215                Position[] ring = new Position[shppolygon.rings.points[i].length];
216    
217                for ( int k = 0; k < shppolygon.rings.points[i].length; k++ ) {
218                    ring[k] = GeometryFactory.createPosition( shppolygon.rings.points[i][k].x,
219                                                              shppolygon.rings.points[i][k].y );
220                }
221                all_rings.add( ring );
222            }
223    
224            // for every outer ring
225            for ( int i = 0; i < all_rings.size(); i++ ) {
226                Position[] out_ring = all_rings.get( i );
227    
228                boolean inn = false;
229                for ( int j = 0; j < all_rings.size(); j++ ) {
230                    if ( i == j )
231                        continue;
232                    Position[] inring = all_rings.get( j );
233    
234                    // check if one or more points of a inner ring are
235                    // within the actual outer ring
236                    try {
237                        if ( isInsideRing( inring, out_ring[0] ) ) {
238                            inn = true;
239                            inner_rings.add( out_ring );
240                            break;
241                        }
242                    } catch ( Exception e ) {
243                        e.printStackTrace();
244                    }
245    
246                }
247                if ( !inn ) {
248                    outer_rings.add( out_ring );
249                }
250    
251            }
252    
253            ArrayList<Surface> wkslp = new ArrayList<Surface>( outer_rings.size() );
254            SurfaceInterpolation si = new SurfaceInterpolationImpl();
255            for ( int i = 0; i < outer_rings.size(); i++ ) {
256                Position[] out_ring = outer_rings.get( i );
257    
258                int count = inner_rings.size() - 1;
259                ArrayList<Position[]> list = new ArrayList<Position[]>( count + 2 );
260                // find inner rings of the current outter ring
261                for ( int k = count; k >= 0; k-- ) {
262                    Position[] in_ring = inner_rings.get( k );
263                    if ( isInsideRing( out_ring, in_ring[0] ) ) {
264                        list.add( inner_rings.remove( k ) );
265                    }
266                }
267                Position[][] inrings = list.toArray( new Position[list.size()][] );
268    
269                try {
270                    Surface sur = GeometryFactory.createSurface( out_ring, inrings, si, crs );
271                    wkslp.add( sur );
272                } catch ( Exception e ) {
273                    e.printStackTrace();
274                }
275    
276            }
277    
278            return wkslp.toArray( new Surface[wkslp.size()] );
279        }
280    }