【问题标题】:d3 v4 voronoi find nearest neighbours in a scatterplot svg filled with (dots / circles)d3 v4 voronoi 在散点图中找到最近的邻居 svg 填充(点/圆圈)
【发布时间】:2018-03-12 06:46:17
【问题描述】:

我试图在这个 sn-p 的帮助下使用下面附加的数据在散点图中找到最近的邻居 -

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 没有帮助,那么会发布它们)。

【问题讨论】:

  • 最近的邻居可以解释为那些紧邻的(与目标共享边的那些单元格)。所以要澄清一下,您是在查看某个半径内的单元格还是那些与给定单元格共享边缘的单元格?
  • 寻找半径内的单元格

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


【解决方案1】:

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

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

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

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

  1. voronoiDiagram.find() 开始查找点所在的单元格
  2. 找到找到的单元格的邻居
  3. 对于每个邻居,看看它的点是否在指定的半径内
  4. 如果相邻点在指定半径内:
    • 将邻居添加到具有指定半径内的点的单元格列表中,
    • 使用邻居重复步骤 2 到 4
  5. 在指定半径内没有找到更多邻居时停止,(保留已检查单元的列表以确保未检查两次)。

下面的sn-p使用上述过程(在函数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};
}
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"&gt;&lt;/script&gt;

【讨论】:

  • 感谢此次更新。有了你的回答,我现在已经阅读了更多关于 voronois 的信息并了解了更多。
  • 嗨 @andrew-reid 如果你对 d3 平行坐标图有所了解,能否请你帮忙解决这些问题 - stackoverflow.com/questions/49329600/…stackoverflow.com/questions/49334965/…
  • 我在平行坐标方面做得不多,但我可以看看 - 可能需要一两天才能有机会,我与移动/蜂窝服务的距离很远(和电网)这个周末。
  • 又回到了文明,但我不熟悉你正在使用的插件 - 我需要一些时间来仔细看看
猜你喜欢
  • 2017-11-23
  • 1970-01-01
  • 2019-09-10
  • 1970-01-01
  • 2014-08-29
  • 2017-09-08
  • 2013-06-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多