    001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.4_testing/src/org/deegree/model/spatialschema/SurfacePatchImpl.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 javax.vecmath.Vector3d;
    041
    042    import org.deegree.model.crs.CoordinateSystem;
    043
    044    /**
    045     * default implementation of the SurfacePatch interface from package deegree.model.spatialschema. the class is abstract because it should
    046     * be specialized by derived classes <code>Polygon</code> for example
    047     *
    048     *
    049     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
    050     * @author last edited by: $Author: apoth $
    051     *
    052     * @version $Revision: 25223 $, $Date: 2010-07-09 09:30:18 +0200 (Fr, 09. Jul 2010) $
    053     */
    054    public abstract class SurfacePatchImpl implements GenericSurface, SurfacePatch, Serializable {
    055        /** Use serialVersionUID for interoperability. */
    056        private final static long serialVersionUID = 7641735268892225180L;
    057
    058        protected CoordinateSystem crs = null;
    059
    060        protected Point centroid = null;
    061
    062        protected SurfaceInterpolation interpolation = null;
    063
    064        protected Ring exteriorRing = null;
    065
    066        protected Ring[] interiorRings = null;
    067
    068        protected double area = 0;
    069
    070        protected boolean valid = false;
    071
    072        /**
    073         *
    074         * @param exteriorRing
    075         * @param interiorRings
    076         * @param crs
    077         * @throws GeometryException
    078         */
    079        protected SurfacePatchImpl( Ring exteriorRing, Ring[] interiorRings, CoordinateSystem crs ) throws GeometryException {
    080            this.exteriorRing = exteriorRing;
    081            if ( interiorRings == null ) {
    082                this.interiorRings = new Ring[0];
    083            } else {
    084                this.interiorRings = interiorRings;
    085            }
    086         // check, if the exteriorRing of the polygon is closed
    087            // and if the interiorRings (if !=null) are closed
    088            Position[] pos = this.exteriorRing.getPositions();
    089            if ( !pos[0].equals(pos[pos.length - 1] ) ) {
    090                throw new GeometryException( "The exterior ring isn't closed!" );
    091            }
    092
    093            if ( interiorRings != null ) {
    094                for ( int i = 0; i < interiorRings.length; i++ ) {
    095                    pos = interiorRings[i].getPositions();
    096                    if ( !pos[0].equals( pos[pos.length - 1] ) ) {
    097                        throw new GeometryException( "The interior ring " + i + " isn't closed!" );
    098                    }
    099                }
    100            }
    101
    102            this.crs = crs;
    103        }
    104
    105        /**
    106         * Creates a new SurfacePatchImpl object.
    107         *
    108         * @param interpolation
    109         * @param exteriorRing
    110         * @param interiorRings
    111         * @param crs
    112         *
    113         * @throws GeometryException
    114         */
    115        protected SurfacePatchImpl( SurfaceInterpolation interpolation, Position[] exteriorRing,
    116                                    Position[][] interiorRings, CoordinateSystem crs ) throws GeometryException {
    117            this.crs = crs;
    118
    119            if ( ( exteriorRing == null ) || ( exteriorRing.length < 3 ) ) {
    120                throw new GeometryException( "The exterior ring doesn't contains enough point!" );
    121            }
    122
    123            // check, if the exteriorRing of the polygon is closed
    124            // and if the interiorRings (if !=null) are closed
    125            if ( !exteriorRing[0].equals( exteriorRing[exteriorRing.length - 1] ) ) {
    126                throw new GeometryException( "The exterior ring isn't closed!" );
    127            }
    128
    129            if ( interiorRings != null ) {
    130                for ( int i = 0; i < interiorRings.length; i++ ) {
    131                    if ( !interiorRings[i][0].equals( interiorRings[i][interiorRings[i].length - 1] ) ) {
    132                        throw new GeometryException( "The interior ring " + i + " isn't closed!" );
    133                    }
    134                }
    135            }
    136
    137            this.interpolation = interpolation;
    138            this.exteriorRing = new RingImpl( exteriorRing, crs );
    139            if ( interiorRings != null ) {
    140                this.interiorRings = new Ring[interiorRings.length];
    141                for ( int i = 0; i < interiorRings.length; i++ ) {
    142                    this.interiorRings[i] = new RingImpl( interiorRings[i], crs );
    143                }
    144            }
    145
    146            setValid( false );
    147        }
    148
    149        /**
    150         * invalidates the calculated parameters of the Geometry
    151         *
    152         * @param valid
    153         */
    154        protected void setValid( boolean valid ) {
    155            this.valid = valid;
    156        }
    157
    158        /**
    159         * returns true if the calculated parameters of the Geometry are valid and false if they must be recalculated
    160         *
    161         * @return true if the calculated parameters of the Geometry are valid and false if they must be recalculated
    162         */
    163        protected boolean isValid() {
    164            return valid;
    165        }
    166
    167        public SurfaceInterpolation getInterpolation() {
    168            return interpolation;
    169        }
    170
    171        public Envelope getEnvelope() {
    172            return exteriorRing.getEnvelope();
    173        }
    174
    175        public Position[] getExteriorRing() {
    176            return exteriorRing.getPositions();
    177        }
    178
    179        public Position[][] getInteriorRings() {
    180            if ( interiorRings != null ) {
    181                Position[][] pos = new Position[interiorRings.length][];
    182                for ( int i = 0; i < pos.length; i++ ) {
    183                    pos[i] = interiorRings[i].getPositions();
    184                }
    185                return pos;
    186            }
    187            return new Position[0][0];
    188        }
    189
    190        public Ring getExterior() {
    191            return exteriorRing;
    192        }
    193
    194        public Ring[] getInterior() {
    195            return interiorRings;
    196        }
    197
    198        /**
    199         * @return -1
    200         */
    201        public double getPerimeter() {
    202            return -1;
    203        }
    204
    205        public CoordinateSystem getCoordinateSystem() {
    206            return crs;
    207        }
    208
    209        @Override
    210        public boolean equals( Object other ) {
    211            if ( ( other == null ) || !( other instanceof SurfacePatch ) ) {
    212                return false;
    213            }
    214
    215            // Assuming envelope cannot be null (always calculated)
    216            if ( !getEnvelope().equals( ( (SurfacePatch) other ).getEnvelope() ) ) {
    217                return false;
    218            }
    219
    220            // check positions of exterior ring
    221            Position[] pos1 = getExteriorRing();
    222            Position[] pos2 = ( (SurfacePatch) other ).getExteriorRing();
    223            if ( pos1.length != pos2.length ) {
    224                return false;
    225            }
    226            for ( int i = 0; i < pos2.length; i++ ) {
    227                if ( !pos1[i].equals( pos2[i] ) ) {
    228                    return false;
    229                }
    230            }
    231
    232            // Assuming either can have interiorRings set to null (not checked
    233            // by Constructor)
    234            if ( getInterior() != null ) {
    235                if ( ( (SurfacePatch) other ).getInterior() == null ) {
    236                    return false;
    237                }
    238                if ( getInterior().length != ( (SurfacePatch) other ).getInterior().length ) {
    239                    return false;
    240                }
    241                for ( int i = 0; i < getInterior().length; i++ ) {
    242                    // TODO
    243                }
    244            } else {
    245                if ( ( (SurfacePatch) other ).getInterior() != null ) {
    246                    return false;
    247                }
    248            }
    249
    250            return true;
    251        }
    252
    253        public Point getCentroid() {
    254            if ( !isValid() ) {
    255                calculateParam();
    256            }
    257            return centroid;
    258        }
    259
    260        public double getArea() {
    261            if ( !isValid() ) {
    262                calculateParam();
    263            }
    264            return area;
    265        }
    266
    267        /**
    268         * calculates the centroid (2D) and area (2D + 3D) of the surface patch.
    269         */
    270        private void calculateCentroidArea() {
    271
    272            double varea = calculateArea( exteriorRing.getPositions() );
    273
    274            Position centroid_ = calculateCentroid( exteriorRing.getPositions() );
    275
    276            double x = centroid_.getX();
    277            double y = centroid_.getY();
    278
    279            x *= varea;
    280            y *= varea;
    281
    282            double tmp = 0;
    283            if ( interiorRings != null ) {
    284                for ( int i = 0; i < interiorRings.length; i++ ) {
    285                    double dum = calculateArea( interiorRings[i].getPositions() );
    286                    tmp += dum;
    287                    Position temp = calculateCentroid( interiorRings[i].getPositions() );
    288                    x += ( temp.getX() * -dum );
    289                    y += ( temp.getY() * -dum );
    290                }
    291            }
    292
    293            area = varea - tmp;
    294            centroid = new PointImpl( x / area, y / area, crs );
    295
    296        }
    297
    298        /**
    299         * calculates the centroid and the area of the surface patch
    300         */
    301        protected void calculateParam() {
    302            calculateCentroidArea();
    303            setValid( true );
    304        }
    305
    306        /**
    307         * calculates the area of the surface patch 2D: taken from gems iv (modified) 3D: see
    308         * http://geometryalgorithms.com/Archive/algorithm_0101/#3D%20Polygons
    309         */
    310        private double calculateArea( Position[] point ) {
    311
    312            double calcArea = 0;
    313
    314            // two-dimensional
    315            if ( point[0].getCoordinateDimension() == 2 ) {
    316                int i;
    317                int j;
    318                double ai;
    319                double atmp = 0;
    320
    321                for ( i = point.length - 1, j = 0; j < point.length; i = j, j++ ) {
    322                    double xi = point[i].getX() - point[0].getX();
    323                    double yi = point[i].getY() - point[0].getY();
    324                    double xj = point[j].getX() - point[0].getX();
    325                    double yj = point[j].getY() - point[0].getY();
    326                    ai = ( xi * yj ) - ( xj * yi );
    327                    atmp += ai;
    328                }
    329                calcArea = Math.abs( atmp / 2 );
    330
    331            }
    332            // three-dimensional
    333            else if ( point[0].getCoordinateDimension() == 3 ) {
    334
    335                Vector3d planeNormal = new Vector3d();
    336                planeNormal.cross( sub( point[1], point[0] ), sub( point[2], point[1] ) );
    337                planeNormal.normalize();
    338
    339                Vector3d resultVector = new Vector3d();
    340                for ( int i = 0; i < point.length - 1; ++i ) {
    341                    Vector3d tmp = cross( point[i], point[i + 1] );
    342                    resultVector.add( tmp );
    343                }
    344                calcArea = ( planeNormal.dot( resultVector ) ) * 0.5;
    345            }
    346            return calcArea;
    347        }
    348
    349        /**
    350         * calculates the centroid of the surface patch
    351         * <p>
    352         * taken from gems iv (modified)
    353         * <p>
    354         * </p>
    355         * this method is only valid for the two-dimensional case.
    356         *
    357         * @param point
    358         * @return the centroid of given positions
    359         */
    360        protected Position calculateCentroid( Position[] point ) {
    361
    362            int i;
    363            int j;
    364            double ai;
    365            double x;
    366            double y;
    367            double atmp = 0;
    368            double xtmp = 0;
    369            double ytmp = 0;
    370
    371            // move points to the origin of the coordinate space
    372            // (to solve precision issues)
    373            double transX = point[0].getX();
    374            double transY = point[0].getY();
    375
    376            for ( i = point.length - 1, j = 0; j < point.length; i = j, j++ ) {
    377                double x1 = point[i].getX() - transX;
    378                double y1 = point[i].getY() - transY;
    379                double x2 = point[j].getX() - transX;
    380                double y2 = point[j].getY() - transY;
    381                ai = ( x1 * y2 ) - ( x2 * y1 );
    382                atmp += ai;
    383                xtmp += ( ( x2 + x1 ) * ai );
    384                ytmp += ( ( y2 + y1 ) * ai );
    385            }
    386
    387            if ( atmp != 0 ) {
    388                x = xtmp / ( 3 * atmp ) + transX;
    389                y = ytmp / ( 3 * atmp ) + transY;
    390            } else {
    391                x = point[0].getX();
    392                y = point[0].getY();
    393            }
    394
    395            return new PositionImpl( x, y );
    396        }
    397
    398        @Override
    399        public String toString() {
    400            String ret = "SurfacePatch: ";
    401            ret = "interpolation = " + interpolation + "\n";
    402            ret += "exteriorRing = \n";
    403            ret += ( exteriorRing + "\n" );
    404            ret += ( "interiorRings = " + interiorRings + "\n" );
    405            ret += ( "envelope = " + getEnvelope() + "\n" );
    406            return ret;
    407        }
    408
    409        /**
    410         * this(x,y,z) = a(x,y,z) - b(x,y,z)
    411         */
    412        private Vector3d sub( Position a, Position b ) {
    413            Vector3d result = new Vector3d( a.getX() - b.getX(), a.getY() - b.getY(), a.getZ() - b.getZ() );
    414            return result;
    415        }
    416
    417        /**
    418         * this(x,y,z) = a(x,y,z) x b(x,y,z)
    419         */
    420        private Vector3d cross( Position a, Position b ) {
    421            Vector3d result = new Vector3d( ( a.getY() * b.getZ() ) - ( a.getZ() * b.getY() ), ( a.getZ() * b.getX() )
    422                                                                                               - ( a.getX() * b.getZ() ),
    423                                            ( a.getX() * b.getY() ) - ( a.getY() * b.getX() ) );
    424            return result;
    425        }
    426    }