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