d3.js散点图 - 缩放/拖动边界,缩放按钮,重置缩放,计算中位数

时间:2013-02-25 15:03:23

标签: javascript d3.js zoom drag scatter-plot

我已经使用缩放/平移功能构建了d3.js散点图。你可以在这里看到完整的东西(点击“在新窗口中打开”以查看整个内容): http://bl.ocks.org/129f64bfa2b0d48d27c9

有一些我无法弄清楚的功能,如果有人能指出我正确的方向,我会爱上它:

  1. 我想将X / Y缩放/平移边界应用于该区域,以便您无法将其拖动到特定点(例如零)以下。
  2. 我还尝试创建Google地图样式+/-缩放按钮,没有任何成功。有什么想法吗?
  3. 更重要的是,还有一些方面我已经找到了解决方案,但它非常粗糙,所以如果你有更好的解决方案,那么请告诉我:

    1. 我添加了一个“重置缩放”按钮,但它只删除了图形并在其位置生成了一个新图形,而不是实际缩放对象。理想情况下,它应该实际重置缩放。
    2. 我编写了自己的函数来计算X和Y数据的中位数。但是我确信必须有一个更好的方法来使用d3.median这样做但我无法弄清楚如何使它工作。

      var xMed = median(_.map(data,function(d){ return d.TotalEmployed2011;}));
      var yMed = median(_.map(data,function(d){ return d.MedianSalary2011;}));
      
      function median(values) {
          values.sort( function(a,b) {return a - b;} );
          var half = Math.floor(values.length/2);
      
          if(values.length % 2)
              return values[half];
          else
              return (parseFloat(values[half-1]) + parseFloat(values[half])) / 2.0;
      };
      
    3. JS的非常简化(即旧版)的版本如下。您可以在https://gist.github.com/richardwestenra/129f64bfa2b0d48d27c9#file-main-js

      找到完整的脚本
      d3.csv("js/AllOccupations.csv", function(data) {
      
          var margin = {top: 30, right: 10, bottom: 50, left: 60},
              width = 960 - margin.left - margin.right,
              height = 500 - margin.top - margin.bottom;
      
          var xMax = d3.max(data, function(d) { return +d.TotalEmployed2011; }),
              xMin = 0,
              yMax = d3.max(data, function(d) { return +d.MedianSalary2011; }),
              yMin = 0;
      
          //Define scales
          var x = d3.scale.linear()
              .domain([xMin, xMax])
              .range([0, width]);
      
          var y = d3.scale.linear()
              .domain([yMin, yMax])
              .range([height, 0]);
      
          var colourScale = function(val){
              var colours = ['#9d3d38','#c5653a','#f9b743','#9bd6d7'];
              if (val > 30) {
                  return colours[0];
              } else if (val > 10) {
                  return colours[1];
              } else if (val > 0) {
                  return colours[2];
              } else {
                  return colours[3];
              }
          };
      
      
          //Define X axis
          var xAxis = d3.svg.axis()
              .scale(x)
              .orient("bottom")
              .tickSize(-height)
              .tickFormat(d3.format("s"));
      
          //Define Y axis
          var yAxis = d3.svg.axis()
              .scale(y)
              .orient("left")
              .ticks(5)
              .tickSize(-width)
              .tickFormat(d3.format("s"));
      
          var svg = d3.select("#chart").append("svg")
              .attr("width", width + margin.left + margin.right)
              .attr("height", height + margin.top + margin.bottom)
              .append("g")
              .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
              .call(d3.behavior.zoom().x(x).y(y).scaleExtent([1, 8]).on("zoom", zoom));
      
          svg.append("rect")
              .attr("width", width)
              .attr("height", height);
      
          svg.append("g")
              .attr("class", "x axis")
              .attr("transform", "translate(0," + height + ")")
              .call(xAxis);
      
          svg.append("g")
              .attr("class", "y axis")
              .call(yAxis);
      
          // Create points
          svg.selectAll("polygon")
              .data(data)
              .enter()
              .append("polygon")
              .attr("transform", function(d, i) {
                  return "translate("+x(d.TotalEmployed2011)+","+y(d.MedianSalary2011)+")";
              })
              .attr('points','4.569,2.637 0,5.276 -4.569,2.637 -4.569,-2.637 0,-5.276 4.569,-2.637')
              .attr("opacity","0.8")
              .attr("fill",function(d) {
                  return colourScale(d.ProjectedGrowth2020);
              });
      
          // Create X Axis label
          svg.append("text")
              .attr("class", "x label")
              .attr("text-anchor", "end")
              .attr("x", width)
              .attr("y", height + margin.bottom - 10)
              .text("Total Employment in 2011");
      
          // Create Y Axis label
          svg.append("text")
              .attr("class", "y label")
              .attr("text-anchor", "end")
              .attr("y", -margin.left)
              .attr("x", 0)
              .attr("dy", ".75em")
              .attr("transform", "rotate(-90)")
              .text("Median Annual Salary in 2011 ($)");
      
      
          function zoom() {
            svg.select(".x.axis").call(xAxis);
            svg.select(".y.axis").call(yAxis);
            svg.selectAll("polygon")
                  .attr("transform", function(d) {
                      return "translate("+x(d.TotalEmployed2011)+","+y(d.MedianSalary2011)+")";
                  });
          };
          }
      });
      

      任何帮助都会受到大力赞赏。谢谢!

      编辑:以下是我使用的修补程序的摘要,基于以下Superboggly的建议:

          // Zoom in/out buttons:
          d3.select('#zoomIn').on('click',function(){
              d3.event.preventDefault();
              if (zm.scale()< maxScale) {
                  zm.translate([trans(0,-10),trans(1,-350)]);
                  zm.scale(zm.scale()*2);
                  zoom();
              }
          });
          d3.select('#zoomOut').on('click',function(){
              d3.event.preventDefault();
              if (zm.scale()> minScale) {
                  zm.scale(zm.scale()*0.5);
                  zm.translate([trans(0,10),trans(1,350)]);
                  zoom();
              }
          });
          // Reset zoom button:
          d3.select('#zoomReset').on('click',function(){
              d3.event.preventDefault();
              zm.scale(1);
              zm.translate([0,0]);
              zoom();
          });
      
      
          function zoom() {
      
              // To restrict translation to 0 value
              if(y.domain()[0] < 0 && x.domain()[0] < 0) {
                  zm.translate([0, height * (1 - zm.scale())]);
              } else if(y.domain()[0] < 0) {
                  zm.translate([d3.event.translate[0], height * (1 - zm.scale())]);
              } else if(x.domain()[0] < 0) {
                  zm.translate([0, d3.event.translate[1]]);
              }
              ...
          };
      

      我使用的缩放转换非常特别,基本上使用abitrary常量来保持定位或多或少在正确的位置。这并不理想,我愿意为更普遍的声音技术提供建议。但是,在这种情况下它运作良好。

2 个答案:

答案 0 :(得分:11)

从中值函数开始,只需要一个数组和一个可选的访问器。所以你可以像使用max:

一样使用它
var med = d3.median(data, function(d) { return +d.TotalEmployed2011; });

至于其他人如果你拉出你的缩放行为,你可以更好地控制它。例如,而不是

var svg = d3.select()...call(d3.behavior.zoom()...) 

尝试:

var zm = d3.behavior.zoom().x(x).y(y).scaleExtent([1, 8]).on("zoom", zoom);
var svg = d3.select()...call(zm);

然后您可以直接设置缩放级别和翻译:

function zoomIn() {
   zm.scale(zm.scale()*2);
   // probably need to compute a new translation also
}

function reset() {
   zm.scale(1);
   zm.translate([0,0]);
}

限制平移范围有点棘手。您无法在缩放功能中根据自己的喜好更新翻译或缩放(或将缩放的“翻译”设置为您需要的缩放功能)。类似的东西(我认为在你的情况下):

function zoom() {
    if(y.domain()[0] < 0) {
        // To restrict translation to 0 value
        zm.translate([d3.event.translate[0], height * (1 - zm.scale())]);
    }
    ....
}        

请记住,如果您想要放大以允许轴上为负,但不平移,则会发现您遇到了一些棘手的情况。

这可能已过时,但请查看Limiting domain when zooming or panning in D3.js

另请注意,缩放行为确实具有限制one point处的平移和缩放功能。但是代码是在稍后的update中删除的。

答案 1 :(得分:-1)

我不想重新发明轮子。我正在寻找允许缩放的散点图。 Highcharts就是其中之一,但有一些情节,它基于D3并且不仅允许缩放,而且你也可以在散点图上有线数据集,我希望我的一些数据集,以及&# 39;很难找到其他情节库。我试一试:

https://plot.ly/javascript/line-and-scatter/

https://github.com/plotly/plotly.js

使用这样漂亮的库可以为您节省大量时间和精力。