001    // License: GPL. For details, see LICENSE file.
002    package org.openstreetmap.josm.data.osm;
003    
004    import java.util.ArrayList;
005    import java.util.Arrays;
006    import java.util.List;
007    
008    import org.openstreetmap.josm.data.Bounds;
009    import org.openstreetmap.josm.data.coor.LatLon;
010    import org.openstreetmap.josm.tools.Utils;
011    
012    public class BBox {
013    
014        private double xmin = Double.POSITIVE_INFINITY;
015        private double xmax = Double.NEGATIVE_INFINITY;
016        private double ymin = Double.POSITIVE_INFINITY;
017        private double ymax = Double.NEGATIVE_INFINITY;
018    
019        public BBox(Bounds bounds) {
020            add(bounds.getMin());
021            add(bounds.getMax());
022        }
023    
024        public BBox(LatLon a, LatLon b) {
025            add(a);
026            add(b);
027        }
028    
029        public BBox(BBox copy) {
030            this.xmin = copy.xmin;
031            this.xmax = copy.xmax;
032            this.ymin = copy.ymin;
033            this.ymax = copy.ymax;
034        }
035    
036        public BBox(double a_x, double a_y, double b_x, double b_y)  {
037            xmin = Math.min(a_x, b_x);
038            xmax = Math.max(a_x, b_x);
039            ymin = Math.min(a_y, b_y);
040            ymax = Math.max(a_y, b_y);
041            sanity();
042        }
043    
044        public BBox(Way w) {
045            for (Node n : w.getNodes()) {
046                LatLon coor = n.getCoor();
047                if (coor == null) {
048                    continue;
049                }
050                add(coor);
051            }
052        }
053    
054        public BBox(Node n) {
055            LatLon coor = n.getCoor();
056            if (coor == null) {
057                xmin = xmax = ymin = ymax = 0;
058            } else {
059                xmin = xmax = coor.lon();
060                ymin = ymax = coor.lat();
061            }
062        }
063    
064        private void sanity()  {
065            if (xmin < -180.0) {
066                xmin = -180.0;
067            }
068            if (xmax >  180.0) {
069                xmax =  180.0;
070            }
071            if (ymin <  -90.0) {
072                ymin =  -90.0;
073            }
074            if (ymax >   90.0) {
075                ymax =   90.0;
076            }
077        }
078    
079        public void add(LatLon c) {
080            add(c.lon(), c.lat());
081        }
082    
083        /**
084         * Extends this bbox to include the point (x, y)
085         */
086        public void add(double x, double y) {
087            xmin = Math.min(xmin, x);
088            xmax = Math.max(xmax, x);
089            ymin = Math.min(ymin, y);
090            ymax = Math.max(ymax, y);
091            sanity();
092        }
093    
094        public void add(BBox box) {
095            add(box.getTopLeft());
096            add(box.getBottomRight());
097        }
098    
099        public void addPrimitive(OsmPrimitive primitive, double extraSpace) {
100            BBox primBbox = primitive.getBBox();
101            add(primBbox.xmin - extraSpace, primBbox.ymin - extraSpace);
102            add(primBbox.xmax + extraSpace, primBbox.ymax + extraSpace);
103        }
104    
105        public double height() {
106            return ymax-ymin;
107        }
108    
109        public double width() {
110            return xmax-xmin;
111        }
112    
113        /**
114         * Tests, weather the bbox b lies completely inside
115         * this bbox.
116         */
117        public boolean bounds(BBox b) {
118            if (!(xmin <= b.xmin) ||
119                    !(xmax >= b.xmax) ||
120                    !(ymin <= b.ymin) ||
121                    !(ymax >= b.ymax))
122                return false;
123            return true;
124        }
125    
126        /**
127         * Tests, weather the Point c lies within the bbox.
128         */
129        public boolean bounds(LatLon c) {
130            if ((xmin <= c.lon()) &&
131                    (xmax >= c.lon()) &&
132                    (ymin <= c.lat()) &&
133                    (ymax >= c.lat()))
134                return true;
135            return false;
136        }
137    
138        /**
139         * Tests, weather two BBoxes intersect as an area.
140         * I.e. whether there exists a point that lies in both of them.
141         */
142        public boolean intersects(BBox b) {
143            if (xmin > b.xmax)
144                return false;
145            if (xmax < b.xmin)
146                return false;
147            if (ymin > b.ymax)
148                return false;
149            if (ymax < b.ymin)
150                return false;
151            return true;
152        }
153    
154        /**
155         * Returns a list of all 4 corners of the bbox rectangle.
156         */
157        public List<LatLon> points()  {
158            LatLon p1 = new LatLon(ymin, xmin);
159            LatLon p2 = new LatLon(ymin, xmax);
160            LatLon p3 = new LatLon(ymax, xmin);
161            LatLon p4 = new LatLon(ymax, xmax);
162            List<LatLon> ret = new ArrayList<LatLon>(4);
163            ret.add(p1);
164            ret.add(p2);
165            ret.add(p3);
166            ret.add(p4);
167            return ret;
168        }
169    
170        public LatLon getTopLeft() {
171            return new LatLon(ymax, xmin);
172        }
173    
174        public LatLon getBottomRight() {
175            return new LatLon(ymin, xmax);
176        }
177        
178        public LatLon getCenter() {
179            return new LatLon(ymin + (ymax-ymin)/2.0, xmin + (xmax-xmin)/2.0);
180        }
181    
182        @Override
183        public int hashCode() {
184            return (int)(ymin * xmin);
185        }
186    
187        @Override
188        public boolean equals(Object o) {
189            if (o instanceof BBox) {
190                BBox b = (BBox)o;
191                return b.xmax == xmax && b.ymax == ymax && b.xmin == xmin && b.ymin == ymin;
192            } else
193                return false;
194        }
195    
196        @Override
197        public String toString() {
198            return "[ x: " + xmin + " -> " + xmax +
199            ", y: " + ymin + " -> " + ymax + " ]";
200        }
201    
202        public String toStringCSV(String separator) {
203            return Utils.join(separator, Arrays.asList(
204                    LatLon.cDdFormatter.format(xmin),
205                    LatLon.cDdFormatter.format(ymin),
206                    LatLon.cDdFormatter.format(xmax),
207                    LatLon.cDdFormatter.format(ymax)));
208        }
209    }