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 }