Index: src/org/openstreetmap/josm/data/osm/BBox.java
===================================================================
--- src/org/openstreetmap/josm/data/osm/BBox.java	(revision 11223)
+++ src/org/openstreetmap/josm/data/osm/BBox.java	(working copy)
@@ -11,12 +11,18 @@
 
 public class BBox {
 
-    private double xmin = Double.POSITIVE_INFINITY;
-    private double xmax = Double.NEGATIVE_INFINITY;
-    private double ymin = Double.POSITIVE_INFINITY;
-    private double ymax = Double.NEGATIVE_INFINITY;
+    protected double xmin = Double.POSITIVE_INFINITY;
+    protected double xmax = Double.NEGATIVE_INFINITY;
+    protected double ymin = Double.POSITIVE_INFINITY;
+    protected double ymax = Double.NEGATIVE_INFINITY;
 
     /**
+     * Constructs a new (invalid) BBox
+     */
+    protected BBox() { }
+
+
+    /**
      * Constructs a new {@code BBox} defined by a single point.
      *
      * @param x X coordinate
Index: src/org/openstreetmap/josm/data/osm/QuadBuckets.java
===================================================================
--- src/org/openstreetmap/josm/data/osm/QuadBuckets.java	(revision 11223)
+++ src/org/openstreetmap/josm/data/osm/QuadBuckets.java	(working copy)
@@ -14,7 +14,7 @@
 
 /**
  * Note: bbox of primitives added to QuadBuckets has to stay the same. In case of coordinate change, primitive must
- * be removed and readded.
+ * be removed and re-added.
  *
  * This class is (no longer) thread safe.
  * @param <T> type of primitives
@@ -31,12 +31,11 @@
         throw new AssertionError(s);
     }
 
-    public static final int MAX_OBJECTS_PER_LEVEL = 16;
+    private static final int MAX_OBJECTS_PER_NODE = 48;
 
-    static class QBLevel<T extends OsmPrimitive> {
-        private final int level;
-        private final int index;
-        private final BBox bbox;
+    static class QBLevel<T extends OsmPrimitive> extends BBox {
+        private final byte level;
+        private final byte index;
         private final long quad;
         private final QBLevel<T> parent;
         private boolean isLeaf = true;
@@ -45,28 +44,26 @@
         // child order by index is sw, nw, se, ne
         private QBLevel<T> nw, ne, sw, se;
 
-        private final QuadBuckets<T> buckets;
-
         private QBLevel<T> getChild(int index) {
             switch (index) {
             case NE_INDEX:
                 if (ne == null) {
-                    ne = new QBLevel<>(this, index, buckets);
+                    ne = new QBLevel<>(this, index);
                 }
                 return ne;
             case NW_INDEX:
                 if (nw == null) {
-                    nw = new QBLevel<>(this, index, buckets);
+                    nw = new QBLevel<>(this, index);
                 }
                 return nw;
             case SE_INDEX:
                 if (se == null) {
-                    se = new QBLevel<>(this, index, buckets);
+                    se = new QBLevel<>(this, index);
                 }
                 return se;
             case SW_INDEX:
                 if (sw == null) {
-                    sw = new QBLevel<>(this, index, buckets);
+                    sw = new QBLevel<>(this, index);
                 }
                 return sw;
             default:
@@ -81,47 +78,35 @@
 
         @Override
         public String toString() {
-            return super.toString() + '[' + level + "]: " + bbox();
+            return super.toString() + '[' + level + "]: ";
         }
 
         /**
          * Constructor for root node
-         * @param buckets quadbuckets
          */
-        QBLevel(final QuadBuckets<T> buckets) {
+        QBLevel() {
+            super(-180, 90, 180, -90);
             level = 0;
             index = 0;
             quad = 0;
             parent = null;
-            bbox = new BBox(-180, 90, 180, -90);
-            this.buckets = buckets;
         }
 
-        QBLevel(QBLevel<T> parent, int parentIndex, final QuadBuckets<T> buckets) {
+        QBLevel(QBLevel<T> parent, int parentIndex) {
             this.parent = parent;
-            this.level = parent.level + 1;
-            this.index = parentIndex;
-            this.buckets = buckets;
+            this.level = (byte) (parent.level + 1);
+            this.index = (byte) parentIndex;
 
             int shift = (QuadTiling.NR_LEVELS - level) * 2;
-            long mult = 1;
-            // Java blows the big one. It seems to wrap when you shift by > 31
-            if (shift >= 30) {
-                shift -= 30;
-                mult = 1 << 30;
-            }
-            long quadpart = mult * (parentIndex << shift);
+            long quadpart = (long) parentIndex << shift;
             this.quad = parent.quad | quadpart;
-            this.bbox = calculateBBox(); // calculateBBox reference quad
+            LatLon bottomLeft = QuadTiling.tile2LatLon(this.quad);
+            xmin = bottomLeft.lon();
+            ymin = bottomLeft.lat();
+            xmax = xmin + parent.width() / 2;
+            ymax = ymin + parent.height() / 2;
         }
 
-        private BBox calculateBBox() {
-            LatLon bottomLeft = this.coor();
-            double lat = bottomLeft.lat() + parent.height() / 2;
-            double lon = bottomLeft.lon() + parent.width() / 2;
-            return new BBox(bottomLeft.lon(), bottomLeft.lat(), lon, lat);
-        }
-
         QBLevel<T> findBucket(BBox bbox) {
             if (!hasChildren())
                 return this;
@@ -282,14 +267,14 @@
 
         void doAdd(T o) {
             if (consistency_testing) {
-                if (!matches(o, this.bbox())) {
+                if (o instanceof Node && !matches(o, this)) {
                     o.getBBox().getIndex(level);
                     o.getBBox().getIndex(level - 1);
-                    abort("\nobject " + o + " does not belong in node at level: " + level + " bbox: " + this.bbox());
+                    abort("\nobject " + o + " does not belong in node at level: " + level + " bbox: " + super.toString());
                 }
             }
             doAddContent(o);
-            if (isLeaf() && content.size() > MAX_OBJECTS_PER_LEVEL && level < QuadTiling.NR_LEVELS) {
+            if (isLeaf() && content.size() > MAX_OBJECTS_PER_NODE && level < QuadTiling.NR_LEVELS) {
                 doSplit();
             }
         }
@@ -298,10 +283,10 @@
             findBucket(o.getBBox()).doAdd(o);
         }
 
-        private void search(BBox searchBbox, List<T> result) {
-            if (!this.bbox().intersects(searchBbox))
+        private void search(QuadBuckets<T> buckets, BBox searchBbox, List<T> result) {
+            if (!this.intersects(searchBbox))
                 return;
-            else if (bbox().bounds(searchBbox)) {
+            else if (this.bounds(searchBbox)) {
                 buckets.searchCache = this;
             }
 
@@ -312,16 +297,16 @@
             //TODO Coincidence vector should be calculated here and only buckets that match search_bbox should be checked
 
             if (nw != null) {
-                nw.search(searchBbox, result);
+                nw.search(buckets, searchBbox, result);
             }
             if (ne != null) {
-                ne.search(searchBbox, result);
+                ne.search(buckets, searchBbox, result);
             }
             if (se != null) {
-                se.search(searchBbox, result);
+                se.search(buckets, searchBbox, result);
             }
             if (sw != null) {
-                sw.search(searchBbox, result);
+                sw.search(buckets, searchBbox, result);
             }
         }
 
@@ -338,26 +323,6 @@
             return -1;
         }
 
-        double width() {
-            return bbox.width();
-        }
-
-        double height() {
-            return bbox.height();
-        }
-
-        public BBox bbox() {
-            return bbox;
-        }
-
-        /*
-         * This gives the coordinate of the bottom-left
-         * corner of the box
-         */
-        final LatLon coor() {
-            return QuadTiling.tile2LatLon(this.quad);
-        }
-
         void removeFromParent() {
             if (parent == null)
                 return;
@@ -403,7 +368,7 @@
 
     @Override
     public final void clear() {
-        root = new QBLevel<>(this);
+        root = new QBLevel<>();
         searchCache = null;
         size = 0;
     }
@@ -582,6 +547,11 @@
         return size == 0;
     }
 
+    /**
+     * Search the tree for objects in the bbox (or crossing the bbox if they are ways)
+     * @param searchBbox the bbox
+     * @return List of primitives within the bbox (or crossing the bbox if they are ways). Can be empty, but not null.
+     */
     public List<T> search(BBox searchBbox) {
         List<T> ret = new ArrayList<>();
         // Doing this cuts down search cost on a real-life data set by about 25%
@@ -589,7 +559,7 @@
             searchCache = root;
         }
         // Walk back up the tree when the last search spot can not cover the current search
-        while (searchCache != null && !searchCache.bbox().bounds(searchBbox)) {
+        while (searchCache != null && !searchCache.bounds(searchBbox)) {
             searchCache = searchCache.parent;
         }
 
@@ -601,7 +571,7 @@
         // Save parent because searchCache might change during search call
         QBLevel<T> tmp = searchCache.parent;
 
-        searchCache.search(searchBbox, ret);
+        searchCache.search(this, searchBbox, ret);
 
         // A way that spans this bucket may be stored in one
         // of the nodes which is a parent of the search cache
