001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/model/spatialschema/EnvelopeImpl.java $ 002 /*---------------- FILE HEADER ------------------------------------------ 003 004 This file is part of deegree. 005 Copyright (C) 2001-2006 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.awt.geom.Rectangle2D; 047 import java.io.Serializable; 048 049 import org.deegree.framework.log.ILogger; 050 import org.deegree.framework.log.LoggerFactory; 051 import org.deegree.model.crs.CoordinateSystem; 052 053 /** 054 * a boundingbox as child of a Polygon isn't part of the iso19107 spec but it simplifies the 055 * geometry handling 056 * 057 * <P> 058 * ------------------------------------------------------------ 059 * </P> 060 * 061 * @author Andreas Poth href="mailto:poth@lat-lon.de" 062 * @author Markus Bedel href="mailto:bedel@giub.uni-bonn.de" 063 * @version $Id: EnvelopeImpl.java 6411 2007-03-27 15:12:52Z aschmitz $ 064 */ 065 public class EnvelopeImpl implements Envelope, Serializable { 066 067 private static final ILogger LOG = LoggerFactory.getLogger( EnvelopeImpl.class ); 068 069 /** Use serialVersionUID for interoperability. */ 070 private final static long serialVersionUID = 1081219767894344990L; 071 072 protected Position max = null; 073 074 protected Position min = null; 075 076 protected CoordinateSystem crs = null; 077 078 /** 079 * Creates a new EnvelopeImpl object. 080 */ 081 protected EnvelopeImpl() { 082 this.min = new PositionImpl(); 083 this.max = new PositionImpl(); 084 } 085 086 /** 087 * Creates a new EnvelopeImpl object. 088 * 089 * @param min 090 * @param max 091 */ 092 protected EnvelopeImpl( Position min, Position max ) { 093 this.min = min; 094 this.max = max; 095 } 096 097 /** 098 * Creates a new EnvelopeImpl object. 099 * 100 * @param min 101 * @param max 102 * @param crs 103 */ 104 protected EnvelopeImpl( Position min, Position max, CoordinateSystem crs ) { 105 this.min = min; 106 this.max = max; 107 this.crs = crs; 108 } 109 110 /** 111 * 112 * 113 * @return a shallow copy of this Envelope 114 */ 115 @Override 116 public Object clone() { 117 return new EnvelopeImpl( (Position) ( (PositionImpl) min ).clone(), 118 (Position) ( (PositionImpl) max ).clone(), this.crs ); 119 } 120 121 /** 122 * returns the spatial reference system of a geometry 123 */ 124 public CoordinateSystem getCoordinateSystem() { 125 return crs; 126 } 127 128 /** 129 * returns the minimum coordinates of bounding box 130 */ 131 public Position getMin() { 132 return min; 133 } 134 135 /** 136 * returns the maximum coordinates of bounding box 137 */ 138 public Position getMax() { 139 return max; 140 } 141 142 /** 143 * returns the width of bounding box 144 */ 145 public double getWidth() { 146 return this.getMax().getX() - this.getMin().getX(); 147 } 148 149 /** 150 * returns the height of bounding box 151 */ 152 public double getHeight() { 153 return this.getMax().getY() - this.getMin().getY(); 154 } 155 156 /** 157 * returns true if the bounding box conatins the specified Point 158 */ 159 public boolean contains( Position point ) { 160 if ( ( point.getX() >= min.getX() ) && ( point.getX() <= max.getX() ) 161 && ( point.getY() >= min.getY() ) && ( point.getY() <= max.getY() ) ) { 162 return true; 163 } 164 165 return false; 166 } 167 168 /** 169 * returns true if this envelope and the submitted intersects 170 */ 171 public boolean intersects( Envelope bb ) { 172 // coordinates of this Envelope's BBOX 173 double west1 = min.getX(); 174 double south1 = min.getY(); 175 double east1 = max.getX(); 176 double north1 = max.getY(); 177 178 // coordinates of the other Envelope's BBOX 179 double west2 = bb.getMin().getX(); 180 double south2 = bb.getMin().getY(); 181 double east2 = bb.getMax().getX(); 182 double north2 = bb.getMax().getY(); 183 184 // special cases: one box lays completly inside the other one 185 if ( ( west1 <= west2 ) && ( south1 <= south2 ) && ( east1 >= east2 ) 186 && ( north1 >= north2 ) ) { 187 return true; 188 } 189 190 if ( ( west1 >= west2 ) && ( south1 >= south2 ) && ( east1 <= east2 ) 191 && ( north1 <= north2 ) ) { 192 return true; 193 } 194 195 // in any other case of intersection, at least one line of the BBOX has 196 // to cross a line of the other BBOX 197 // check western boundary of box 1 198 // "touching" boxes must not intersect 199 if ( ( west1 >= west2 ) && ( west1 < east2 ) ) { 200 if ( ( south1 <= south2 ) && ( north1 > south2 ) ) { 201 return true; 202 } 203 204 if ( ( south1 < north2 ) && ( north1 >= north2 ) ) { 205 return true; 206 } 207 } 208 209 // check eastern boundary of box 1 210 // "touching" boxes must not intersect 211 if ( ( east1 > west2 ) && ( east1 <= east2 ) ) { 212 if ( ( south1 <= south2 ) && ( north1 > south2 ) ) { 213 return true; 214 } 215 216 if ( ( south1 < north2 ) && ( north1 >= north2 ) ) { 217 return true; 218 } 219 } 220 221 // check southern boundary of box 1 222 // "touching" boxes must not intersect 223 if ( ( south1 >= south2 ) && ( south1 < north2 ) ) { 224 if ( ( west1 <= west2 ) && ( east1 > west2 ) ) { 225 return true; 226 } 227 228 if ( ( west1 < east2 ) && ( east1 >= east2 ) ) { 229 return true; 230 } 231 } 232 233 // check northern boundary of box 1 234 // "touching" boxes must not intersect 235 if ( ( north1 > south2 ) && ( north1 <= north2 ) ) { 236 if ( ( west1 <= west2 ) && ( east1 > west2 ) ) { 237 return true; 238 } 239 240 if ( ( west1 < east2 ) && ( east1 >= east2 ) ) { 241 return true; 242 } 243 } 244 245 return false; 246 } 247 248 /** 249 * returns true if all points of the submitted bounding box are within this bounding box 250 */ 251 public boolean contains( Envelope bb ) { 252 Position p1 = new PositionImpl( bb.getMin().getX(), bb.getMin().getY() ); 253 Position p2 = new PositionImpl( bb.getMin().getX(), bb.getMax().getY() ); 254 Position p3 = new PositionImpl( bb.getMax().getX(), bb.getMin().getY() ); 255 Position p4 = new PositionImpl( bb.getMax().getX(), bb.getMax().getY() ); 256 257 boolean ins = ( this.contains( p1 ) && this.contains( p2 ) && this.contains( p3 ) && this.contains( p4 ) ); 258 return ins; 259 } 260 261 /** 262 * returns a new Envelope object representing the intersection of this Envelope with the 263 * specified Envelope. * Note: If there is no intersection at all Envelope will be null. 264 * 265 * @param bb 266 * the Envelope to be intersected with this Envelope 267 * @return the largest Envelope contained in both the specified Envelope and in this Envelope. 268 */ 269 public Envelope createIntersection( Envelope bb ) { 270 Rectangle2D rect = new Rectangle2D.Double( bb.getMin().getX(), bb.getMin().getY(), 271 bb.getWidth(), bb.getHeight() ); 272 Rectangle2D rect2 = new Rectangle2D.Double( this.getMin().getX(), this.getMin().getY(), 273 this.getWidth(), this.getHeight() ); 274 275 if ( rect2.intersects( bb.getMin().getX(), bb.getMin().getY(), bb.getWidth(), 276 bb.getHeight() ) ) { 277 rect = rect.createIntersection( rect2 ); 278 } else { 279 rect = null; 280 } 281 282 if ( rect == null ) { 283 return null; 284 } 285 286 double xmin = rect.getX(); 287 double ymin = rect.getY(); 288 double xmax = rect.getX() + rect.getWidth(); 289 double ymax = rect.getY() + rect.getHeight(); 290 291 Position p1 = new PositionImpl( xmin, ymin ); 292 Position p2 = new PositionImpl( xmax, ymax ); 293 294 return new EnvelopeImpl( p1, p2, this.crs ); 295 } 296 297 /** 298 * checks if this point is completly equal to the submitted geometry 299 */ 300 @Override 301 public boolean equals( Object other ) { 302 if ( ( other == null ) || !( other instanceof EnvelopeImpl ) ) { 303 return false; 304 } 305 Envelope envelope = (Envelope) other; 306 if ( ( envelope.getCoordinateSystem() == null && getCoordinateSystem() != null ) 307 || ( envelope.getCoordinateSystem() != null && getCoordinateSystem() == null ) 308 || ( getCoordinateSystem() != null && !getCoordinateSystem().equals( 309 envelope.getCoordinateSystem() ) ) ) { 310 return false; 311 } 312 313 return ( min.equals( ( (Envelope) other ).getMin() ) && max.equals( ( (Envelope) other ).getMax() ) ); 314 } 315 316 public Envelope getBuffer( double b ) { 317 Position bmin = new PositionImpl( new double[] { min.getX() - b, min.getY() - b } ); 318 Position bmax = new PositionImpl( new double[] { max.getX() + b, max.getY() + b } ); 319 return GeometryFactory.createEnvelope( bmin, bmax, getCoordinateSystem() ); 320 } 321 322 /** 323 * @see org.deegree.model.spatialschema.Envelope#merge(org.deegree.model.spatialschema.Envelope) 324 */ 325 public Envelope merge( Envelope envelope ) 326 throws GeometryException { 327 328 CoordinateSystem crs1 = this.getCoordinateSystem(); 329 CoordinateSystem crs2 = envelope.getCoordinateSystem(); 330 331 LOG.logDebug( "Merging envelopes with " + crs1 + " => " + crs2 ); 332 333 if ( ( crs1 == null && crs2 != null ) || ( crs1 != null && crs2 == null ) 334 || ( crs1 != null && !crs1.equals( crs2 ) ) ) { 335 String crs1Name = null; 336 String crs2Name = null; 337 if ( crs1 != null ) { 338 crs1Name = crs1.getName(); 339 } 340 if ( crs2 != null ) { 341 crs2Name = crs2.getName(); 342 } 343 throw new GeometryException( "Cannot merge envelopes with different CRS (" + crs1Name 344 + "/" + crs2Name + ")!" ); 345 } 346 double minx = min.getX(); 347 double miny = min.getY(); 348 double minz = min.getZ(); 349 double maxx = max.getX(); 350 double maxy = max.getY(); 351 double maxz = max.getZ(); 352 353 if ( envelope != null ) { 354 if ( envelope.getMin().getX() < minx ) { 355 minx = envelope.getMin().getX(); 356 } 357 if ( envelope.getMin().getY() < miny ) { 358 miny = envelope.getMin().getY(); 359 } 360 if ( envelope.getMax().getX() > maxx ) { 361 maxx = envelope.getMax().getX(); 362 } 363 if ( envelope.getMax().getY() > maxy ) { 364 maxy = envelope.getMax().getY(); 365 } 366 if ( !Double.isNaN( maxz ) && !Double.isNaN( envelope.getMax().getZ() ) ) { 367 if ( envelope.getMax().getZ() > maxz ) { 368 maxz = envelope.getMax().getZ(); 369 } 370 } else if ( Double.isNaN( maxz ) ) { 371 maxz = envelope.getMax().getZ(); 372 } 373 if ( !Double.isNaN( minz ) && !Double.isNaN( envelope.getMin().getZ() ) ) { 374 if ( envelope.getMin().getZ() < minz ) { 375 minz = envelope.getMin().getZ(); 376 } 377 } else if ( Double.isNaN( minz ) ) { 378 minz = envelope.getMin().getZ(); 379 } 380 381 } 382 Position minPos = GeometryFactory.createPosition( minx, miny, minz ); 383 Position maxPos = GeometryFactory.createPosition( maxx, maxy, maxz ); 384 return GeometryFactory.createEnvelope( minPos, maxPos, this.getCoordinateSystem() ); 385 } 386 387 /** 388 * ensures that the passed Envepole is contained within this.Envelope 389 * 390 * @param other 391 */ 392 public void expandToContain( Envelope other ) { 393 double minx = min.getX(); 394 double miny = min.getY(); 395 double maxx = max.getX(); 396 double maxy = max.getY(); 397 if ( other.getMin().getX() < minx ) { 398 minx = other.getMin().getX(); 399 } 400 if ( other.getMax().getX() > maxx ) { 401 maxx = other.getMax().getX(); 402 } 403 if ( other.getMin().getY() < miny ) { 404 miny = other.getMin().getY(); 405 } 406 if ( other.getMax().getY() > maxy ) { 407 maxy = other.getMax().getY(); 408 } 409 min = new PositionImpl( minx, miny ); 410 max = new PositionImpl( maxx, maxy ); 411 } 412 413 /** 414 * translate a envelope in the direction defined by the two passed values and retiurns the 415 * resulting envelope 416 * 417 * @param x 418 * @param y 419 */ 420 public Envelope translate( double x, double y ) { 421 min = new PositionImpl( this.getMin().getX() + x, this.getMin().getY() + y ); 422 max = new PositionImpl( this.getMax().getX() + x, this.getMax().getY() + y ); 423 return new EnvelopeImpl( min, max, this.crs ); 424 } 425 426 /** 427 * returns the centroid of an Envelope 428 * 429 * @return centroid of an Envelope 430 */ 431 public Point getCentroid() { 432 433 double x = min.getX() + ( max.getX() - min.getX() ) / 2d; 434 double y = min.getY() + ( max.getY() - min.getY() ) / 2d; 435 double z = 0; 436 Point point = null; 437 if ( min.getCoordinateDimension() == 3 ) { 438 z = min.getZ() + ( max.getZ() - min.getZ() ) / 2d; 439 point = new PointImpl( x, y, z, crs ); 440 } else { 441 point = new PointImpl( x, y, crs ); 442 } 443 444 return point; 445 } 446 447 @Override 448 public String toString() { 449 String ret = "min = " + min; 450 ret += ( " max = " + max ); 451 return ret; 452 } 453 454 }