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    }