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    }