001 //$HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_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 }