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 }