001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/model/spatialschema/EnvelopeImpl.java $
002    /*----------------    FILE HEADER  ------------------------------------------
003    
004     This file is part of deegree.
005     Copyright (C) 2001-2008 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 9343 2007-12-27 13:30:32Z apoth $
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(), (Position) ( (PositionImpl) max ).clone(),
118                                     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() ) && ( point.getY() >= min.getY() )
161                 && ( 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 ) && ( north1 >= north2 ) ) {
186                return true;
187            }
188    
189            if ( ( west1 >= west2 ) && ( south1 >= south2 ) && ( east1 <= east2 ) && ( north1 <= north2 ) ) {
190                return true;
191            }
192    
193            // in any other case of intersection, at least one line of the BBOX has
194            // to cross a line of the other BBOX
195            // check western boundary of box 1
196            // "touching" boxes must not intersect
197            if ( ( west1 >= west2 ) && ( west1 < east2 ) ) {
198                if ( ( south1 <= south2 ) && ( north1 > south2 ) ) {
199                    return true;
200                }
201    
202                if ( ( south1 < north2 ) && ( north1 >= north2 ) ) {
203                    return true;
204                }
205            }
206    
207            // check eastern boundary of box 1
208            // "touching" boxes must not intersect
209            if ( ( east1 > west2 ) && ( east1 <= east2 ) ) {
210                if ( ( south1 <= south2 ) && ( north1 > south2 ) ) {
211                    return true;
212                }
213    
214                if ( ( south1 < north2 ) && ( north1 >= north2 ) ) {
215                    return true;
216                }
217            }
218    
219            // check southern boundary of box 1
220            // "touching" boxes must not intersect
221            if ( ( south1 >= south2 ) && ( south1 < north2 ) ) {
222                if ( ( west1 <= west2 ) && ( east1 > west2 ) ) {
223                    return true;
224                }
225    
226                if ( ( west1 < east2 ) && ( east1 >= east2 ) ) {
227                    return true;
228                }
229            }
230    
231            // check northern boundary of box 1
232            // "touching" boxes must not intersect
233            if ( ( north1 > south2 ) && ( north1 <= north2 ) ) {
234                if ( ( west1 <= west2 ) && ( east1 > west2 ) ) {
235                    return true;
236                }
237    
238                if ( ( west1 < east2 ) && ( east1 >= east2 ) ) {
239                    return true;
240                }
241            }
242    
243            return false;
244        }
245    
246        /**
247         * returns true if all points of the submitted bounding box are within this bounding box
248         */
249        public boolean contains( Envelope bb ) {
250            Position p1 = new PositionImpl( bb.getMin().getX(), bb.getMin().getY() );
251            Position p2 = new PositionImpl( bb.getMin().getX(), bb.getMax().getY() );
252            Position p3 = new PositionImpl( bb.getMax().getX(), bb.getMin().getY() );
253            Position p4 = new PositionImpl( bb.getMax().getX(), bb.getMax().getY() );
254    
255            boolean ins = ( this.contains( p1 ) && this.contains( p2 ) && this.contains( p3 ) && this.contains( p4 ) );
256            return ins;
257        }
258    
259        /**
260         * returns a new Envelope object representing the intersection of this Envelope with the
261         * specified Envelope. * Note: If there is no intersection at all Envelope will be null.
262         * 
263         * @param bb
264         *            the Envelope to be intersected with this Envelope
265         * @return the largest Envelope contained in both the specified Envelope and in this Envelope.
266         */
267        public Envelope createIntersection( Envelope bb ) {
268            Rectangle2D rect = new Rectangle2D.Double( bb.getMin().getX(), bb.getMin().getY(), bb.getWidth(),
269                                                       bb.getHeight() );
270            Rectangle2D rect2 = new Rectangle2D.Double( this.getMin().getX(), this.getMin().getY(), this.getWidth(),
271                                                        this.getHeight() );
272    
273            if ( rect2.intersects( bb.getMin().getX(), bb.getMin().getY(), bb.getWidth(), bb.getHeight() ) ) {
274                rect = rect.createIntersection( rect2 );
275            } else {
276                rect = null;
277            }
278    
279            if ( rect == null ) {
280                return null;
281            }
282    
283            double xmin = rect.getX();
284            double ymin = rect.getY();
285            double xmax = rect.getX() + rect.getWidth();
286            double ymax = rect.getY() + rect.getHeight();
287    
288            Position p1 = new PositionImpl( xmin, ymin );
289            Position p2 = new PositionImpl( xmax, ymax );
290    
291            return new EnvelopeImpl( p1, p2, this.crs );
292        }
293    
294        /**
295         * checks if this point is completly equal to the submitted geometry
296         */
297        @Override
298        public boolean equals( Object other ) {
299            if ( ( other == null ) || !( other instanceof EnvelopeImpl ) ) {
300                return false;
301            }
302            Envelope envelope = (Envelope) other;
303            if ( ( envelope.getCoordinateSystem() == null && getCoordinateSystem() != null )
304                 || ( envelope.getCoordinateSystem() != null && getCoordinateSystem() == null )
305                 || ( getCoordinateSystem() != null && !getCoordinateSystem().equals( envelope.getCoordinateSystem() ) ) ) {
306                return false;
307            }
308    
309            return ( min.equals( ( (Envelope) other ).getMin() ) && max.equals( ( (Envelope) other ).getMax() ) );
310        }
311    
312        public Envelope getBuffer( double b ) {
313            Position bmin = new PositionImpl( new double[] { min.getX() - b, min.getY() - b } );
314            Position bmax = new PositionImpl( new double[] { max.getX() + b, max.getY() + b } );
315            return GeometryFactory.createEnvelope( bmin, bmax, getCoordinateSystem() );
316        }
317    
318        /**
319         * @see org.deegree.model.spatialschema.Envelope#merge(org.deegree.model.spatialschema.Envelope)
320         */
321        public Envelope merge( Envelope envelope )
322                                throws GeometryException {
323    
324            CoordinateSystem crs1 = this.getCoordinateSystem();
325            CoordinateSystem crs2 = envelope.getCoordinateSystem();
326    
327            LOG.logDebug( "Merging envelopes with " + crs1 + " => " + crs2 );
328    
329            if ( ( crs1 == null && crs2 != null ) || ( crs1 != null && crs2 == null )
330                 || ( crs1 != null && !crs1.equals( crs2 ) ) ) {
331                String crs1Name = null;
332                String crs2Name = null;
333                if ( crs1 != null ) {
334                    crs1Name = crs1.getName();
335                }
336                if ( crs2 != null ) {
337                    crs2Name = crs2.getName();
338                }
339                throw new GeometryException( "Cannot merge envelopes with different CRS (" + crs1Name + "/" + crs2Name
340                                             + ")!" );
341            }
342            double minx = min.getX();
343            double miny = min.getY();
344            double minz = min.getZ();
345            double maxx = max.getX();
346            double maxy = max.getY();
347            double maxz = max.getZ();
348    
349            if ( envelope != null ) {
350                if ( envelope.getMin().getX() < minx ) {
351                    minx = envelope.getMin().getX();
352                }
353                if ( envelope.getMin().getY() < miny ) {
354                    miny = envelope.getMin().getY();
355                }
356                if ( envelope.getMax().getX() > maxx ) {
357                    maxx = envelope.getMax().getX();
358                }
359                if ( envelope.getMax().getY() > maxy ) {
360                    maxy = envelope.getMax().getY();
361                }
362                if ( !Double.isNaN( maxz ) && !Double.isNaN( envelope.getMax().getZ() ) ) {
363                    if ( envelope.getMax().getZ() > maxz ) {
364                        maxz = envelope.getMax().getZ();
365                    }
366                } else if ( Double.isNaN( maxz ) ) {
367                    maxz = envelope.getMax().getZ();
368                }
369                if ( !Double.isNaN( minz ) && !Double.isNaN( envelope.getMin().getZ() ) ) {
370                    if ( envelope.getMin().getZ() < minz ) {
371                        minz = envelope.getMin().getZ();
372                    }
373                } else if ( Double.isNaN( minz ) ) {
374                    minz = envelope.getMin().getZ();
375                }
376    
377            }
378            Position minPos = GeometryFactory.createPosition( minx, miny, minz );
379            Position maxPos = GeometryFactory.createPosition( maxx, maxy, maxz );
380            return GeometryFactory.createEnvelope( minPos, maxPos, this.getCoordinateSystem() );
381        }
382    
383        /**
384         * ensures that the passed Envepole is contained within this.Envelope
385         * 
386         * @param other
387         */
388        public void expandToContain( Envelope other ) {
389            double minx = min.getX();
390            double miny = min.getY();
391            double maxx = max.getX();
392            double maxy = max.getY();
393            if ( other.getMin().getX() < minx ) {
394                minx = other.getMin().getX();
395            }
396            if ( other.getMax().getX() > maxx ) {
397                maxx = other.getMax().getX();
398            }
399            if ( other.getMin().getY() < miny ) {
400                miny = other.getMin().getY();
401            }
402            if ( other.getMax().getY() > maxy ) {
403                maxy = other.getMax().getY();
404            }
405            min = new PositionImpl( minx, miny );
406            max = new PositionImpl( maxx, maxy );
407        }
408    
409        /**
410         * translate a envelope in the direction defined by the two passed values and retiurns the
411         * resulting envelope
412         * 
413         * @param x
414         * @param y
415         */
416        public Envelope translate( double x, double y ) {
417            min = new PositionImpl( this.getMin().getX() + x, this.getMin().getY() + y );
418            max = new PositionImpl( this.getMax().getX() + x, this.getMax().getY() + y );
419            return new EnvelopeImpl( min, max, this.crs );
420        }
421    
422        /**
423         * returns the centroid of an Envelope
424         * 
425         * @return centroid of an Envelope
426         */
427        public Point getCentroid() {
428    
429            double x = min.getX() + ( max.getX() - min.getX() ) / 2d;
430            double y = min.getY() + ( max.getY() - min.getY() ) / 2d;
431            double z = 0;
432            Point point = null;
433            if ( min.getCoordinateDimension() == 3 ) {
434                z = min.getZ() + ( max.getZ() - min.getZ() ) / 2d;
435                point = new PointImpl( x, y, z, crs );
436            } else {
437                point = new PointImpl( x, y, crs );
438            }
439    
440            return point;
441        }
442    
443        @Override
444        public String toString() {
445            String ret = "min = " + min;
446            ret += ( " max = " + max );
447            return ret;
448        }
449    
450    }