001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_testing/src/org/deegree/io/shpapi/SHP2WKS.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.util.ArrayList;
039    
040    import org.deegree.model.crs.CoordinateSystem;
041    import org.deegree.model.spatialschema.Curve;
042    import org.deegree.model.spatialschema.CurveSegment;
043    import org.deegree.model.spatialschema.GeometryFactory;
044    import org.deegree.model.spatialschema.Point;
045    import org.deegree.model.spatialschema.Position;
046    import org.deegree.model.spatialschema.Surface;
047    import org.deegree.model.spatialschema.SurfaceInterpolation;
048    import org.deegree.model.spatialschema.SurfaceInterpolationImpl;
049    
050    /**
051     * the class SHP2WKS transforms a polygon structure read from a shape-file<BR>
052     * into a WKSLinearPolygon specified by the sf-specifications<BR>
053     *
054     * @version $Revision: 18195 $
055     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
056     * @author last edited by: $Author: mschneider $
057     *
058     * @version 1.0. $Revision: 18195 $, $Date: 2009-06-18 17:55:39 +0200 (Do, 18. Jun 2009) $
059     *
060     * @since 2.0
061     */
062    public class SHP2WKS {
063    
064        /**
065         * method: Point transformPoint(CS_CoordinateSystem srs,<BR>
066         * SHPPoint shppoint))<BR>
067         * transforms a SHPPoint to a WKSGeometry<BR>
068         * gets a point that should be transformed<BR>
069         *
070         * @param crs
071         * @param shppoint
072         * @return the transformed point
073         */
074        public Point transformPoint( CoordinateSystem crs, SHPPoint shppoint ) {
075            return GeometryFactory.createPoint( shppoint.x, shppoint.y, crs );
076        }
077    
078        /**
079         * method: Point[] transformMultiPoint(CS_CoordinateSystem srs,<BR>
080         * SHPMultiPoint shpmultipoint))<BR>
081         * transforms a SHPMultiPoint to a WKSGeometry<BR>
082         * gets a multipoint that should be transformed<BR>
083         *
084         * @param srs
085         * @param shpmultipoint
086         * @return the transformed points
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, shpmultipoint.points[i].y, srs );
093    
094            return gm_points;
095        }
096    
097        /**
098         * method: Point[][] transformPolyLine(CS_CoordinateSystem srs,<BR>
099         * SHPPolyLine shppolyline))<BR>
100         * transforms a SHPPolyLine to a WKSGeometry<BR>
101         * gets a polyline that should be transformed<BR>
102         *
103         * @param crs
104         * @param shppolyline
105         * @return the transformed line
106         */
107        public Curve[] transformPolyLine( CoordinateSystem crs, SHPPolyLine shppolyline ) {
108            Curve[] curve = new Curve[shppolyline.numParts];
109    
110            try {
111                for ( int j = 0; j < shppolyline.numParts; j++ ) {
112                    Position[] gm_points = new Position[shppolyline.points[j].length];
113    
114                    for ( int i = 0; i < shppolyline.points[j].length; i++ ) {
115                        gm_points[i] = GeometryFactory.createPosition( shppolyline.points[j][i].x,
116                                                                       shppolyline.points[j][i].y );
117                    }
118    
119                    CurveSegment cs = GeometryFactory.createCurveSegment( gm_points, crs );
120                    curve[j] = GeometryFactory.createCurve( cs );
121                }
122            } catch ( Exception e ) {
123                e.printStackTrace();
124            }
125    
126            return curve;
127        }
128    
129        /**
130         * method: private boolean isInsideRing(Point[] ring, Point point)<BR>
131         * checks if a point is inside a polygon. the algorithm is taken from:<BR>
132         * http://www.ics.uci.edu/~eppstein/161/960307.html#intest<BR>
133         */
134        private boolean isInsideRing( Position[] ring, Position point ) {
135            int crossings = 0;
136    
137            for ( int i = 0; i < ring.length; i++ ) {
138                int z = i + 1;
139    
140                if ( ( i + 1 ) >= ring.length ) {
141                    z = 0;
142                }
143    
144                // check if point.x is between x of vertex i and z of ring
145                if ( ( ring[i].getX() < point.getX() && point.getX() < ring[z].getX() )
146                     || ( ring[i].getX() > point.getX() && point.getX() > ring[z].getX() ) ) {
147                    double t = ( point.getX() - ring[z].getX() ) / ( ring[i].getX() - ring[z].getX() );
148    
149                    double cy = ( t * ring[i].getY() ) + ( ( 1 - t ) * ring[z].getY() );
150    
151                    if ( point.getY() == cy ) { // point is on border of ring
152                        return false;
153                    } else if ( point.getY() > cy ) { // downwards vertical line through point crosses
154                        // ring
155                        crossings++;
156                    }
157                }
158    
159                // check if point.x equals x of vertex i of ring while point.y > ring[i].y
160                if ( ( ring[i].getX() == point.getX() ) && ( ring[i].getY() <= point.getY() ) ) {
161    
162                    if ( ring[i].getY() == point.getY() ) { // point is on border of ring
163                        return false;
164                    }
165    
166                    // find next point on ring with different x
167                    // (adjacent points in shapefile can have equal x&y)
168                    while ( ring[z].getX() == point.getX() ) {
169                        if ( z == i ) {
170                            return false;
171                        }
172                        z += 1;
173                        if ( z == ring.length ) {
174                            z = 0;
175                        }
176                    }
177    
178                    // find previous point on ring with different x
179                    int zz = i - 1;
180                    if ( zz < 0 ) {
181                        zz = ring.length - 1;
182                    }
183                    while ( ring[zz].getX() == point.getX() ) {
184                        if ( zz == i ) {
185                            return false;
186                        }
187                        zz -= 1;
188                        if ( zz < 0 ) {
189                            zz = ring.length - 1;
190                        }
191                    }
192    
193                    // if point.x between previous and next x then crossing
194                    if ( ring[z].getX() < point.getX() && point.getX() < ring[zz].getX() || ring[z].getX() > point.getX()
195                         && point.getX() > ring[zz].getX() ) {
196                        crossings++;
197                    }
198    
199                }
200            }
201    
202            if ( ( crossings % 2 ) != 0 ) {
203                return true;
204            }
205            return false;
206        }
207    
208        /**
209         * transforms the SHPPolygon to a WKSGeometry<BR>
210         * gets the polygon that should be transformed<BR>
211         *
212         * @param crs
213         * @param shppolygon
214         * @return the transformed polygon
215         */
216        public Surface[] transformPolygon( CoordinateSystem crs, SHPPolygon shppolygon ) {
217            ArrayList<Position[]> all_rings = new ArrayList<Position[]>( shppolygon.numRings );
218            ArrayList<Position[]> outer_rings = new ArrayList<Position[]>( shppolygon.numRings );
219            ArrayList<Position[]> inner_rings = new ArrayList<Position[]>( shppolygon.numRings );
220    
221            for ( int i = 0; i < shppolygon.numRings; i++ ) {
222    
223                Position[] ring = new Position[shppolygon.rings.points[i].length];
224    
225                for ( int k = 0; k < shppolygon.rings.points[i].length; k++ ) {
226                    ring[k] = GeometryFactory.createPosition( shppolygon.rings.points[i][k].x,
227                                                              shppolygon.rings.points[i][k].y );
228                }
229                all_rings.add( ring );
230            }
231    
232            // for every outer ring
233            for ( int i = 0; i < all_rings.size(); i++ ) {
234                Position[] out_ring = all_rings.get( i );
235    
236                boolean inn = false;
237                for ( int j = 0; j < all_rings.size(); j++ ) {
238                    if ( i == j )
239                        continue;
240                    Position[] inring = all_rings.get( j );
241    
242                    // check if one or more points of a inner ring are
243                    // within the actual outer ring
244                    try {
245                        if ( isInsideRing( inring, out_ring[0] ) ) {
246                            inn = true;
247                            inner_rings.add( out_ring );
248                            break;
249                        }
250                    } catch ( Exception e ) {
251                        e.printStackTrace();
252                    }
253    
254                }
255                if ( !inn ) {
256                    outer_rings.add( out_ring );
257                }
258    
259            }
260    
261            ArrayList<Surface> wkslp = new ArrayList<Surface>( outer_rings.size() );
262            SurfaceInterpolation si = new SurfaceInterpolationImpl();
263            for ( int i = 0; i < outer_rings.size(); i++ ) {
264                Position[] out_ring = outer_rings.get( i );
265    
266                int count = inner_rings.size() - 1;
267                ArrayList<Position[]> list = new ArrayList<Position[]>( count + 2 );
268                // find inner rings of the current outter ring
269                for ( int k = count; k >= 0; k-- ) {
270                    Position[] in_ring = inner_rings.get( k );
271                    if ( isInsideRing( out_ring, in_ring[0] ) ) {
272                        list.add( inner_rings.remove( k ) );
273                    }
274                }
275                Position[][] inrings = list.toArray( new Position[list.size()][] );
276    
277                try {
278                    Surface sur = GeometryFactory.createSurface( out_ring, inrings, si, crs );
279                    wkslp.add( sur );
280                } catch ( Exception e ) {
281                    e.printStackTrace();
282                }
283    
284            }
285    
286            return wkslp.toArray( new Surface[wkslp.size()] );
287        }
288    }