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 }