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