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