001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_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    }