    001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.4_testing/src/org/deegree/model/spatialschema/SurfaceImpl.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.model.spatialschema;
    037
    038    import java.io.Serializable;
    039
    040    import org.deegree.framework.log.ILogger;
    041    import org.deegree.framework.log.LoggerFactory;
    042    import org.deegree.model.crs.CoordinateSystem;
    043
    044    /**
    045     * default implementation of the Surface interface from package deegree.model.spatialschema.
    046     * <p>
    047     * </p>
    048     * for simplicity of the implementation it is assumed that a surface is build from just one surface
    049     * patch. this isn't completely conform to the ISO 19107 and the OGC GAIA specification but
    050     * sufficient for most applications.
    051     * <p>
    052     * </p>
    053     * It will be extended to fulfill the complete specification as soon as possible.
    054     *
    055     * @version 05.04.2002
    056     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
    057     */
    058
    059    public class SurfaceImpl extends OrientableSurfaceImpl implements Surface, GenericSurface, Serializable {
    060        /** Use serialVersionUID for interoperability. */
    061        private final static long serialVersionUID = -2148069106391096842L;
    062
    063        private static final ILogger LOG = LoggerFactory.getLogger( SurfaceImpl.class );
    064
    065        protected SurfacePatch[] patch = null;
    066
    067        private double area = 0;
    068
    069        /**
    070         * initializes the surface with default orientation submitting one surface patch.
    071         *
    072         * @param surfacePatch
    073         *            patches of the surface.
    074         * @throws GeometryException
    075         *             will be thrown if orientation is invalid
    076         */
    077        protected SurfaceImpl( SurfacePatch surfacePatch ) throws GeometryException {
    078            this( '+', surfacePatch );
    079        }
    080
    081        /**
    082         * initializes the surface with default orientation submitting one surface patch.
    083         *
    084         * @param surfacePatches
    085         *            patches of the surface.
    086         * @throws GeometryException
    087         *             will be thrown if orientation is invalid
    088         */
    089        protected SurfaceImpl( SurfacePatch[] surfacePatches ) throws GeometryException {
    090            this( '+', surfacePatches );
    091        }
    092
    093        /**
    094         * initializes the surface with default orientation submitting one surface patch.
    095         *
    096         * @param surfacePatches
    097         *            patches of the surface.
    098         * @param crs
    099         * @throws GeometryException
    100         *             will be thrown if orientation is invalid
    101         */
    102        protected SurfaceImpl( SurfacePatch[] surfacePatches, CoordinateSystem crs ) throws GeometryException {
    103            this( '+', surfacePatches );
    104            this.crs = crs;
    105        }
    106
    107        /**
    108         * initializes the surface submitting the orientation and one surface patch.
    109         *
    110         * @param orientation
    111         *            of the surface
    112         *
    113         * @param surfacePatch
    114         *            patches of the surface.
    115         * @throws GeometryException
    116         *             will be thrown if orientation is invalid
    117         */
    118        protected SurfaceImpl( char orientation, SurfacePatch surfacePatch ) throws GeometryException {
    119            super( surfacePatch.getCoordinateSystem(), orientation );
    120
    121            patch = new SurfacePatch[] { surfacePatch };
    122
    123            setValid( false );
    124        }
    125
    126        /**
    127         * initializes the surface submitting the orientation and one surface patch.
    128         *
    129         * @param orientation
    130         *            of the surface
    131         *
    132         * @param surfacePatches
    133         *            patches of the surface.
    134         * @throws GeometryException
    135         *             will be thrown if orientation is invalid
    136         */
    137        protected SurfaceImpl( char orientation, SurfacePatch[] surfacePatches ) throws GeometryException {
    138            super( surfacePatches[0].getCoordinateSystem(), orientation );
    139            patch = surfacePatches;
    140            setValid( false );
    141        }
    142
    143        /**
    144         * initializes the surface with default orientation submitting the surfaces boundary
    145         *
    146         * @param boundary
    147         *            boundary of the surface
    148         * @throws GeometryException
    149         *             will be thrown if orientation is invalid
    150         */
    151        protected SurfaceImpl( SurfaceBoundary boundary ) throws GeometryException {
    152            this( '+', boundary );
    153        }
    154
    155        /**
    156         * initializes the surface submitting the orientation and the surfaces boundary.
    157         *
    158         * @param orientation
    159         *            of the surface
    160         *
    161         * @param boundary
    162         *            boundary of the surface
    163         *
    164         * @throws GeometryException
    165         *             will be thrown if orientation is invalid
    166         */
    167        protected SurfaceImpl( char orientation, SurfaceBoundary boundary ) throws GeometryException {
    168            // todo
    169            // extracting surface patches from the boundary
    170            super( boundary.getCoordinateSystem(), orientation );
    171
    172            this.boundary = boundary;
    173        }
    174
    175        /**
    176         * calculates the centroid and area of the surface
    177         */
    178        private void calculateCentroidArea() {
    179            double x = 0;
    180            double y = 0;
    181            area = 0;
    182            for ( int i = 0; i < patch.length; i++ ) {
    183                if ( patch[i].getCentroid() != null && patch[i].getArea() > 0 ) {
    184                    x += ( patch[i].getCentroid().getX() * patch[i].getArea() );
    185                    y += ( patch[i].getCentroid().getY() * patch[i].getArea() );
    186                }
    187                if ( patch[i].getArea() > 0 ) {
    188                    area += patch[i].getArea();
    189                }
    190            }
    191            if ( area > 0 ) {
    192                centroid = GeometryFactory.createPoint( x / area, y / area, this.crs );
    193            } else {
    194                // fall back
    195                centroid = GeometryFactory.createPoint( 0, 0, this.crs );
    196            }
    197        }
    198
    199        /**
    200         * calculates the boundary and area of the surface
    201         */
    202        private void calculateBoundary() {
    203            // TODO
    204            // consider more than one patch
    205            try {
    206                Ring ext = new RingImpl( patch[0].getExteriorRing(), crs );
    207                Position[][] inn_ = patch[0].getInteriorRings();
    208                Ring[] inn = null;
    209
    210                if ( inn_ != null ) {
    211                    inn = new RingImpl[inn_.length];
    212
    213                    for ( int i = 0; i < inn_.length; i++ ) {
    214                        inn[i] = new RingImpl( inn_[i], crs );
    215                    }
    216                }
    217                boundary = new SurfaceBoundaryImpl( ext, inn );
    218            } catch ( Exception e ) {
    219                LOG.logError( e.getMessage(), e );
    220                throw new RuntimeException( e.getMessage(), e );
    221            }
    222        }
    223
    224        /**
    225         * calculates area, centroid and the envelope of the surface
    226         */
    227        @Override
    228        protected void calculateParam() {
    229            calculateCentroidArea();
    230            try {
    231                calculateEnvelope();
    232            } catch ( GeometryException e ) {
    233                LOG.logError( e.getMessage(), e );
    234            }
    235            calculateBoundary();
    236            setValid( true );
    237        }
    238
    239        /**
    240         * calculates the envelope of the surface
    241         */
    242        private void calculateEnvelope()
    243                                throws GeometryException {
    244
    245            envelope = patch[0].getEnvelope();
    246            for ( int i = 1; i < patch.length; i++ ) {
    247                envelope = envelope.merge( patch[i].getEnvelope() );
    248            }
    249            envelope = GeometryFactory.createEnvelope( envelope.getMin(), envelope.getMax(), getCoordinateSystem() );
    250
    251        }
    252
    253        /**
    254         * returns the length of all boundaries of the surface in a reference system appropriate for
    255         * measuring distances.
    256         */
    257        public double getPerimeter() {
    258            return -1;
    259        }
    260
    261        /**
    262         * The operation "area" shall return the area of this GenericSurface. The area of a 2
    263         * dimensional geometric object shall be a numeric measure of its surface area Since area is an
    264         * accumulation (integral) of the product of two distances, its return value shall be in a unit
    265         * of measure appropriate for measuring distances squared.
    266         */
    267        public double getArea() {
    268            if ( !isValid() ) {
    269                calculateParam();
    270            }
    271            return area;
    272        }
    273
    274        public SurfaceBoundary getSurfaceBoundary() {
    275            if ( !isValid() ) {
    276                calculateParam();
    277            }
    278            return (SurfaceBoundary) boundary;
    279        }
    280
    281        public int getNumberOfSurfacePatches() {
    282            return patch.length;
    283        }
    284
    285        public SurfacePatch getSurfacePatchAt( int index )
    286                                throws GeometryException {
    287            if ( index >= patch.length ) {
    288                throw new GeometryException( "invalid index/position to get a patch!" );
    289            }
    290            return patch[index];
    291        }
    292
    293        /**
    294         * checks if this surface is completly equal to the submitted geometry
    295         *
    296         * @param other
    297         *            object to compare to
    298         */
    299        @Override
    300        public boolean equals( Object other ) {
    301            if ( !super.equals( other ) ) {
    302                return false;
    303            }
    304            if ( !( other instanceof SurfaceImpl ) ) {
    305                return false;
    306            }
    307            if ( envelope == null ) {
    308                try {
    309                    calculateEnvelope();
    310                } catch ( GeometryException e1 ) {
    311                    return false;
    312                }
    313            }
    314            if ( !envelope.equals( ( (Geometry) other ).getEnvelope() ) ) {
    315                return false;
    316            }
    317            try {
    318                for ( int i = 0; i < patch.length; i++ ) {
    319                    if ( !patch[i].equals( ( (Surface) other ).getSurfacePatchAt( i ) ) ) {
    320                        return false;
    321                    }
    322                }
    323            } catch ( Exception e ) {
    324                return false;
    325            }
    326            return true;
    327        }
    328
    329        /**
    330         * The operation "dimension" shall return the inherent dimension of this Geometry, which shall
    331         * be less than or equal to the coordinate dimension. The dimension of a collection of geometric
    332         * objects shall be the largest dimension of any of its pieces. Points are 0-dimensional, curves
    333         * are 1-dimensional, surfaces are 2-dimensional, and solids are 3-dimensional.
    334         */
    335        public int getDimension() {
    336            return 2;
    337        }
    338
    339        /**
    340         * The operation "coordinateDimension" shall return the dimension of the coordinates that define
    341         * this Geometry, which must be the same as the coordinate dimension of the coordinate reference
    342         * system for this Geometry.
    343         */
    344        public int getCoordinateDimension() {
    345            return patch[0].getExteriorRing()[0].getCoordinateDimension();
    346        }
    347
    348        /**
    349         * @return a shallow copy of the geometry
    350         */
    351        @Override
    352        public Object clone() {
    353            Surface s = null;
    354            try {
    355                Position[] ext = patch[0].getExteriorRing();
    356                Position[] c_ext = new Position[ext.length];
    357                Position[][] in = patch[0].getInteriorRings();
    358                Position[][] c_in = new Position[in.length][];
    359                    for ( int i = 0; i < c_ext.length; i++ ) {
    360                        c_ext[i] = new PositionImpl( ext[i].getX(), ext[i].getY(), ext[i].getZ() );
    361                    }
    362                    for ( int i = 0; i < in.length; i++ ) {
    363                        c_in[i] = new Position[in[i].length];
    364                        for ( int j = 0; j < in[i].length; j++ ) {
    365                            c_in[i][j] = new PositionImpl( in[i][j].getX(), in[i][j].getY(), in[i][j].getX() );
    366                        }
    367                    }
    368
    369                s = GeometryFactory.createSurface( c_ext, c_in, patch[0].getInterpolation(), getCoordinateSystem() );
    370            } catch ( Exception e ) {
    371                LOG.logError( e.getMessage(), e );
    372            }
    373
    374            return s;
    375        }
    376
    377        /**
    378         * translate each point of the surface with the values of the submitted double array.
    379         */
    380        @Override
    381        public void translate( double[] d ) {
    382            for ( int i = 0; i < patch.length; i++ ) {
    383                Position[] ext = patch[i].getExteriorRing();
    384                Position[][] inn = patch[i].getInteriorRings();
    385                for ( int j = 0; j < ext.length; j++ ) {
    386                    ext[j].translate( d );
    387                }
    388                if ( inn != null ) {
    389                    for ( int j = 0; j < inn.length; j++ ) {
    390                        for ( int k = 0; k < inn[j].length; k++ ) {
    391                            inn[j][k].translate( d );
    392                        }
    393                    }
    394                }
    395            }
    396            setValid( false );
    397        }
    398
    399        /**
    400         * The boolean valued operation "intersects" shall return TRUE if this <tt>SurfaceImpl</tt>
    401         * intersects with the given <tt>Geometry</t>.
    402         * Within a <tt>Complex</tt>, the <tt>Primitives</tt> do not
    403         * intersect one another. In general, topologically structured data uses
    404         * shared geometric objects to capture intersection information.
    405         * @param gmo the <tt>Geometry</tt> to test for intersection
    406         * @return true if the <tt>Geometry</tt> intersects with this
    407         */
    408        @Override
    409        public boolean intersects( Geometry gmo ) {
    410            if ( !isValid() ) {
    411                calculateParam();
    412            }
    413
    414            for ( int i = 0; i < patch.length; i++ ) {
    415                if ( patch[i].intersects( gmo ) ) {
    416                    return true;
    417                }
    418            }
    419            return false;
    420        }
    421
    422        /**
    423         * The Boolean valued operation "contains" shall return TRUE if this Geometry contains a single
    424         * point given by a coordinate.
    425         * <p>
    426         * </p>
    427         */
    428        @Override
    429        public boolean contains( Position position ) {
    430            return contains( new PointImpl( position, null ) );
    431        }
    432
    433        /**
    434         * The Boolean valued operation "contains" shall return TRUE if this Geometry contains another
    435         * Geometry.
    436         * <p>
    437         * </p>
    438         */
    439        @Override
    440        public boolean contains( Geometry gmo ) {
    441            if ( !isValid() ) {
    442                calculateParam();
    443            }
    444            return boundary.contains( gmo );
    445        }
    446
    447        /**
    448         *
    449         *
    450         * @return the Stringrepresenation of this surface.
    451         */
    452        @Override
    453        public String toString() {
    454            StringBuffer ret = new StringBuffer( 2000 );
    455            ret.append( "\n------------------------------------------\n" );
    456            ret.append( getClass().getName() ).append( ":\n" );
    457            ret.append( "envelope = " ).append( envelope ).append( "\n" );
    458            ret.append( "patch = " ).append( patch.length ).append( "\n" );
    459            for ( int i = 0; i < patch.length; i++ ) {
    460                Position[] pos = patch[i].getExteriorRing();
    461                ret.append( "Exterior Ring: \n" );
    462                ret.append( "length: " ).append( pos.length ).append( "\n" );
    463                for ( int j = 0; j < pos.length; j++ ) {
    464                    ret.append( pos[j] + "\n" );
    465                }
    466            }
    467            ret.append( "\n------------------------------------------\n" );
    468            return ret.toString();
    469        }
    470    }