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