mourner / rbush

RBush — a high-performance JavaScript R-tree-based 2D spatial index for points and rectangles

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

How to limit the result count from the query?

hoogw opened this issue · comments

commented

Is there something like this, return max 10 item by

  `tree.search(
                        {minX,..... maxY:...}, 
                        10).`

It is same idea on sql 'top'

     `select top 100, * from table` 
commented

It's right in the readme, pal: https://github.com/mourner/rbush#k-nearest-neighbors

No, it is not related to my question.

commented

Is there something like this, return max 10 item by

  `tree.search(
                        {minX,..... maxY:...}, 
                        10).`

KNN is searching for 10 around a point,
What I want is for 10 within a bbox, in KNN, how to implement a query select top 10 intersect a bbox? KNN can only query around a point, not within a bounding box.

commented

I add limit as
tree.search(bbox,limit)

will return max limit count:

rbush.js do following modification:
I test it, it works great!!!!

Can you put a new request to add this new feature? It will be very useful for many people.
Because googlemaps api will freeze when displaying over 5000 feature,
I must limit the search results size under (for example 300 feature) for smooth(googlemap api) interaction.

   `RBush.prototype.search = function search (bbox, limit) {
    var node = this.data;
    var result = [];

    if (!intersects(bbox, node)) { return result; }

    var toBBox = this.toBBox;
    var nodesToSearch = [];

    while (node) {
        for (var i = 0; i < node.children.length; i++) {

            // limit the result size
            if (result.length > limit) { return result;}

            var child = node.children[i];
            var childBBox = node.leaf ? toBBox(child) : child;

            if (intersects(bbox, childBBox)) {
                if (node.leaf) { result.push(child); }
                else if (contains(bbox, childBBox)) { this._all(child, result); }
                else { nodesToSearch.push(child); }
            }
        }
        node = nodesToSearch.pop();
    }

    return result;
};`

@hoogw you also need to add the limit to _all so that it doesn't add more than the limit to the result.

I think it's fine to simply augment the method on the application side, no need to have this option in the core.

commented

@hoogw you also need to add the limit to _all so that it doesn't add more than the limit to the result.

I think it's fine to simply augment the method on the application side, no need to have this option in the core.

@mourner mourner
I am not sure is this correct? Do I have to put a big number 99999999?

RBush.prototype.all = function all () { return this._all(this.data, [], 99999999); };

      `if (intersects(bbox, childBBox)) {
                if (node.leaf) { result.push(child); }
                else if (contains(bbox, childBBox)) { this._all(child, result, limit); }
                else { nodesToSearch.push(child); }
            }

`

    `RBush.prototype._all = function _all (node, result, limit) {
    var nodesToSearch = [];
    while (node) {

        if (result.length > limit) { return result;}

        if (node.leaf) { result.push.apply(result, node.children); }
        else { nodesToSearch.push.apply(nodesToSearch, node.children); }

        node = nodesToSearch.pop();
    }
    return result;
};`
commented

@mourner mourner
what the all() function use for?

Is my modification Safe?
Hope my modification NOT break other things. This is my big concern.

RBush.prototype.all = function all () { return this._all(this.data, [], 0); };

           ` //add limit result size
RBush.prototype._all = function _all (node, result, limit) {
    var nodesToSearch = [];
    while (node) {

         //add limit result size
        // limit = 0 means, no limit 
        if (limit != 0){
            if (result.length > limit) { return result;}
        } 
        

        if (node.leaf) { result.push.apply(result, node.children); }
        else { nodesToSearch.push.apply(nodesToSearch, node.children); }

        node = nodesToSearch.pop();
    }
    return result;
};`
commented

Here is my latest modification, it works as expect:
credit to @mourner mourner to remind me must limit size at _all() function.
This is very important, without limit _all(), you will run into a leak,

You limit 20, but because of the leak, you could get 2000 in size, horrible leaking.

Now I fix the leaking.

              `//modification by add limit result size
RBush.prototype.search = function search (bbox, limit) {
    var node = this.data;
    var result = [];

    if (!intersects(bbox, node)) { return result; }

    var toBBox = this.toBBox;
    var nodesToSearch = [];

    while (node) {
        for (var i = 0; i < node.children.length; i++) {

           

            var child = node.children[i];
            var childBBox = node.leaf ? toBBox(child) : child;

            if (intersects(bbox, childBBox)) {

                if (node.leaf) { 
                    
                        //modification by add limit result size
                        // leaf means lowest level (feature element itself), 'child' in here means leaf, element iteself.
                        console.log('rbush -----1-----> result.length ', result.length, child, limit)
                        if ((result.length) > limit) { return result;}



                        result.push(child); 

                } else if (contains(bbox, childBBox)) { 

                                                                                
                                        //modification by add limit result size
                                        // secondary lowest level, 'child' in here means a smallest branch with immediate leaf only
                                        // we need to pass limit via _all()
                                        console.log('rbush -----2-----> result.length ', result.length, child, child.children.length, limit)
                                       


                                       this._all(child, result, limit); 


                } else { 


                    //modification by add limit result size
                    // 'child' in here means high level big branch, with multiple mix of level 1,2,3
                    // we do not limit high level branch node, only limit lowest level leaf and lowest branch with immediate leaf _all()
                    console.log('rbush -----3-----> result.length ', result.length, child, limit)
                   
                    
                     nodesToSearch.push(child); 
                }



            }



        }
        node = nodesToSearch.pop();
    }

    return result;
};`

also _all() function

   `

//modification by add limit result size 
RBush.prototype._all = function _all (node, result, limit) {
    var nodesToSearch = [];
    while (node) {

        


        if (node.leaf) { 
            
                        //modification by add limit result size
                        if (limit != 0){
                                            // leaf means relatively lowest level ( node.children means to real lowest leaf )
                                            console.log('rbush ----- node _all() -----> result.length ', result.length, node.children, limit)
                                            if (((result.length) + (node.children.length))> limit) { return result;}
                        } else {
                        
                            // limit = 0 means, no limit, nothing to do
                        }      
                        
                        




               result.push.apply(result, node.children); 

        }else { 
            
                 //modification by add limit result size
                 // we do not limit high level branch node, only limit lowest level leaf and lowest branch with immediate leaf
                nodesToSearch.push.apply(nodesToSearch, node.children); 
            
        }

        node = nodesToSearch.pop();
    }
    return result;
};

`

not sure all() was use for? but anyway, just be safe,

     ` RBush.prototype.all = function all () {
   //modification by add limit result size
   //return this._all(this.data, []);
    return this._all(this.data, [], 0);
};`
commented

Now the only problem is geographic distribution is not balanced,
As you can see, I limit 20 of total 151891,

Ideally, the 20 point should geographically even distribute cross whole map view window,

reality is it centralize in right side corner, ( see those cluster of yellow tiny circle on pic)
image

How to geographically even the distribution while limit the size?

commented

here is another example, I limit it to 500, they all cluster, centralized in middle,
I want it even distribute, not centralized

image

commented

if I do NOT limit lowest level leaf, but only limit _all(),
it run into leak, all point surrounding map view border, may be all because of leaked point. (644 leaking)
center cluster may still be the 500,
image