Ticket #3671: store-ways-in-quadbuckets.patch

File store-ways-in-quadbuckets.patch, 37.2 KB (added by hansendc, 15 years ago)
  • (a) core/src/org/openstreetmap/josm/data/osm/DataSet.java~store-ways-in-quadbuckets vs. (b) core-dave/src/org/openstreetmap/josm/data/osm/DataSet.java

    
    ---
    
     core-dave/src/org/openstreetmap/josm/data/osm/DataSet.java     |    6 
     core-dave/src/org/openstreetmap/josm/data/osm/QuadBuckets.java |  573 ++++++----
     2 files changed, 391 insertions(+), 188 deletions(-)
    
    diff -puN src/org/openstreetmap/josm/data/osm/DataSet.java~store-ways-in-quadbuckets src/org/openstreetmap/josm/data/osm/DataSet.java
    a b  
    11// License: GPL. Copyright 2007 by Immanuel Scholz and others
    22package org.openstreetmap.josm.data.osm;
    3 
     3import org.openstreetmap.josm.tools.ReverseLookup;
    44import static org.openstreetmap.josm.tools.I18n.tr;
    55
    66import java.awt.geom.Area;
    public class DataSet implements Cloneabl  
    3838     * All nodes goes here, even when included in other data (ways etc). This enables the instant
    3939     * conversion of the whole DataSet by iterating over this data structure.
    4040     */
    41     public Collection<Node> nodes = new QuadBuckets();
     41    public QuadBuckets<Node> nodes = new QuadBuckets<Node>();
    4242
    4343    /**
    4444     * All ways (Streets etc.) in the DataSet.
    4545     *
    4646     * The way nodes are stored only in the way list.
    4747     */
    48     public Collection<Way> ways = new LinkedList<Way>();
     48    public QuadBuckets<Way> ways = new QuadBuckets<Way>();
    4949
    5050    /**
    5151     * All relations/relationships
  • (a) core/src/org/openstreetmap/josm/data/osm/QuadBuckets.java~store-ways-in-quadbuckets vs. (b) core-dave/src/org/openstreetmap/josm/data/osm/QuadBuckets.java

    diff -puN src/org/openstreetmap/josm/data/osm/QuadBuckets.java~store-ways-in-quadbuckets src/org/openstreetmap/josm/data/osm/QuadBuckets.java
    a b  
    11package org.openstreetmap.josm.data.osm;
     2import java.lang.reflect.Array;
    23import java.util.ArrayList;
    34import java.util.Collection;
    45import java.util.Collections;
    import java.util.TreeMap;  
    89
    910import org.openstreetmap.josm.data.coor.EastNorth;
    1011import org.openstreetmap.josm.data.osm.Node;
     12import org.openstreetmap.josm.data.osm.Way;
     13import org.openstreetmap.josm.data.osm.OsmPrimitive;
    1114import org.openstreetmap.josm.tools.Pair;
    1215
    1316import java.nio.MappedByteBuffer;
    import org.openstreetmap.josm.data.coor.  
    3437import org.openstreetmap.josm.data.osm.visitor.Visitor;
    3538
    3639
    37 public class QuadBuckets implements Collection<Node>
     40public class QuadBuckets<T extends OsmPrimitive> implements Collection<T>
    3841{
    3942    public static boolean debug = false;
    4043    static boolean consistency_testing = false;
    public class QuadBuckets implements Coll  
    7982    // has to be a power of 2
    8083    public static int TILES_PER_LEVEL_SHIFT = 2;
    8184    public static int TILES_PER_LEVEL = 1<<TILES_PER_LEVEL_SHIFT;
     85    // Maybe this should just be a Rectangle??
    8286    class BBox
    8387    {
    84         private double xmin;
    85         private double xmax;
    86         private double ymin;
    87         private double ymax;
     88        private double xmin = Double.POSITIVE_INFINITY;
     89        private double xmax = Double.NEGATIVE_INFINITY;
     90        private double ymin = Double.POSITIVE_INFINITY;
     91        private double ymax = Double.NEGATIVE_INFINITY;
    8892        void sanity()
    8993        {
    9094            if (xmin < -180.0)
    public class QuadBuckets implements Coll  
    106110        }
    107111        public String toString()
    108112        {
    109             return "[ " + xmin + " -> " + xmax + ", " +
    110                          ymin + " -> " + ymax + " ]";
     113            return "[ x: " + xmin + " -> " + xmax +
     114                   ", y: " + ymin + " -> " + ymax + " ]";
    111115        }
    112116        double min(double a, double b)
    113117        {
    public class QuadBuckets implements Coll  
    121125                return a;
    122126            return b;
    123127        }
     128        private void add(LatLon c)
     129        {
     130            xmin = min(xmin, c.lon());
     131            xmax = max(xmax, c.lon());
     132            ymin = min(ymin, c.lat());
     133            ymax = max(ymax, c.lat());
     134        }
    124135        public BBox(LatLon a, LatLon b)
    125136        {
    126             xmin = min(a.lon(), b.lon());
    127             xmax = max(a.lon(), b.lon());
    128             ymin = min(a.lat(), b.lat());
    129             ymax = max(a.lat(), b.lat());
     137            add(a);
     138            add(b);
    130139            sanity();
    131140        }
    132141        public BBox(double a_x, double a_y, double b_x, double b_y)
    public class QuadBuckets implements Coll  
    137146            ymax = max(a_y, b_y);
    138147            sanity();
    139148        }
     149        public BBox(Way w)
     150        {
     151            for (Node n : w.getNodes()) {
     152                LatLon coor = n.getCoor();
     153                if (coor == null)
     154                    continue;
     155                add(coor);
     156            }
     157            this.sanity();
     158        }
    140159        public double height()
    141160        {
    142161            return ymax-ymin;
    public class QuadBuckets implements Coll  
    179198        {
    180199            return this.inside(b) || b.inside(this);
    181200        }
     201        List<LatLon> points()
     202        {
     203            LatLon p1 = new LatLon(ymin, xmin);
     204            LatLon p2 = new LatLon(ymin, xmax);
     205            LatLon p3 = new LatLon(ymax, xmin);
     206            LatLon p4 = new LatLon(ymax, xmax);
     207            List<LatLon> ret = new ArrayList<LatLon>();
     208            ret.add(p1);
     209            ret.add(p2);
     210            ret.add(p3);
     211            ret.add(p4);
     212            return ret;
     213        }
    182214    }
    183215    class QBLevel
    184216    {
    public class QuadBuckets implements Coll  
    186218        long quad;
    187219        QBLevel parent;
    188220
    189         public List<Node> content;
     221        public List<T> content;
    190222        public QBLevel children[];
     223        public String toString()
     224        {
     225            return super.toString()+ "["+level+"]: " + bbox;
     226        }
    191227        public QBLevel(QBLevel parent)
    192228        {
    193229            init(parent);
    194230        }
    195         String quads(Node n)
     231        String quads(T o)
    196232        {
    197             return Long.toHexString(QuadTiling.quadTile(n.getCoor()));
     233            if (o instanceof Node) {
     234                LatLon coor = ((Node)o).getCoor();
     235                if (coor == null)
     236                    return "null node coordinates";
     237                return Long.toHexString(QuadTiling.quadTile(coor));
     238            }
     239            return "Way??";
     240        }
     241        boolean remove_content(T o)
     242        {
     243            boolean ret = this.content.remove(o);
     244            if (this.content.size() == 0)
     245                this.content = null;
     246            return ret;
     247        }
     248        // Get the correct index for the given primitive
     249        // at the given level.  If the primitive can not
     250        // fit into a single quad at this level, return -1
     251        int get_index(T o, int level)
     252        {
     253            if (debug)
     254                out("getting index for " + o + " at level: " + level);
     255            if (o instanceof Node) {
     256                Node n = (Node)o;
     257                LatLon coor = ((Node)o).getCoor();
     258                if (coor == null)
     259                    return -1;
     260                return QuadTiling.index(coor, level);
     261            }
     262            if (o instanceof Way) {
     263                Way w = (Way)o;
     264                int index = -1;
     265                //for (Node n : w.getNodes()) {
     266                for (LatLon c : way_bbox(w).points()) {
     267                //    LatLon c = n.getCoor();
     268                    if (debug)
     269                        out("getting index for point: " + c);
     270                    if (index == -1) {
     271                        index = QuadTiling.index(c, level);
     272                        if (debug)
     273                            out("set initial way index to: " + index);
     274                        continue;
     275                    }
     276                    int node_index = QuadTiling.index(c, level);
     277                    if (debug)
     278                        out("other node way index: " + node_index);
     279                    if (node_index != index) {
     280                        // This happens at level 0 sometimes when a way has no
     281                        // nodes present.
     282                        if (debug) {
     283                            out("way ("+w.getId()+") would have gone across two quads "
     284                                    + node_index + "/" + index + " at level: " + level + "    ");
     285                            out("node count: " + w.getNodes().size());
     286                            for (LatLon c2 : way_bbox(w).points())
     287                                out("points: " + c2);
     288                        }
     289                        return -1;
     290                    }
     291                }
     292                return index;
     293            }
     294            abort("bad primitive: " + o);
     295            return -1;
    198296        }
    199297        void split()
    200298        {
    public class QuadBuckets implements Coll  
    205303            if (children != null) {
    206304                abort("overwrote children");
    207305            }
    208             children = new QBLevel[QuadTiling.TILES_PER_LEVEL];
     306            // This is ugly and hackish.  But, it seems to work,
     307            // and using an ArrayList here seems to cost us
     308            // a significant performance penalty -- 50% in my
     309            // testing.  Child access is one of the single
     310            // hottest code paths in this entire class.
     311            children = (QBLevel[])Array.newInstance(this.getClass(), QuadTiling.TILES_PER_LEVEL);
    209312            // deferring allocation of children until use
    210313            // seems a bit faster
    211314            //for (int i = 0; i < TILES_PER_LEVEL; i++)
    212315            //    children[i] = new QBLevel(this, i);
    213             List<Node> tmpcontent = content;
     316            List<T> tmpcontent = content;
    214317            content = null;
    215             for (Node n : tmpcontent) {
    216                 int new_index = QuadTiling.index(n, level);
     318            for (T o : tmpcontent) {
     319                int new_index = get_index(o, level);
     320                if (new_index == -1) {
     321                    this.add_content(o);
     322                    //out("adding content to branch: " + this);
     323                    continue;
     324                }
    217325                if (children[new_index] == null)
    218326                    children[new_index] = new QBLevel(this, new_index);
    219327                QBLevel child = children[new_index];
    220328                if (debug)
    221                     out("putting "+n+"(q:"+quads(n)+") into ["+new_index+"] " + child.bbox());
    222                 child.add(n);
     329                    out("putting "+o+"(q:"+quads(o)+") into ["+new_index+"] " + child.bbox());
     330                child.add(o);
    223331            }
    224332        }
    225         void add_leaf(Node n)
     333        boolean add_content(T o)
     334        {
     335            boolean ret = false;
     336            if (content == null)
     337                content = new LinkedList<T>();
     338            ret = content.add(o);
     339            if (debug && !this.isLeaf())
     340                pout("added "+o.getClass().getName()+" to non-leaf with size: " + content.size());
     341            return ret;
     342        }
     343        void add_to_leaf(T o)
    226344        {
    227             LatLon c = n.getCoor();
    228345            QBLevel ret = this;
    229             if (content == null) {
    230                 if (debug)
    231                     out("   new content array");
    232                 // I chose a LinkedList because we do not do
    233                 // any real random access to this.  We generally
    234                 // just run through the whole thing all at once
    235                 content = new LinkedList<Node>();
    236             }
    237             content.add(n);
     346            add_content(o);
    238347            if (content.size() > MAX_OBJECTS_PER_LEVEL) {
    239348                if (debug)
    240349                    out("["+level+"] deciding to split");
    public class QuadBuckets implements Coll  
    254363                }
    255364                return;
    256365            }
    257             if (debug) {
    258                 out("   plain content put now have " + content.size());
    259                 if (content.contains(c))
    260                     out("   and I already have that one");
     366        }
     367        /*
     368         * This is a quick hack.  The problem is that we need the
     369         * way's bounding box a *bunch* of times when it gets
     370         * inserted.  It gets expensive if we have to recreate
     371         * them each time.
     372         *
     373         * An alternative would be to calculate it at .add() time
     374         * and passing it down the call chain.
     375         */
     376        HashMap<Way,BBox> way_bbox_cache = new HashMap<Way, BBox>();
     377        BBox way_bbox(Way w)
     378        {
     379            if (way_bbox_cache.size() > 100)
     380                way_bbox_cache.clear();
     381            BBox b = way_bbox_cache.get(w);
     382            if (b == null) {
     383                b = new BBox(w);
     384                way_bbox_cache.put(w, b);
    261385            }
     386            return b;
     387            //return new BBox(w);
    262388        }
    263389
    264         List<Node> search(BBox bbox, LatLon point, double radius)
     390        boolean matches(T o, BBox search_bbox)
    265391        {
    266             if (isLeaf())
    267                 return search_leaf(bbox, point, radius);
    268             return search_branch(bbox, point, radius);
     392            if (o instanceof Node) {
     393                LatLon coor = ((Node)o).getCoor();
     394                if (coor == null)
     395                    return false;
     396                return search_bbox.bounds(coor);
     397            }
     398            if (o instanceof Way) {
     399                BBox bbox = way_bbox((Way)o);
     400                return bbox.intersects(search_bbox);
     401            }
     402            abort("matches() bad primitive: " + o);
     403            return false;
    269404        }
    270         private List<Node> search_leaf(BBox bbox, LatLon point, double radius)
     405        private List<T> search_contents(BBox search_bbox)
    271406        {
     407            String size = "null";
     408            if (content != null)
     409                size = ""+content.size();
    272410            if (debug)
    273                 out("searching contents in tile " + this.bbox() + " for " + bbox);
     411                out("searching contents (size: "+size+") for " + search_bbox);
    274412            /*
    275413             * It is possible that this was created in a split
    276414             * but never got any content populated.
    public class QuadBuckets implements Coll  
    283421            //
    284422            // the iterator's calls to ArrayList.size() were showing up in
    285423            // some profiles, so I'm trying a LinkedList instead
    286             List<Node> ret = new LinkedList<Node>();
    287             for (Node n : content) {
    288                 // This supports two modes: one where the bbox is just
    289                 // an outline of the point/radius combo, and one where
    290                 // it is *just* the bbox.  If it is *just* the bbox,
    291                 // don't consider the points below
    292                 if (point == null) {
    293                     ret.add(n);
    294                     continue;
    295                 }
    296                 LatLon c = n.getCoor();
    297                 // is greatCircleDistance really necessary?
    298                 double d = c.greatCircleDistance(point);
    299                 if (debug)
    300                     out("[" + level + "] Checking coor: " + c + " dist: " + d + " vs. " + radius + " " + quads(n));
    301                 if (d > radius)
    302                     continue;
    303                 if (debug)
    304                     out("hit in quad: "+Long.toHexString(this.quad)+"\n");
    305                 //if (ret == null)
    306                 //    ret = new LinkedList<Node>();
    307                 ret.add(n);
     424            List<T> ret = new LinkedList<T>();
     425            for (T o : content) {
     426                if (matches(o, search_bbox))
     427                    ret.add(o);
    308428            }
    309429            if (debug)
    310430                out("done searching quad " + Long.toHexString(this.quad));
    public class QuadBuckets implements Coll  
    354474            }
    355475            return null;
    356476        }
    357         QBLevel nextLeaf()
     477        boolean hasContent()
     478        {
     479            if (content == null)
     480                return false;
     481            return true;
     482        }
     483        QBLevel nextContentNode()
    358484        {
    359485            QBLevel next = this;
    360486            if (this.isLeaf()) {
    public class QuadBuckets implements Coll  
    380506            // leaf.  So, walk back down the tree
    381507            // and find the first leaf
    382508            while (!next.isLeaf()) {
     509                if (next.hasContent() && next != this)
     510                    break;
    383511                if (debug)
    384                     out("["+next.level+"] next node is a branch, moving down...");
     512                    out("["+next.level+"] next node ("+next+") is a branch (content: "+next.hasContent()+"), moving down...");
    385513                for (QBLevel child : next.children) {
    386514                    if (child == null)
    387515                        continue;
    public class QuadBuckets implements Coll  
    416544                    continue;
    417545                ret += l.size();
    418546            }
     547            if (content != null)
     548                ret += content.size();
    419549            if (debug)
    420550                out("["+level+"] branch size: " + ret);
    421551            return ret;
    422552        }
    423         boolean contains(Node n)
     553        boolean contains(T n)
    424554        {
    425555            QBLevel res = find_exact(n);
    426556            if (res == null)
    427557                return false;
    428558            return true;
    429559        }
    430         QBLevel find_exact(Node n)
     560        QBLevel find_exact(T n)
    431561        {
    432562            if (isLeaf())
    433563                return find_exact_leaf(n);
    434564            return find_exact_branch(n);
    435565        }
    436         QBLevel find_exact_leaf(Node n)
     566        QBLevel find_exact_leaf(T n)
    437567        {
    438568            QBLevel ret = null;
    439569            if (content != null && content.contains(n))
    440570                ret = this;
    441571            return ret;
    442572        }
    443         QBLevel find_exact_branch(Node n)
     573        QBLevel find_exact_branch(T n)
    444574        {
    445575            QBLevel ret = null;
    446576            for (QBLevel l : children) {
    public class QuadBuckets implements Coll  
    452582            }
    453583            return ret;
    454584        }
    455         boolean add(Node n)
     585        boolean add(T n)
    456586        {
     587            if (consistency_testing) {
     588                if (!matches(n, this.bbox())) {
     589                    out("-----------------------------");
     590                    debug = true;
     591                    get_index(n, level);
     592                    abort("object " + n + " does not belong in node at level: " + level + " bbox: " + this.bbox());
     593                }
     594            }
    457595            if (isLeaf())
    458                 add_leaf(n);
     596                add_to_leaf(n);
    459597            else
    460                 add_branch(n);
     598                add_to_branch(n);
    461599            return true;
    462600        }
    463         QBLevel add_branch(Node n)
     601        QBLevel add_to_branch(T n)
    464602        {
    465             LatLon c = n.getCoor();
    466             int index = QuadTiling.index(n, level);
    467             if (debug)
     603            int index = get_index(n, level);
     604            if (index == -1) {
     605                if (debug)
     606                    out("unable to get index for " + n + "at level: " + level + ", adding content to branch: " + this);
     607                this.add_content(n);
     608                return this;
     609            }
     610            if (debug) {
    468611                out("[" + level + "]: " + n +
    469612                    " index " + index + " levelquad:" + this.quads() + " level bbox:" + this.bbox());
    470             if (debug)
    471613                out("   put in child["+index+"]");
     614            }
    472615            if (children[index] == null)
    473616                children[index] = new QBLevel(this, index);
    474617            children[index].add(n);
    475             /* this is broken at the moment because we need to handle the null n.getCoor()
    476             if (consistency_testing && !children[index].bbox().bounds(n.getCoor())) {
    477                 out("target child["+index+"] does not bound " + children[index].bbox() + " " + c);
    478                 for (int i = 0; i < children.length; i++) {
    479                     QBLevel ch = children[i];
    480                     if (ch == null)
    481                         continue;
    482                     out("child["+i+"] quad: " + ch.quads() + " bounds: " + ch.bbox.bounds(n.getCoor()));
    483                 }
    484                 out(" coor quad: " + quads(n));
    485                 abort("");
    486             }*/
    487 
    488             if (consistency_testing) {
    489                 for (int i = 0; i < children.length; i++) {
    490                     QBLevel ch = children[i];
    491                     if (ch == null)
    492                         continue;
    493                     if (ch.bbox().bounds(c) && i != index) {
    494                         out("multible bounding?? expected: " + index + " but saw at " + i + " " + ch.quads());
    495                         out("child["+i+"] bbox: " + ch.bbox());
    496                     }
    497                 }
    498             }
    499618            return this;
    500619        }
    501         private List<Node> search_branch(BBox bbox, LatLon point, double radius)
     620        private List<T> search(BBox search_bbox)
    502621        {
    503             List<Node> ret = null;
     622            List<T> ret = null;
    504623            if (debug)
    505624                System.out.print("[" + level + "] qb bbox: " + this.bbox() + " ");
    506             if (!this.bbox().intersects(bbox)) {
     625            if (!this.bbox().intersects(search_bbox)) {
    507626                if (debug) {
    508627                    out("miss " + Long.toHexString(this.quad));
    509628                    //QuadTiling.tile2xy(this.quad);
    510629                }
    511630                return ret;
    512631            }
     632            if (this.hasContent())
     633                ret = this.search_contents(search_bbox);
     634            if (this.isLeaf()) {
     635                return ret;
     636            }
     637            //if (this.hasContent())
     638            //    abort("branch had stuff");
    513639            if (debug)
    514640                out("hit " + this.quads());
    515641
    516642            if (debug)
    517643                out("[" + level + "] not leaf, moving down");
    518             int child_nr = 0;
    519             for (QBLevel q : children) {
     644            for (int i = 0; i < children.length; i++) {
     645                QBLevel q = children[i];
     646                if (debug)
     647                    out("child["+i+"]: " + q);
    520648                if (q == null)
    521649                    continue;
    522                 child_nr++;
    523650                if (debug)
    524                     System.out.print(child_nr+": ");
    525                 List<Node> coors = q.search(bbox, point, radius);
     651                    System.out.print(i+": ");
     652                List<T> coors = q.search(search_bbox);
    526653                if (coors == null)
    527654                    continue;
    528655                if (ret == null)
    529656                    ret = coors;
    530657                else
    531658                    ret.addAll(coors);
    532                 if (q.bbox().bounds(bbox)) {
     659                if (q.bbox().bounds(search_bbox)) {
    533660                    search_cache = q;
    534661                    // optimization: do not continue searching
    535662                    // other tiles if this one wholly contained
    public class QuadBuckets implements Coll  
    550677        public void init(QBLevel parent)
    551678        {
    552679                this.parent = parent;
    553                 if (parent != null)
     680                if (parent == null)
     681                    this.level = 0;
     682                else
    554683                    this.level = parent.level + 1;
    555684                this.quad = 0;
    556685        }
    public class QuadBuckets implements Coll  
    568697        public QBLevel(QBLevel parent, int parent_index)
    569698        {
    570699            this.init(parent);
    571             this.level = parent.level+1;
    572             this.parent = parent;
    573700            int shift = (QuadBuckets.NR_LEVELS - level) * 2;
    574701            long mult = 1;
    575702            // Java blows the big one.  It seems to wrap when
    public class QuadBuckets implements Coll  
    644771    {
    645772        root = new QBLevel(null);
    646773        search_cache = null;
    647         if (debug)
     774        if (debug) {
    648775            out("QuadBuckets() cleared: " + this);
     776            out("root: " + root + " level: " + root.level + " bbox: " + root.bbox());
     777        }
    649778    }
    650     public boolean add(Node n)
     779    public boolean add(T n)
    651780    {
    652781        if (debug)
    653             out(this + " QuadBuckets() add: " + n + " size now: " + this.size());
     782            out("QuadBuckets() add: " + n + " size now: " + this.size());
    654783        int before_size = -1;
    655784        if (consistency_testing)
    656785            before_size = root.size();
    657         boolean ret = root.add(n);
     786        boolean ret;
     787        // A way with no nodes will have an infinitely large
     788        // bounding box.  Just stash it in the root node.
     789        if (!n.isUsable())
     790            ret = root.add_content(n);
     791        else
     792            ret = root.add(n);
    658793        if (consistency_testing) {
    659794            int end_size = root.size();
    660795            if (ret)
    public class QuadBuckets implements Coll  
    662797            if (before_size != end_size)
    663798                abort("size inconsistency before add: " + before_size + " after: " + end_size);
    664799        }
     800        if (debug)
     801            out("QuadBuckets() add: " + n + " size after: " + this.size());
    665802        return ret;
    666803    }
     804    public void reindex()
     805    {
     806        QBLevel newroot = new QBLevel(null);
     807        Iterator<T> i = this.iterator();
     808        while (i.hasNext()) {
     809            T o = i.next();
     810            newroot.add(o);
     811        }
     812        root = newroot;
     813    }
    667814    public void unsupported()
    668815    {
    669816        out("unsupported operation");
    670817        throw new UnsupportedOperationException();
    671818    }
    672     public boolean retainAll(Collection nodes)
     819    public boolean retainAll(Collection objects)
    673820    {
    674         for (Node n : this) {
    675             if (nodes.contains(n))
     821        for (T o : this) {
     822            if (objects.contains(o))
    676823                continue;
    677             if (!this.remove(n))
     824            if (!this.remove(o))
    678825                return false;
    679826        }
    680827        return true;
    681828    }
    682     public boolean removeAll(Collection nodes)
     829    boolean canStore(Object o)
     830    {
     831        if (o instanceof Way)
     832            return true;
     833        if (o instanceof Node)
     834            return true;
     835        return false;
     836    }
     837    public boolean removeAll(Collection objects)
    683838    {
    684         for (Object o : nodes) {
    685             if (!(o instanceof Node))
     839        for (Object o : objects) {
     840            if (!canStore(o))
    686841                return false;
    687             Node n = (Node)o;
    688             if (!this.remove(n))
     842            if (!this.remove(o))
    689843                return false;
    690844        }
    691845        return true;
    692846    }
    693     public boolean addAll(Collection nodes)
     847    public boolean addAll(Collection objects)
    694848    {
    695         for (Object o : nodes) {
    696             if (!(o instanceof Node))
     849        for (Object o : objects) {
     850            if (!canStore(o))
    697851                return false;
    698             Node n = (Node)o;
    699             if (!this.add(n))
     852            if (!this.add(convert(o)))
    700853                return false;
    701854        }
    702855        return true;
    703856    }
    704     public boolean containsAll(Collection nodes)
     857    public boolean containsAll(Collection objects)
    705858    {
    706859        boolean ret = true;
    707         for (Object o : nodes) {
    708             if (!(o instanceof Node))
     860        for (Object o : objects) {
     861            if (!canStore(o))
    709862                return false;
    710             Node n = (Node)o;
    711             if (!this.contains(n))
     863            if (!this.contains(o))
    712864                return false;
    713865        }
    714866        return true;
    715867    }
    716868    private void check_type(Object o)
    717869    {
    718         if (o instanceof Node)
     870        if (canStore(o))
    719871            return;
    720872        unsupported();
    721873    }
     874    // If anyone has suggestions for how to fix
     875    // this properly, I'm listening :)
     876    private T convert(Object raw)
     877    {
     878        //@SuppressWarnings("unchecked")
     879        return (T)raw;
     880    }
    722881    public boolean remove(Object o)
    723882    {
    724883        check_type(o);
    725         return this.remove((Node)o);
     884        return this.remove(convert(o));
    726885    }
    727     public boolean remove(Node n)
     886    public boolean remove(T n)
    728887    {
    729888        QBLevel bucket = root.find_exact(n);
    730889        if (!bucket.isLeaf())
    731890            abort("found branch where leaf expected");
    732         return bucket.content.remove(n);
     891        boolean ret = bucket.remove_content(n);
     892        return ret;
    733893    }
    734894    public boolean contains(Object o)
    735895    {
    736         if (!(o instanceof Node))
     896        if (!canStore(o))
    737897            return false;
    738         QBLevel bucket = root.find_exact((Node)o);
     898        QBLevel bucket = root.find_exact(convert(o));
    739899        if (bucket == null)
    740900            return false;
    741901        return true;
    742902    }
    743     public ArrayList<Node> toArrayList()
     903    public ArrayList<T> toArrayList()
    744904    {
    745         ArrayList<Node> a = new ArrayList<Node>();
    746         for (Node n : this)
     905        ArrayList<T> a = new ArrayList<T>();
     906        for (T n : this)
    747907            a.add(n);
    748         out("returning array list with size: " + a.size());
     908        if (debug)
     909           out("returning array list with size: " + a.size());
    749910        return a;
    750911    }
    751912    public Object[] toArray()
    public class QuadBuckets implements Coll  
    756917    {
    757918        return this.toArrayList().toArray(template);
    758919    }
    759     class QuadBucketIterator implements Iterator<Node>
     920    class QuadBucketIterator implements Iterator<T>
    760921    {
    761         QBLevel current_leaf;
    762         int index_in_leaf;
     922        QBLevel current_node;
     923        int content_index;
    763924        int iterated_over;
    764         QBLevel next_leaf(QBLevel q)
     925        QBLevel next_content_node(QBLevel q)
    765926        {
    766927            if (q == null)
    767928                return null;
    768929            QBLevel orig = q;
    769             QBLevel next = q.nextLeaf();
    770             if (consistency_testing && (orig == next))
     930            QBLevel next = q.nextContentNode();
     931            //if (consistency_testing && (orig == next))
     932            if (orig == next)
    771933                abort("got same leaf back leaf: " + q.isLeaf());
    772934            return next;
    773935        }
    774         public QuadBucketIterator(QuadBuckets qb)
     936        public QuadBucketIterator(QuadBuckets<T> qb)
    775937        {
    776938            if (debug)
    777                 out(this + " is a new iterator qb.root: " + qb.root + " size: " + qb.size());
    778             if (qb.root.isLeaf())
    779                 current_leaf = qb.root;
     939                out(this + " is a new iterator qb: " + qb + " size: " + qb.size());
     940            if (qb.root.isLeaf() || qb.root.hasContent())
     941                current_node = qb.root;
    780942            else
    781                 current_leaf = next_leaf(qb.root);
     943                current_node = next_content_node(qb.root);
    782944            if (debug)
    783                 out("\titerator first leaf: " + current_leaf);
     945                out("\titerator first leaf: " + current_node);
    784946            iterated_over = 0;
    785947        }
    786948        public boolean hasNext()
    public class QuadBuckets implements Coll  
    792954            }
    793955            return true;
    794956        }
    795         Node peek()
     957        T peek()
    796958        {
    797             if (current_leaf == null) {
     959            if (current_node == null) {
    798960                if (debug)
    799961                    out("null current leaf, nowhere to go");
    800962                return null;
    801963            }
    802             while((current_leaf.content == null) ||
    803                   (index_in_leaf >= current_leaf.content.size())) {
     964            while((current_node.content == null) ||
     965                  (content_index >= current_node.content.size())) {
    804966                if (debug)
    805967                    out("moving to next leaf");
    806                 index_in_leaf = 0;
    807                 current_leaf = next_leaf(current_leaf);
    808                 if (current_leaf == null)
     968                content_index = 0;
     969                current_node = next_content_node(current_node);
     970                if (current_node == null)
    809971                    break;
    810972            }
    811             if (current_leaf == null || current_leaf.content == null) {
     973            if (current_node == null || current_node.content == null) {
    812974                if (debug)
    813                     out("late nowhere to go " + current_leaf);
     975                    out("late nowhere to go " + current_node);
    814976                return null;
    815977            }
    816             return current_leaf.content.get(index_in_leaf);
     978            return current_node.content.get(content_index);
    817979        }
    818         public Node next()
     980        public T next()
    819981        {
    820             Node ret = peek();
    821             index_in_leaf++;
     982            T ret = peek();
     983            content_index++;
    822984            if (debug)
    823                 out("iteration["+iterated_over+"] " + index_in_leaf + " leaf: " + current_leaf);
     985                out("iteration["+iterated_over+"] " + content_index + " leaf: " + current_node);
    824986            iterated_over++;
    825987            if (ret == null) {
    826988                if (debug)
    public class QuadBuckets implements Coll  
    834996            // 1. Back up to the thing we just returned
    835997            // 2. move the index back since we removed
    836998            //    an element
    837             index_in_leaf--;
    838             current_leaf.content.remove(index_in_leaf);
     999            content_index--;
     1000            T object = peek();
     1001            current_node.content.remove(content_index);
    8391002        }
    8401003    }
    841     public Iterator<Node> iterator()
     1004    public Iterator<T> iterator()
    8421005    {
    8431006        return new QuadBucketIterator(this);
    8441007    }
    public class QuadBuckets implements Coll  
    8501013            out(this + " QuadBuckets size: " + ret);
    8511014        return ret;
    8521015    }
     1016    public int size_iter()
     1017    {
     1018        int count = 0;
     1019        Iterator<T> i = this.iterator();
     1020        while (i.hasNext()) {
     1021            i.next();
     1022            count++;
     1023        }
     1024        return count;
     1025    }
    8531026    public boolean isEmpty()
    8541027    {
    8551028        if (this.size() == 0)
    public class QuadBuckets implements Coll  
    8671040            out("search bbox after sanity: " +  bbox);
    8681041        return bbox;
    8691042    }
    870     List<Node> search(LatLon point, double radius)
     1043    List<T> search(Way w)
     1044    {
     1045        BBox way_bbox = new BBox(w);
     1046        return this.search(way_bbox);
     1047    }
     1048    public List<T> search(Node n, double radius)
    8711049    {
    872         return this.search(search_to_bbox(point, radius), point, radius);
     1050        return this.search(n.getCoor(), radius);
    8731051    }
    874     List<Node> search(BBox bbox)
     1052    public List<T> search(LatLon point, double radius)
    8751053    {
    876         return this.search(bbox, null, -1);
     1054        if (point == null)
     1055            return Collections.emptyList();
     1056        return this.search(search_to_bbox(point, radius));
    8771057    }
    878     public List<Node> search(LatLon b1, LatLon b2)
     1058    public List<T> search(LatLon b1, LatLon b2)
    8791059    {
    8801060        BBox bbox = new BBox(b1.lon(), b1.lat(), b2.lon(), b2.lat());
    8811061        bbox.sanity();
    8821062        return this.search(bbox);
    8831063    }
    884     List<Node> search(BBox bbox, LatLon point, double radius)
     1064    List<T> search(BBox search_bbox)
    8851065    {
    8861066        if (debug) {
    887             out("qb root search at " + point + " around: " + radius);
     1067            out("qb root search at " + search_bbox);
    8881068            out("root bbox: " + root.bbox());
    8891069        }
    890         List<Node> ret = null;
     1070        List<T> ret;
    8911071        // Doing this cuts down search cost on a real-life data
    8921072        // set by about 25%
    8931073        boolean cache_searches = true;
    public class QuadBuckets implements Coll  
    8971077            // Walk back up the tree when the last
    8981078            // search spot can not cover the current
    8991079            // search
    900             while (!search_cache.bbox().bounds(bbox)) {
     1080            while (!search_cache.bbox().bounds(search_bbox)) {
     1081                out("bbox: " + search_bbox);
     1082                if (debug) {
     1083                    out("search_cache: " + search_cache + " level: " + search_cache.level);
     1084                    out("search_cache.bbox(): " + search_cache.bbox());
     1085                }
    9011086                search_cache = search_cache.parent;
     1087                if (debug)
     1088                    out("new search_cache: " + search_cache);
    9021089            }
    9031090        } else {
    9041091            search_cache = root;
    9051092        }
    906         return search_cache.search(bbox, point, radius);
     1093        ret = search_cache.search(search_bbox);
     1094        if (ret == null)
     1095            ret = new ArrayList<T>();
     1096        // A way that spans this bucket may be stored in one
     1097        // of the nodes which is a parent of the search cache
     1098        QBLevel tmp = search_cache.parent;
     1099        while (tmp != null) {
     1100            List<T> content_result = tmp.search_contents(search_bbox);
     1101            if (content_result != null)
     1102                ret.addAll(content_result);
     1103            tmp = tmp.parent;
     1104        }
     1105        if (ret == null || ret.size() == 0)
     1106            return Collections.emptyList();
     1107        if (debug)
     1108            out("search of QuadBuckets for " + search_bbox + " ret len: " + ret.size());
     1109        return ret;
    9071110    }
    9081111}
  • src/org/openstreetmap/josm/data/coor/QuadTiling.java

     
    9797        int total_shift = TILES_PER_LEVEL_SHIFT*(NR_LEVELS-level-1);
    9898        return (int)(mask & (quad >> total_shift));
    9999    }
    100     static public int index(Node n, int level)
     100    static public int index(LatLon coor, int level)
    101101    {
    102         LatLon coor = n.getCoor();
    103102        // The nodes that don't return coordinates will all get
    104103        // stuck in a single tile.  Hopefully there are not too
    105104        // many of them
    106105        if (coor == null)
    107106            return 0;
    108         long quad = coorToTile(n.getCoor());
     107        long quad = coorToTile(coor);
    109108        return index(level, quad);
    110109    }
    111110}