// source: http://bl.ocks.org/llb4ll/8709363 var kNearest = {}; (function() { kNearest.nodes = nodes; kNearest.neighbors = knearest; // PDS Collect a list of nodes to draw rectangles, adding extent and depth data function nodes(quadtree) { var nodes = []; quadtree.depth = 0; // root quadtree.visit(function(node, x1, y1, x2, y2) { node.x1 = x1; node.y1 = y1; node.x2 = x2; node.y2 = y2; nodes.push(node); for (var i=0; i<4; i++) { if (node.nodes[i]) node.nodes[i].depth = node.depth+1; } }); return nodes; } // calculate euclidean distance of two points with coordinates: a(ax, ay) and b(bx, by) function euclidDistance(ax, ay, bx, by){ return Math.sqrt(Math.pow(ax-bx, 2) + Math.pow(ay-by, 2)); } // calculate mindist between searchpoint and rectangle function mindist(x, y, x1, y1, x2, y2){ var dx1 = x - x1, dx2 = x - x2, dy1 = y - y1, dy2 = y - y2; if (dx1*dx2 < 0) { // x is between x1 and x2 if (dy1*dy2 < 0) { // (x,y) is inside the rectangle return 0; // return 0 as point is in rect } return Math.min(Math.abs(dy1),Math.abs(dy2)); } if (dy1*dy2 < 0) { // y is between y1 and y2 // we don't have to test for being inside the rectangle, it's already tested. return Math.min(Math.abs(dx1),Math.abs(dx2)); } return Math.min( Math.min(euclidDistance(x,y,x1,y1),euclidDistance(x,y,x2,y2)), Math.min(euclidDistance(x,y,x1,y2),euclidDistance(x,y,x2,y1)) ); } // Find the nodes within the specified rectangle. function knearest(bestqueue, resultqueue, x, y, k) { // sort children according to their mindist/dist to searchpoint bestqueue.sort(function(a, b){ // add minidst to nodes if not there already [a, b].forEach(function(val, idx, array){ if(val.mindist == undefined){ val.scanned = true; if(val.leaf){ val.point.scanned=true; val.mindist = euclidDistance(x, y, val.x, val.y); }else{ val.mindist = mindist(x, y, val.x1, val.y1, val.x2, val.y2); } } }); return b.mindist - a.mindist; }); // add nearest leafs if any for (var i=bestqueue.length-1; i>=0; i--){ var elem = bestqueue[i]; if(elem.leaf){ elem.point.selected = true; bestqueue.pop(); resultqueue.push(elem); }else{ break; } if(resultqueue.length >=k){ break; } } // check if enough points found if(resultqueue.length >=k || bestqueue.length == 0){ // return if k neighbors found return; }else{ // add child nodes to bestqueue and recurse var vistitednode = bestqueue.pop(); vistitednode.visited = true; // add nodes to queue vistitednode.nodes.forEach(function(val, idx, array){ bestqueue.push(val); }); // recursion knearest(bestqueue, resultqueue, x, y, k); } } }());