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