001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/model/spatialschema/GeometryImpl.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     53177 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 Geometry interface from package deegree.model. The implementation
054     * is abstract because only the management of the spatial reference system is unique for all
055     * geometries.
056     * <p>
057     * 
058     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
059     * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider</a>
060     * @version $Revision: 9343 $ $Date: 2007-12-27 14:30:32 +0100 (Do, 27 Dez 2007) $
061     */
062    
063    public abstract class GeometryImpl implements Geometry, Serializable {
064    
065        private static final ILogger LOG = LoggerFactory.getLogger( GeometryImpl.class );
066    
067        /** Use serialVersionUID for interoperability. */
068        private final static long serialVersionUID = 130728662284673112L;
069    
070        protected static double mute = 0.000000001;
071    
072        protected CoordinateSystem crs = null;
073    
074        protected Boundary boundary = null;
075    
076        protected Envelope envelope = null;
077    
078        protected Geometry convexHull = null;
079    
080        protected Point centroid = null;
081    
082        protected boolean empty = true;
083    
084        protected boolean valid = false;
085    
086        /**
087         * constructor that sets the spatial reference system
088         * 
089         * @param crs
090         *            new spatial reference system
091         */
092        protected GeometryImpl( CoordinateSystem crs ) {
093            setCoordinateSystem( crs );
094        }
095    
096        /**
097         * @return the spatial reference system of a geometry
098         */
099        public CoordinateSystem getCoordinateSystem() {
100            return crs;
101        }
102    
103        /**
104         * sets the spatial reference system
105         * 
106         * @param crs
107         *            new spatial reference system
108         */
109        public void setCoordinateSystem( CoordinateSystem crs ) {
110            this.crs = crs;
111            valid = false;
112        }
113    
114        /**
115         * @return a shallow copy of the geometry. this isn't realized at this level so a
116         *         CloneNotSupportedException will be thrown.
117         */
118        @Override
119        public Object clone()
120                                throws CloneNotSupportedException {
121            throw new CloneNotSupportedException();
122        }
123    
124        /**
125         * @return true if no geometry values resp. points stored within the geometry.
126         */
127        public boolean isEmpty() {
128            return empty;
129        }
130    
131        /**
132         * 
133         * @param empty
134         *            indicates the geometry as empty
135         */
136        public void setEmpty( boolean empty ) {
137            this.empty = empty;
138        }
139    
140        /**
141         * returns the boundary of the surface as general boundary
142         * 
143         */
144        public Boundary getBoundary() {
145            if ( !isValid() ) {
146                calculateParam();
147            }
148            return boundary;
149        }
150    
151        /**
152         * dummy implementation of this method
153         */
154        public void translate( double[] d ) {
155            setValid( false );
156        }
157    
158        /**
159         * <p>
160         * The operation "distance" shall return the distance between this Geometry and another
161         * Geometry. This distance is defined to be the greatest lower bound of the set of distances
162         * between all pairs of points that include one each from each of the two Geometries. A
163         * "distance" value shall be a positive number associated to distance units such as meters or
164         * standard foot. If necessary, the second geometric object shall be transformed into the same
165         * coordinate reference system as the first before the distance is calculated.
166         * </p>
167         * <p>
168         * If the geometric objects overlap, or touch, then their distance apart shall be zero. Some
169         * current implementations use a "negative" distance for such cases, but the approach is neither
170         * consistent between implementations, nor theoretically viable.
171         * </p>
172         */
173        public double distance( Geometry gmo ) {
174            try {
175                com.vividsolutions.jts.geom.Geometry jtsThis = JTSAdapter.export( this );
176                com.vividsolutions.jts.geom.Geometry jtsThat = JTSAdapter.export( gmo );
177                return jtsThis.distance( jtsThat );
178            } catch ( GeometryException e ) {
179                LOG.logError( e.getMessage(), e );
180                return -1;
181            }
182        }
183    
184        /**
185         * <p>
186         * The operation "centroid" shall return the mathematical centroid for this Geometry. The result
187         * is not guaranteed to be on the object. For heterogeneous collections of primitives, the
188         * centroid only takes into account those of the largest dimension. For example, when
189         * calculating the centroid of surfaces, an average is taken weighted by area. Since curves have
190         * no area they do not contribute to the average.
191         * </p>
192         */
193        public Point getCentroid() {
194            if ( !isValid() ) {
195                calculateParam();
196            }
197            return centroid;
198        }
199    
200        /**
201         * returns the bounding box / envelope of a geometry
202         */
203        public Envelope getEnvelope() {
204            if ( !isValid() ) {
205                calculateParam();
206            }
207            return envelope;
208        }
209    
210        /**
211         * <p>
212         * The operation "convexHull" shall return a Geometry that represents the convex hull of this
213         * Geometry.
214         * </p>
215         * This method throws an
216         * 
217         * @see java.lang.UnsupportedOperationException an may has an useful implementation in extending
218         *      classes
219         */
220        public Geometry getConvexHull() {
221            throw new UnsupportedOperationException();
222        }
223    
224        /**
225         * <p>
226         * The operation "buffer" shall return a Geometry containing all points whose distance from this
227         * Geometry is less than or equal to the "distance" passed as a parameter. The Geometry returned
228         * is in the same reference system as this original Geometry. The dimension of the returned
229         * Geometry is normally the same as the coordinate dimension - a collection of Surfaces in 2D
230         * space and a collection of Solids in 3D space, but this may be application defined.
231         * </p>
232         * This method throws an
233         * 
234         * @see java.lang.UnsupportedOperationException an may has an useful implementation in extending
235         *      classes
236         */
237        public Geometry getBuffer( double distance ) {
238            throw new UnsupportedOperationException();
239        }
240    
241        /**
242         * The Boolean valued operation "contains" shall return TRUE if this Geometry contains another
243         * Geometry.
244         * <p>
245         * 
246         * @param that
247         *            the Geometry to test (whether is is contained)
248         * @return true if the given object is contained, else false
249         */
250        public boolean contains( Geometry that ) {
251            try {
252                // let JTS do the hard work
253                com.vividsolutions.jts.geom.Geometry jtsThis = JTSAdapter.export( this );
254                com.vividsolutions.jts.geom.Geometry jtsThat = JTSAdapter.export( that );
255                return jtsThis.contains( jtsThat );
256    
257            } catch ( GeometryException e ) {
258                LOG.logError( e.getMessage(), e );
259                return false;
260            }
261        }
262    
263        /**
264         * The Boolean valued operation "contains" shall return TRUE if this Geometry contains a single
265         * point given by a coordinate.
266         * 
267         * @param position
268         *            Position to test (whether is is contained)
269         * @return true if the given object is contained, else false
270         */
271        public boolean contains( Position position ) {
272            return contains( new PointImpl( position, null ) );
273        }
274    
275        /**
276         * The Boolean valued operation "intersects" shall return TRUE if this Geometry intersects
277         * another Geometry. Within a Complex, the Primitives do not intersect one another. In general,
278         * topologically structured data uses shared geometric objects to capture intersection
279         * information.
280         * 
281         * @param that
282         *            the Geometry to intersect with
283         * @return true if the objects intersects, else false
284         */
285        public boolean intersects( Geometry that ) {
286            try {
287                // let JTS do the hard work
288                com.vividsolutions.jts.geom.Geometry jtsThis = JTSAdapter.export( this );
289                com.vividsolutions.jts.geom.Geometry jtsThat = JTSAdapter.export( that );
290                return jtsThis.intersects( jtsThat );
291    
292            } catch ( GeometryException e ) {
293                LOG.logError( "", e );
294                return false;
295            }
296        }
297    
298        /**
299         * The "union" operation shall return the set theoretic union of this Geometry and the passed
300         * Geometry.
301         * 
302         * @param that
303         *            the Geometry to unify
304         * @return intersection or null, if computation failed
305         */
306        public Geometry union( Geometry that ) {
307            Geometry union = null;
308    
309            try {
310                // let JTS do the hard work
311                com.vividsolutions.jts.geom.Geometry jtsThis = JTSAdapter.export( this );
312                com.vividsolutions.jts.geom.Geometry jtsThat = JTSAdapter.export( that );
313                com.vividsolutions.jts.geom.Geometry jtsUnion = jtsThis.union( jtsThat );
314    
315                if ( !jtsUnion.isEmpty() ) {
316                    union = JTSAdapter.wrap( jtsUnion );
317                    ( (GeometryImpl) union ).setCoordinateSystem( getCoordinateSystem() );
318                }
319            } catch ( GeometryException e ) {
320                LOG.logError( e.getLocalizedMessage(), e );
321            }
322            return union;
323        }
324    
325        /**
326         * The "intersection" operation shall return the set theoretic intersection of this
327         * <tt>Geometry</tt> and the passed <tt>Geometry</tt>.
328         * 
329         * @param that
330         *            the Geometry to intersect with
331         * @return intersection or null, if it is empty (or computation failed)
332         */
333        public Geometry intersection( Geometry that )
334                                throws GeometryException {
335    
336            Geometry intersection = null;
337    
338            // let JTS do the hard work
339            com.vividsolutions.jts.geom.Geometry jtsIntersection = null;
340            try {
341                com.vividsolutions.jts.geom.Geometry jtsThis = JTSAdapter.export( this );
342                com.vividsolutions.jts.geom.Geometry jtsThat = JTSAdapter.export( that );
343                jtsIntersection = jtsThis.intersection( jtsThat );
344            } catch ( Exception e ) {
345                LOG.logError( e.getMessage(), e );
346                throw new GeometryException( e.getLocalizedMessage() );
347            }
348    
349            if ( jtsIntersection != null && !jtsIntersection.isEmpty() ) {
350                intersection = JTSAdapter.wrap( jtsIntersection );
351                ( (GeometryImpl) intersection ).setCoordinateSystem( getCoordinateSystem() );
352            }
353    
354            return intersection;
355        }
356    
357        /**
358         * The "difference" operation shall return the set theoretic difference of this Geometry and the
359         * passed Geometry.
360         * 
361         * @param that
362         *            the Geometry to calculate the difference with
363         * @return difference or null, if it is empty (or computation failed)
364         */
365        public Geometry difference( Geometry that ) {
366            Geometry difference = null;
367    
368            try {
369                // let JTS do the hard work
370                com.vividsolutions.jts.geom.Geometry jtsThis = JTSAdapter.export( this );
371                com.vividsolutions.jts.geom.Geometry jtsThat = JTSAdapter.export( that );
372                com.vividsolutions.jts.geom.Geometry jtsDifference = jtsThis.difference( jtsThat );
373    
374                if ( !jtsDifference.isEmpty() ) {
375                    difference = JTSAdapter.wrap( jtsDifference );
376                    ( (GeometryImpl) difference ).setCoordinateSystem( getCoordinateSystem() );
377                }
378            } catch ( GeometryException e ) {
379                LOG.logError( "", e );
380            }
381            return difference;
382        }
383    
384        /**
385         * Compares the Geometry to be equal to another Geometry.
386         * 
387         * @param that
388         *            the Geometry to test for equality
389         * @return true if the objects are equal, else false
390         */
391        @Override
392        public boolean equals( Object that ) {
393            if ( ( that == null ) || !( that instanceof GeometryImpl ) ) {
394                return false;
395            }
396            if ( crs != null ) {
397                if ( !crs.equals( ( (Geometry) that ).getCoordinateSystem() ) ) {
398                    return false;
399                }
400            } else {
401                if ( ( (Geometry) that ).getCoordinateSystem() != null ) {
402                    return false;
403                }
404            }
405    
406            // do not add JTS calls here!!!!
407    
408            return true;
409    
410        }
411    
412        /**
413         * provide optimized proximity queries within for a distance . calvin added on 10/21/2003
414         */
415        public boolean isWithinDistance( Geometry that, double distance ) {
416            if ( that == null )
417                return false;
418            try {
419                // let JTS do the hard work
420                com.vividsolutions.jts.geom.Geometry jtsThis = JTSAdapter.export( this );
421                com.vividsolutions.jts.geom.Geometry jtsThat = JTSAdapter.export( that );
422                return jtsThis.isWithinDistance( jtsThat, distance );
423            } catch ( GeometryException e ) {
424                LOG.logError( e.getMessage(), e );
425                return false;
426            }
427    
428        }
429    
430        /**
431         * sets tolerance value use for topological operations
432         * 
433         * @param tolerance
434         */
435        public void setTolerance( double tolerance ) {
436            mute = tolerance;
437        }
438    
439        /**
440         * returns the tolerance value use for topological operations
441         * 
442         * @return tolerance value use for topological operations
443         */
444        public double getTolerance() {
445            return mute;
446        }
447    
448        /**
449         * 
450         * @param valid
451         *            invalidates the calculated parameters of the Geometry
452         */
453        protected void setValid( boolean valid ) {
454            this.valid = valid;
455        }
456    
457        /**
458         * @return true if the calculated parameters of the Geometry are valid and false if they must be
459         *         recalculated
460         */
461        protected boolean isValid() {
462            return valid;
463        }
464    
465        /**
466         * recalculates internal parameters
467         */
468        protected abstract void calculateParam();
469    
470        /**
471         * 
472         * @return the String representation containing the crs, empty-field and the mut-field
473         */
474        @Override
475        public String toString() {
476            String ret = null;
477            ret = "CoordinateSystem = " + crs + "\n";
478            ret += ( "empty = " + empty + "\n" );
479            ret += ( "mute = " + mute + "\n" );
480            return ret;
481        }
482    }