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 }