001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.data.osm; 003 004import java.awt.geom.Rectangle2D; 005import java.util.Arrays; 006 007import org.openstreetmap.josm.data.coor.LatLon; 008import org.openstreetmap.josm.data.coor.QuadTiling; 009import org.openstreetmap.josm.tools.Utils; 010 011public class BBox { 012 013 private double xmin = Double.POSITIVE_INFINITY; 014 private double xmax = Double.NEGATIVE_INFINITY; 015 private double ymin = Double.POSITIVE_INFINITY; 016 private double ymax = Double.NEGATIVE_INFINITY; 017 018 /** 019 * Constructs a new {@code BBox} defined by a single point. 020 * 021 * @param x X coordinate 022 * @param y Y coordinate 023 * @since 6203 024 */ 025 public BBox(final double x, final double y) { 026 xmax = xmin = x; 027 ymax = ymin = y; 028 sanity(); 029 } 030 031 /** 032 * Constructs a new {@code BBox} defined by points <code>a</code> and <code>b</code>. 033 * Result is minimal BBox containing both points. 034 * 035 * @param a first point 036 * @param b second point 037 */ 038 public BBox(LatLon a, LatLon b) { 039 this(a.lon(), a.lat(), b.lon(), b.lat()); 040 } 041 042 /** 043 * Constructs a new {@code BBox} from another one. 044 * 045 * @param copy the BBox to copy 046 */ 047 public BBox(BBox copy) { 048 this.xmin = copy.xmin; 049 this.xmax = copy.xmax; 050 this.ymin = copy.ymin; 051 this.ymax = copy.ymax; 052 } 053 054 public BBox(double a_x, double a_y, double b_x, double b_y) { 055 056 if (a_x > b_x) { 057 xmax = a_x; 058 xmin = b_x; 059 } else { 060 xmax = b_x; 061 xmin = a_x; 062 } 063 064 if (a_y > b_y) { 065 ymax = a_y; 066 ymin = b_y; 067 } else { 068 ymax = b_y; 069 ymin = a_y; 070 } 071 072 sanity(); 073 } 074 075 public BBox(Way w) { 076 for (Node n : w.getNodes()) { 077 LatLon coor = n.getCoor(); 078 if (coor == null) { 079 continue; 080 } 081 add(coor); 082 } 083 } 084 085 public BBox(Node n) { 086 LatLon coor = n.getCoor(); 087 if (coor == null) { 088 xmin = xmax = ymin = ymax = 0; 089 } else { 090 xmin = xmax = coor.lon(); 091 ymin = ymax = coor.lat(); 092 } 093 } 094 095 private void sanity() { 096 if (xmin < -180.0) { 097 xmin = -180.0; 098 } 099 if (xmax > 180.0) { 100 xmax = 180.0; 101 } 102 if (ymin < -90.0) { 103 ymin = -90.0; 104 } 105 if (ymax > 90.0) { 106 ymax = 90.0; 107 } 108 } 109 110 public final void add(LatLon c) { 111 add(c.lon(), c.lat()); 112 } 113 114 /** 115 * Extends this bbox to include the point (x, y) 116 */ 117 public final void add(double x, double y) { 118 xmin = Math.min(xmin, x); 119 xmax = Math.max(xmax, x); 120 ymin = Math.min(ymin, y); 121 ymax = Math.max(ymax, y); 122 sanity(); 123 } 124 125 public final void add(BBox box) { 126 xmin = Math.min(xmin, box.xmin); 127 xmax = Math.max(xmax, box.xmax); 128 ymin = Math.min(ymin, box.ymin); 129 ymax = Math.max(ymax, box.ymax); 130 sanity(); 131 } 132 133 public void addPrimitive(OsmPrimitive primitive, double extraSpace) { 134 BBox primBbox = primitive.getBBox(); 135 add(primBbox.xmin - extraSpace, primBbox.ymin - extraSpace); 136 add(primBbox.xmax + extraSpace, primBbox.ymax + extraSpace); 137 } 138 139 public double height() { 140 return ymax-ymin; 141 } 142 143 public double width() { 144 return xmax-xmin; 145 } 146 147 /** 148 * Tests, weather the bbox b lies completely inside 149 * this bbox. 150 */ 151 public boolean bounds(BBox b) { 152 if (!(xmin <= b.xmin) || 153 !(xmax >= b.xmax) || 154 !(ymin <= b.ymin) || 155 !(ymax >= b.ymax)) 156 return false; 157 return true; 158 } 159 160 /** 161 * Tests, weather the Point c lies within the bbox. 162 */ 163 public boolean bounds(LatLon c) { 164 if ((xmin <= c.lon()) && 165 (xmax >= c.lon()) && 166 (ymin <= c.lat()) && 167 (ymax >= c.lat())) 168 return true; 169 return false; 170 } 171 172 /** 173 * Tests, weather two BBoxes intersect as an area. 174 * I.e. whether there exists a point that lies in both of them. 175 */ 176 public boolean intersects(BBox b) { 177 if (xmin > b.xmax) 178 return false; 179 if (xmax < b.xmin) 180 return false; 181 if (ymin > b.ymax) 182 return false; 183 if (ymax < b.ymin) 184 return false; 185 return true; 186 } 187 188 /** 189 * Returns the top-left point. 190 * @return The top-left point 191 */ 192 public LatLon getTopLeft() { 193 return new LatLon(ymax, xmin); 194 } 195 196 /** 197 * Returns the latitude of top-left point. 198 * @return The latitude of top-left point 199 * @since 6203 200 */ 201 public double getTopLeftLat() { 202 return ymax; 203 } 204 205 /** 206 * Returns the longitude of top-left point. 207 * @return The longitude of top-left point 208 * @since 6203 209 */ 210 public double getTopLeftLon() { 211 return xmin; 212 } 213 214 /** 215 * Returns the bottom-right point. 216 * @return The bottom-right point 217 */ 218 public LatLon getBottomRight() { 219 return new LatLon(ymin, xmax); 220 } 221 222 /** 223 * Returns the latitude of bottom-right point. 224 * @return The latitude of bottom-right point 225 * @since 6203 226 */ 227 public double getBottomRightLat() { 228 return ymin; 229 } 230 231 /** 232 * Returns the longitude of bottom-right point. 233 * @return The longitude of bottom-right point 234 * @since 6203 235 */ 236 public double getBottomRightLon() { 237 return xmax; 238 } 239 240 public LatLon getCenter() { 241 return new LatLon(ymin + (ymax-ymin)/2.0, xmin + (xmax-xmin)/2.0); 242 } 243 244 int getIndex(final int level) { 245 246 int idx1 = QuadTiling.index(ymin, xmin, level); 247 248 final int idx2 = QuadTiling.index(ymin, xmax, level); 249 if (idx1 == -1) idx1 = idx2; 250 else if (idx1 != idx2) return -1; 251 252 final int idx3 = QuadTiling.index(ymax, xmin, level); 253 if (idx1 == -1) idx1 = idx3; 254 else if (idx1 != idx3) return -1; 255 256 final int idx4 = QuadTiling.index(ymax, xmax, level); 257 if (idx1 == -1) idx1 = idx4; 258 else if (idx1 != idx4) return -1; 259 260 return idx1; 261 } 262 263 public Rectangle2D toRectangle() { 264 return new Rectangle2D.Double(xmin, ymin, xmax - xmin, ymax - ymin); 265 } 266 267 @Override 268 public int hashCode() { 269 return (int) (ymin * xmin); 270 } 271 272 @Override 273 public boolean equals(Object o) { 274 if (o instanceof BBox) { 275 BBox b = (BBox) o; 276 return b.xmax == xmax && b.ymax == ymax 277 && b.xmin == xmin && b.ymin == ymin; 278 } else 279 return false; 280 } 281 282 @Override 283 public String toString() { 284 return "[ x: " + xmin + " -> " + xmax + ", y: " + ymin + " -> " + ymax + " ]"; 285 } 286 287 public String toStringCSV(String separator) { 288 return Utils.join(separator, Arrays.asList( 289 LatLon.cDdFormatter.format(xmin), 290 LatLon.cDdFormatter.format(ymin), 291 LatLon.cDdFormatter.format(xmax), 292 LatLon.cDdFormatter.format(ymax))); 293 } 294}