d3 v4 voronoi在散点图svg中找到最近邻居(点/圆圈)

时间:2018-03-12 06:46:17

标签: d3.js scatter-plot nearest-neighbor voronoi quadtree

我试图使用下面附带的数据在散点图中找到最近的邻居,借助此片段 -

const voronoiDiagram = d3.voronoi()
                    .x(d => d.x)
                    .y(d => d.y)(data);
            data.forEach(function(d){   
                console.log(d, voronoiDiagram.find(d.x, d.y, 50));
            });

现在我使用的数据集是标准的虹膜萼片,花瓣长度数据 格式 -

{"sepalLength":7.7,"sepalWidth":3,"petalLength":"6.1","petalWidth":"2.3","species":"virginica","index":135,"x":374.99999999999994,"y":33.75,"vy":0,"vx":0},
{"sepalLength":6.3,"sepalWidth":3.4,"petalLength":"5.6","petalWidth":"2.4","species":"virginica","index":136,"x":524.9999999999999,"y":191.25,"vy":0,"vx":0},
{"sepalLength":6.4,"sepalWidth":3.1,"petalLength":"5.5","petalWidth":"1.8","species":"virginica","index":137,"x":412.5,"y":179.99999999999994,"vy":0,"vx":0},    
{"sepalLength":6,"sepalWidth":3,"petalLength":"4.8","petalWidth":"1.8","species":"virginica","index":138,"x":374.99999999999994,"y":225,"vy":0,"vx":0},
....

所以,基本上它的形式是 {d: {x, y, sepal length, width, petal length, width}

现在,我正试图从reference找到与d3 voronoi最近的邻居。

但是,我得到的只是结果 -

让我的数据集中的d点=

{"sepalLength":5.9,"sepalWidth":3,"petalLength":"5.1","petalWidth":"1.8","species":"virginica","index":149,"x":374.99999999999994,"y":236.24999999999997,"vy":0,"vx":0}

现在,voronoiDiagram.find(d.x, d.y, 50)导致了 -

"[375,236.25]"

也就是说,坐标的四舍五入而不是另一个点。

那么,如何从voronoi图中排除当前正在扫描的点。 此外,如果我排除这一点和&从性能角度重新计算一切都会好吗?

任何人都可以帮助我从一组点找到最近的邻居 与d3 voronoi / quadtrees(我已经尝试了几个来自Mike Bostock的例子,但由于一些错误,无法让他们在我的案例中工作 如果d3 voronoi没有帮助,他们会发布它们。

1 个答案:

答案 0 :(得分:1)

voronoiDiagram.find(y, x, r)最多只返回一次单元格。从API文档:

  

返回指向[x,y]的最近站点。如果指定了半径,则仅考虑半径距离内的站点。 (link

我之前读到的是复数,显然我从来没有仔细观察(我认为能够找到给定半径内的所有点有很大的实用性。)

我们可以做的是相当容易地创建一个函数:

  1. voronoiDiagram.find()开始,找到该点落入的单元格
  2. 找到找到的单元格的邻居
  3. 对于每个邻居,查看其点是否在指定的半径范围内
  4. 如果邻居点在指定的半径范围内:
    • 将邻居添加到指定半径范围内的点的单元格列表
    • 使用邻居重复步骤2到4
  5. 在指定的半径范围内找不到更多邻居时停止,(保留已经检查过的单元格列表以确保没有被检查过两次)。
  6. 下面的代码段使用上面的过程(在函数findAll(x,y,r)中)将指定距离内的点显示为橙色,最近的点将为红色(我设置了区分两者的功能) )。

    
    
    var width = 500;
    var height = 300;
    
    var data = d3.range(200).map(function(d) {
      var x = Math.random()*width;
      var y = Math.random()*height;
      var index = d;
      return {x:x,y:y,index:index}
    });
    
    var svg = d3.select("body")
      .append("svg")
      .attr("width",width)
      .attr("height",height);
      
    var circles = svg.selectAll()
      .data(data, function(d,i) { return d.index; });
      
      
    circles = circles.enter()
      .append("circle")
      .attr("cx",function(d) { return d.x; })
      .attr("cy",function(d) { return d.y; })
      .attr("r",3)
      .attr("fill","steelblue")
      .merge(circles);
      
    var voronoi = d3.voronoi()
      .x(function(d) { return d.x; })
      .y(function(d) { return d.y; })
      .size([width,height])(data);
      
    
    var results = findAll(width/2,height/2,30);
    
    circles.data(results.nearest,function(d) { return d.index; })
      .attr("fill","orange");
    circles.data([results.center],function(d) { return d.index; })
      .attr("fill","crimson");
      
    var circle = svg.append("circle")
      .attr("cx",width/2)
      .attr("cy",height/2)
      .attr("r",30)
      .attr("fill","none")
      .attr("stroke","black")
      .attr("stroke-width",1);
      
      
    circle.transition()
      .attrTween("r", function() {
        var node = this;
          return function(t) { 
            var r = d3.interpolate(30,148)(t);
            var results = findAll(width/2,height/2,r);
            circles.data(results.nearest,function(d) { return d.index; })
              .attr("fill","orange");
            return r;
          }
        })
        .duration(2000)
        .delay(1000);
    
    	
    function findAll(x,y,r) {
      var start = voronoi.find(x,y,r);
      
      if(!start) return {center:[],nearest:[]}  ; // no results.
      
      var queue = [start];
      var checked = [];
      var results = [];
    	
      for(i = 0; i < queue.length; i++) {
        checked.push(queue[i].index);                           // don't check cells twice
        var edges = voronoi.cells[queue[i].index].halfedges;   
        // use edges to find neighbors
        var neighbors = edges.map(function(e) {
          if(voronoi.edges[e].left == queue[i]) return voronoi.edges[e].right; 
    	  else return voronoi.edges[e].left;		
        })
        // for each neighbor, see if its point is within the radius:
        neighbors.forEach(function(n) { 
          if (n && checked.indexOf(n.index) == -1) {
          var dx = n[0] - x;
          var dy = n[1] - y;
          var d = Math.sqrt(dx*dx+dy*dy);
    			
          if(d>r) checked.push(n.index)          // don't check cells twice
          else {
            queue.push(n);   // add to queue
            results.push(n); // add to results
          }
        }	
      })
    }
      // center: the point/cell that is closest/overlapping, and within the specified radius, of point x,y
      // nearest: all other cells within the specified radius of point x,y
      return {center:start,nearest:results};
    }
    &#13;
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
    &#13;
    &#13;
    &#13;