001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_testing/src/org/deegree/io/datastore/sql/postgis/PGgeometryAdapter.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    
037    package org.deegree.io.datastore.sql.postgis;
038    
039    import java.sql.SQLException;
040    import java.util.ArrayList;
041    import java.util.Iterator;
042    import java.util.List;
043    
044    import org.deegree.model.crs.CoordinateSystem;
045    import org.deegree.model.spatialschema.Curve;
046    import org.deegree.model.spatialschema.Envelope;
047    import org.deegree.model.spatialschema.Geometry;
048    import org.deegree.model.spatialschema.GeometryException;
049    import org.deegree.model.spatialschema.GeometryFactory;
050    import org.deegree.model.spatialschema.MultiCurve;
051    import org.deegree.model.spatialschema.MultiGeometry;
052    import org.deegree.model.spatialschema.MultiPoint;
053    import org.deegree.model.spatialschema.MultiSurface;
054    import org.deegree.model.spatialschema.Point;
055    import org.deegree.model.spatialschema.Position;
056    import org.deegree.model.spatialschema.Surface;
057    import org.deegree.model.spatialschema.SurfaceInterpolationImpl;
058    import org.deegree.model.spatialschema.WKTAdapter;
059    import org.postgis.GeometryCollection;
060    import org.postgis.LineString;
061    import org.postgis.MultiLineString;
062    import org.postgis.MultiPolygon;
063    import org.postgis.PGbox3d;
064    import org.postgis.PGboxbase;
065    import org.postgis.PGgeometry;
066    import org.postgis.Polygon;
067    
068    /**
069     * Adapter between deegree <code>Geometry</code> objects and PostGIS <code>Geometry</code> objects.
070     *
071     * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider </A>
072     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth </a>
073     *
074     * @author last edited by: $Author: mschneider $
075     *
076     * @version $Revision: 18195 $, $Date: 2009-06-18 17:55:39 +0200 (Do, 18. Jun 2009) $
077     */
078    public class PGgeometryAdapter {
079    
080        private PGgeometryAdapter() {
081            // avoid instantiation
082        }
083    
084        /**
085         * Converts a deegree <code>Geometry</code> instance to a corresponding PostGIS {@link PGgeometry} object.
086         *
087         * @param geometry
088         *            deegree <code>Geometry</code> to be converted
089         * @param srid
090         *            PostGIS SRS id that is used to store the geometry
091         * @return corresponding PostGIS <code>Geometry</code>
092         * @throws GeometryException
093         */
094        public static PGgeometry export( Geometry geometry, int srid )
095                                throws GeometryException {
096            PGgeometry pgGeometry = null;
097            if ( geometry instanceof Point ) {
098                pgGeometry = exportPoint( (Point) geometry, srid );
099            } else if ( geometry instanceof MultiPoint ) {
100                pgGeometry = exportMultiPoint( (MultiPoint) geometry, srid );
101            } else if ( geometry instanceof Curve ) {
102                pgGeometry = exportCurve( (Curve) geometry, srid );
103            } else if ( geometry instanceof MultiCurve ) {
104                pgGeometry = exportMultiCurve( (MultiCurve) geometry, srid );
105            } else if ( geometry instanceof Surface ) {
106                pgGeometry = exportSurface( (Surface) geometry, srid );
107            } else if ( geometry instanceof MultiSurface ) {
108                pgGeometry = exportMultiSurface( (MultiSurface) geometry, srid );
109            } else if ( geometry instanceof MultiGeometry ) {
110                pgGeometry = exportMultiGeometry( (MultiGeometry) geometry, srid );
111            } else {
112                throw new GeometryException( "Cannot export geometry of type '" + geometry.getClass()
113                                             + "' to PostGIS geometry: Unsupported type." );
114            }
115            return pgGeometry;
116        }
117    
118        /**
119         * Converts a deegree <code>Envelope</code> instance to a corresponding PostGIS <code>PGboxbase</code> object.
120         *
121         * @param envelope
122         *            deegree <code>Envelope</code> to be converted
123         * @return corresponding PostGIS <code>PGboxbase</code>
124         * @throws GeometryException
125         */
126        public static PGboxbase export( Envelope envelope )
127                                throws GeometryException {
128            StringBuffer sb = WKTAdapter.export( envelope );
129            PGbox3d box = null;
130            try {
131                box = new PGbox3d( sb.toString() );
132            } catch ( Exception e ) {
133                throw new GeometryException( e.toString() );
134            }
135    
136            return box;
137        }
138    
139        /**
140         * Converts a PostGIS <code>PGGeometry</code> instance to a corresponding deegree <code>Geometry</code> object.
141         *
142         * @param pgGeometry
143         *            PostGIS <code>PGgeometry</code> to be converted
144         * @param crs
145         *            coordinate system of the created deegree <code>Geometry</code> object
146         * @return corresponding deegree <code>Geometry</code>
147         * @throws GeometryException
148         */
149        public static Geometry wrap( PGgeometry pgGeometry, CoordinateSystem crs )
150                                throws GeometryException {
151            return wrap( pgGeometry.getGeometry(), crs );
152        }
153    
154        /**
155         * Converts a PostGIS <code>Geometry</code> instance to a corresponding deegree <code>Geometry</code> object.
156         *
157         * @param geometry
158         *            PostGIS <code>PGgeometry</code> to be converted
159         * @param crs
160         *            coordinate system of the created deegree <code>Geometry</code> object
161         * @return corresponding deegree <code>Geometry</code>
162         * @throws GeometryException
163         */
164        public static Geometry wrap( org.postgis.Geometry geometry, CoordinateSystem crs )
165                                throws GeometryException {
166            Geometry geo = null;
167    
168            switch ( geometry.type ) {
169            case org.postgis.Geometry.POINT:
170                geo = wrapPoint( (org.postgis.Point) geometry, crs );
171                break;
172            case org.postgis.Geometry.LINESTRING:
173                geo = wrapCurve( (LineString) geometry, crs );
174                break;
175            case org.postgis.Geometry.POLYGON:
176                geo = wrapSurface( (Polygon) geometry, crs );
177                break;
178            case org.postgis.Geometry.MULTIPOINT:
179                geo = wrapMultiPoint( (org.postgis.MultiPoint) geometry, crs );
180                break;
181            case org.postgis.Geometry.MULTILINESTRING:
182                geo = wrapMultiCurve( (MultiLineString) geometry, crs );
183                break;
184            case org.postgis.Geometry.MULTIPOLYGON:
185                geo = wrapMultiSurface( (MultiPolygon) geometry, crs );
186                break;
187            case org.postgis.Geometry.GEOMETRYCOLLECTION:
188                geo = wrapMultiGeometry( (GeometryCollection) geometry, crs );
189                break;
190            default: {
191                throw new GeometryException( "Cannot export PostGIS geometry of type '" + geometry.getType()
192                                             + "' to deegree geometry: Unsupported type." );
193            }
194            }
195            return geo;
196        }
197    
198        /**
199         * Creates a PostGIS <code>MultiPoint</code> from a deegree <code>Point</code>.
200         *
201         * @param point
202         * @param srid
203         *            PostGIS SRS id that is used to store the geometry
204         * @throws GeometryException
205         */
206        private static PGgeometry exportPoint( Point point, int srid )
207                                throws GeometryException {
208    
209            StringBuffer sb = WKTAdapter.export( point );
210            org.postgis.Point pgPoint = null;
211    
212            try {
213                pgPoint = new org.postgis.Point( sb.toString() );
214            } catch ( SQLException e ) {
215                throw new GeometryException( e.toString() );
216            }
217    
218            pgPoint.setSrid( srid );
219            return new PGgeometry( pgPoint );
220        }
221    
222        /**
223         * Creates a PostGIS <code>MultiPoint</code> from a deegree <code>MultiPoint</code>.
224         *
225         * @param multiPoint
226         * @param srid
227         *            PostGIS SRS id that is used to store the geometry
228         * @throws GeometryException
229         */
230        private static PGgeometry exportMultiPoint( MultiPoint multiPoint, int srid )
231                                throws GeometryException {
232    
233            StringBuffer sb = WKTAdapter.export( multiPoint );
234            org.postgis.MultiPoint pgMPoint = null;
235    
236            try {
237                pgMPoint = new org.postgis.MultiPoint( sb.toString() );
238            } catch ( Exception e ) {
239                throw new GeometryException( e.toString() );
240            }
241    
242            pgMPoint.setSrid( srid );
243            return new PGgeometry( pgMPoint );
244        }
245    
246        /**
247         * Creates a PostGIS <code>LineString</code> from a deegree <code>Curve</code>.
248         *
249         * @param curve
250         * @param srid
251         *            PostGIS SRS id that is used to store the geometry
252         */
253        private static PGgeometry exportCurve( Curve curve, int srid )
254                                throws GeometryException {
255            StringBuffer sb = WKTAdapter.export( curve );
256            org.postgis.LineString pgLineString = null;
257    
258            try {
259                pgLineString = new org.postgis.LineString( sb.toString() );
260            } catch ( Exception e ) {
261                throw new GeometryException( e.toString() );
262            }
263    
264            pgLineString.setSrid( srid );
265            return new PGgeometry( pgLineString );
266        }
267    
268        /**
269         * Creates a PostGIS <code>org.postgis.MultiCurve</code> from a deegree <code>MultiCurve</code>.
270         *
271         * @param multiCurve
272         * @param srid
273         *            PostGIS SRS id that is used to store the geometry
274         * @throws GeometryException
275         */
276        private static PGgeometry exportMultiCurve( MultiCurve multiCurve, int srid )
277                                throws GeometryException {
278            StringBuffer sb = WKTAdapter.export( multiCurve );
279            org.postgis.MultiLineString pgMLineString = null;
280    
281            try {
282                pgMLineString = new org.postgis.MultiLineString( sb.toString() );
283            } catch ( Exception e ) {
284                throw new GeometryException( e.toString() );
285            }
286    
287            pgMLineString.setSrid( srid );
288            return new PGgeometry( pgMLineString );
289        }
290    
291        /**
292         * Creates a PostGIS <code>Polygon</code> from a deegree <code>Surface</code>.
293         *
294         * @param surface
295         * @param srid
296         *            PostGIS SRS id that is used to store the geometry
297         * @throws GeometryException
298         */
299        private static PGgeometry exportSurface( Surface surface, int srid )
300                                throws GeometryException {
301            StringBuffer sb = WKTAdapter.export( surface );
302            org.postgis.Polygon pgPoly = null;
303    
304            try {
305                pgPoly = new org.postgis.Polygon( sb.toString() );
306            } catch ( Exception e ) {
307                throw new GeometryException( e.toString() );
308            }
309    
310            pgPoly.setSrid( srid );
311            return new PGgeometry( pgPoly );
312        }
313    
314        /**
315         * Creates a PostGIS <code>MultiSurface</code> from a deegree <code>MultiSurface</code>.
316         *
317         * @param multiSurface
318         * @param srid
319         *            PostGIS SRS id that is used to store the geometry
320         * @throws GeometryException
321         */
322        private static PGgeometry exportMultiSurface( MultiSurface multiSurface, int srid )
323                                throws GeometryException {
324            StringBuffer sb = WKTAdapter.export( multiSurface );
325            org.postgis.MultiPolygon pgMPoly = null;
326    
327            try {
328                pgMPoly = new org.postgis.MultiPolygon( sb.toString() );
329            } catch ( Exception e ) {
330                throw new GeometryException( e.toString() );
331            }
332    
333            pgMPoly.setSrid( srid );
334            return new PGgeometry( pgMPoly );
335        }
336    
337        /**
338         * Creates a PostGIS <code>GeometryCollection</code> from a deegree <code>MultiGeometry</code>.
339         *
340         * @param multiGeometry
341         * @param srid
342         *            PostGIS SRS id that is used to store the geometry
343         * @throws GeometryException
344         */
345        private static PGgeometry exportMultiGeometry( MultiGeometry multiGeometry, int srid )
346                                throws GeometryException {
347    
348            StringBuffer sb = WKTAdapter.export( multiGeometry );
349            GeometryCollection pgGeometryCollection = null;
350    
351            try {
352                pgGeometryCollection = new GeometryCollection( sb.toString() );
353            } catch ( Exception e ) {
354                throw new GeometryException( e.toString() );
355            }
356    
357            pgGeometryCollection.setSrid( srid );
358            return new PGgeometry( pgGeometryCollection );
359        }
360    
361        /**
362         * Creates a deegree <code>Point</code> from a PostGIS <code>Point</code>.
363         *
364         * @param pgPoint
365         *            PostGIS <code>Point</code>
366         * @param crs
367         *            coordinate system of the created deegree <code>Geometry</code> object
368         * @return deegree <code>Point</code>
369         */
370        private static Point wrapPoint( org.postgis.Point pgPoint, CoordinateSystem crs ) {
371            // if geometry is 2-dimensional
372            Position p = null;
373            if ( pgPoint.getDimension() == 2 ) {
374                // convert PostGIS Point to a Point using the GeometryFactory
375                p = GeometryFactory.createPosition( new double[] { pgPoint.getX(), pgPoint.getY() } );
376                // if geometry is 3-dimensional
377            } else if ( pgPoint.getDimension() == 3 ) {
378                // convert PostGIS Point to a Point using the GeometryFactory
379                p = GeometryFactory.createPosition( new double[] { pgPoint.getX(), pgPoint.getY(), pgPoint.getZ() } );
380            }
381            return GeometryFactory.createPoint( p, crs );
382        }
383    
384        /**
385         * Creates a deegree <code>MultiPoint</code> from a PostGIS <code>MultiPoint</code>.
386         *
387         * @param pgMultiPoint
388         *            PostGIS <code>MultiPoint</code>
389         * @param crs
390         *            coordinate system of the created deegree <code>Geometry</code> object
391         * @return deegree <code>MultiPoint</code>
392         */
393        private static MultiPoint wrapMultiPoint( org.postgis.MultiPoint pgMultiPoint, CoordinateSystem crs ) {
394            // create a temporary Point Array to store the Points the
395            // MultiPoint will consist of
396            Point[] mpoints = new Point[pgMultiPoint.numPoints()];
397            // for all Points
398            for ( int i = 0; i < pgMultiPoint.numPoints(); i++ ) {
399                // convert PostGIS Point to a Point using the GeometryFactory
400                mpoints[i] = wrapPoint( pgMultiPoint.getPoint( i ), crs );
401            }
402            // create a Multipoint from the Array points
403            return GeometryFactory.createMultiPoint( mpoints );
404        }
405    
406        /**
407         * Creates a deegree <code>Curve</code> from a PostGIS <code>LineString</code>.
408         *
409         * @param pgLineString
410         *            PostGIS <code>LineString</code>
411         * @param crs
412         *            coordinate system of the created deegree <code>Geometry</code> object
413         * @return deegree <code>Curve</code>
414         * @throws GeometryException
415         */
416        private static Curve wrapCurve( LineString pgLineString, CoordinateSystem crs )
417                                throws GeometryException {
418            // create a Position Array. Used to store the Points the
419            // Curve will consist of
420            Position[] points = new Position[pgLineString.numPoints()];
421    
422            // if geometry is 2-dimensional
423            if ( pgLineString.getDimension() == 2 ) {
424                // for all Points
425                for ( int i = 0; i < pgLineString.numPoints(); i++ ) {
426                    // create a Position from the PostGIS Point using the
427                    // GeometryFactory
428                    double[] d = new double[] { pgLineString.getPoint( i ).getX(), pgLineString.getPoint( i ).getY() };
429                    points[i] = GeometryFactory.createPosition( d );
430                }
431                // if geometry is 3-dimensional
432            } else if ( pgLineString.getDimension() == 3 ) {
433                // for all Points
434                for ( int i = 0; i < pgLineString.numPoints(); i++ ) {
435                    // create a Position from the PostGIS Point using the
436                    // GeometryFactory
437                    double[] d = new double[] { pgLineString.getPoint( i ).getX(), pgLineString.getPoint( i ).getY(),
438                                               pgLineString.getPoint( i ).getZ() };
439                    points[i] = GeometryFactory.createPosition( d );
440                }
441            }
442            return GeometryFactory.createCurve( points, crs );
443        }
444    
445        /**
446         * Creates a deegree <code>MultiCurve</code> from a PostGIS <code>MultiLineString</code>.
447         *
448         * @param pgMultiLineString
449         *            PostGIS <code>MultiLineString</code>
450         * @param crs
451         *            coordinate system of the created deegree <code>Geometry</code> object
452         * @return deegree <code>MultiCurve</code>
453         * @throws GeometryException
454         */
455        private static MultiCurve wrapMultiCurve( MultiLineString pgMultiLineString, CoordinateSystem crs )
456                                throws GeometryException {
457            // create a Curve Array. Used to store the CurveSegments the
458            // Curve will consist of
459            Curve[] curves = new Curve[pgMultiLineString.numLines()];
460            // for all Lines
461            for ( int i = 0; i < pgMultiLineString.numLines(); i++ ) {
462                // create a Curve form the positions Array using the
463                // GeometryFactory
464                curves[i] = wrapCurve( pgMultiLineString.getLine( i ), crs );
465            }
466            // create a Curve form all the CurveSegments stored in the
467            // csegments Array using the GeometryFactory
468            return GeometryFactory.createMultiCurve( curves );
469        }
470    
471        /**
472         * Creates a deegree <code>Surface</code> from a PostGIS <code>Polygon</code>.
473         *
474         * @param pgPolygon
475         *            PostGIS <code>Polygon</code>
476         * @param crs
477         *            coordinate system of the created deegree <code>Geometry</code> object
478         * @return deegree <code>Surface</code>
479         * @throws GeometryException
480         */
481        private static Surface wrapSurface( Polygon pgPolygon, CoordinateSystem crs )
482                                throws GeometryException {
483    
484            // create a Position Array. Used to store the Positions the
485            // exterior Ring of the Surface will consist of
486            Position[] eRing = new Position[pgPolygon.getRing( 0 ).numPoints()];
487            // declares a Position[][] Array. Used to store the Positions
488            // of the interior Rings the Surface will consist of. The exterior
489            // Ring is stored seperately
490            Position[][] iRings = null;
491    
492            // if geometry is 2-dimensional
493            if ( pgPolygon.getDimension() == 2 ) {
494                // for all the Points of the fist LinearRing (which is the exterior)
495                org.postgis.LinearRing ring = pgPolygon.getRing( 0 );
496                for ( int j = 0; j < eRing.length; j++ ) {
497                    // store all the Points of the exterior Ring in the Array
498                    // eRing. Convert them using GeometryFactory
499                    double[] d = new double[] { ring.getPoint( j ).getX(), ring.getPoint( j ).getY() };
500                    eRing[j] = GeometryFactory.createPosition( d );
501                }
502    
503                if ( pgPolygon.numRings() > 1 ) {
504                    iRings = new Position[pgPolygon.numRings() - 1][];
505                    // for all LinearRings except the first one (which is the exterior one)
506                    for ( int i = 1; i < pgPolygon.numRings(); i++ ) {
507                        iRings[i - 1] = new Position[pgPolygon.getRing( i ).numPoints()];
508                        // for all the Points in the ith LinearRing
509                        ring = pgPolygon.getRing( i );
510                        for ( int j = 0; j < ring.numPoints(); j++ ) {
511                            // store all the Points of the ith interior Ring in
512                            // the iRings Array
513                            double[] d = new double[] { ring.getPoint( j ).getX(), ring.getPoint( j ).getY() };
514                            iRings[i - 1][j] = GeometryFactory.createPosition( d );
515                        }
516                    }
517                }
518                // if geometry is 3-dimensional
519            } else if ( pgPolygon.getDimension() == 3 ) {
520                // for all the Points of the fist LinearRing (which is the exterior)
521                org.postgis.LinearRing ring = pgPolygon.getRing( 0 );
522                for ( int j = 0; j < ring.numPoints(); j++ ) {
523                    // store all the Points of the exterior Ring in the Array
524                    // eRing. Convert them using GeometryFactory
525                    double[] d = new double[] { ring.getPoint( j ).getX(), ring.getPoint( j ).getY(),
526                                               ring.getPoint( j ).getZ() };
527                    eRing[j] = GeometryFactory.createPosition( d );
528                }
529    
530                if ( pgPolygon.numRings() > 1 ) {
531                    iRings = new Position[pgPolygon.numRings() - 1][];
532                    // for all LinearRings except the first one (which is the exterior one)
533                    for ( int i = 1; i < pgPolygon.numRings(); i++ ) {
534                        iRings[i - 1] = new Position[pgPolygon.getRing( i ).numPoints()];
535                        // for all the Points in the ith LinearRing
536                        ring = pgPolygon.getRing( i );
537                        for ( int j = 0; j < ring.numPoints(); j++ ) {
538                            // store all the Points of the ith interior Ring in the iRings Array
539                            double[] d = new double[] { ring.getPoint( j ).getX(), ring.getPoint( j ).getY(),
540                                                       ring.getPoint( j ).getZ() };
541                            iRings[i - 1][j] = GeometryFactory.createPosition( d );
542                        }
543                    }
544                }
545            }
546    
547            return GeometryFactory.createSurface( eRing, iRings, new SurfaceInterpolationImpl(), crs );
548        }
549    
550        /**
551         * Creates a deegree <code>MultiSurface</code> from a PostGIS <code>MultiPolygon</code>.
552         *
553         * @param pgMultiPolygon
554         *            PostGIS <code>MultiPolygon</code>
555         * @param crs
556         *            coordinate system of the created deegree <code>Geometry</code> object
557         * @return deegree <code>MultiSurface</code>
558         * @throws GeometryException
559         */
560        private static MultiSurface wrapMultiSurface( MultiPolygon pgMultiPolygon, CoordinateSystem crs )
561                                throws GeometryException {
562            // create a Surfaces Array. Used to store the Surfaces the
563            // MultiSurface will consist of
564            Surface[] surfaces = new Surface[pgMultiPolygon.numPolygons()];
565            // for all Polygons the MultiPolygon consists of
566            for ( int i = 0; i < pgMultiPolygon.numPolygons(); i++ ) {
567                surfaces[i] = wrapSurface( pgMultiPolygon.getPolygon( i ), crs );
568            }
569    
570            return GeometryFactory.createMultiSurface( surfaces );
571        }
572    
573        /**
574         * Creates a deegree <code>MultiGeometry</code> from a PostGIS <code>GeometryCollection</code>.
575         *
576         * @param pgGeometryCollection
577         *            PostGIS <code>GeometryCollection</code>
578         * @param crs
579         *            coordinate system of the created deegree <code>Geometry</code> object
580         * @return deegree <code>MultiGeometry</code>
581         * @throws GeometryException
582         */
583        private static MultiGeometry wrapMultiGeometry( GeometryCollection pgGeometryCollection, CoordinateSystem crs )
584                                throws GeometryException {
585    
586            List<Geometry> members = new ArrayList<Geometry>();
587            Iterator<?> memberIter = pgGeometryCollection.iterator();
588            while ( memberIter.hasNext() ) {
589                org.postgis.Geometry memberGeometry = (org.postgis.Geometry) memberIter.next();
590                members.add( wrap( memberGeometry, crs ) );
591            }
592            return GeometryFactory.createMultiGeometry( members.toArray( new Geometry[members.size()] ), crs );
593        }
594    }