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