d3.js散点图 - 缩放/拖动边界,缩放按钮,重置缩放,计算中值 [英] d3.js scatter plot - zoom/drag boundaries, zoom buttons, reset zoom, calculate median

查看:4120
本文介绍了d3.js散点图 - 缩放/拖动边界,缩放按钮,重置缩放,计算中值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经建立了一个带有缩放/平移功能的d3.js散点图。您可以在这里查看完整的内容(点击在新窗口中打开查看整个内容):
http://bl.ocks.org/129f64bfa2b0d48d27c9

I've built a d3.js scatter plot with zoom/pan functionality. You can see the full thing here (click 'Open in a new window' to see the whole thing): http://bl.ocks.org/129f64bfa2b0d48d27c9

有几个功能,我一直无法弄清楚,我会如果有人可以指出我的方向是正确的:

There are a couple of features that I've been unable to figure out, that I'd love a hand with it if someone can point me in the right direction:


  1. 我想将X / Y缩放/平移边界应用于

  2. 我还创建了Google地图样式+/-缩放按钮,但没有任何成功。任何想法?

更重要的是,还有一些领域我已经找到了解决方案,所以如果你有更好的解决方案,那么请让我知道:

Much less importantly, there are also a couple of areas where I've figured out a solution but it's very rough, so if you have a better solution then please do let me know:


  1. 我添加了一个重置缩放按钮,但它只是删除图形并在其位置生成一个新的,而不是实际缩放对象。

  2. 我已经编写了自己的函数来计算X和Y数据的中值。但是我敢肯定,有一个更好的方法来做这个与d3.median,但我不知道如何使它工作。

  1. I've added a 'reset zoom' button but it merely deletes the graph and generates a new one in its place, rather than actually zooming the objects. Ideally it should actually reset the zoom.
  2. I've written my own function to calculate the median of the X and Y data. However I'm sure that there must be a better way to do this with d3.median but I can't figure out how to make it work.

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;
};


JS下面。您可以在 https://gist.github.com/richardwestenra/找到完整脚本129f64bfa2b0d48d27c9#file-main-js

A very simplified (i.e. old) version of the JS is below. You can find the full script at 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)+")";
            });
    };
    }
});

任何帮助将被大量赞赏。感谢!

Any help would be massively appreciated. Thanks!

编辑:以下是我使用的修正的摘要,基于Superboggly的建议如下:

Here is a summary of the fixes I used, based on Superboggly's suggestions below:

    // 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]]);
        }
        ...
    };

我使用的缩放转换非常临时,基本上使用辅助常数来保持定位更多,少在正确的地方。这不是理想的,我愿意接受更普遍的声音技术的建议。

The zoom translation that I used is very ad hoc and basically uses abitrary constants to keep the positioning more or less in the right place. It's not ideal, and I'd be willing to entertain suggestions for a more universally sound technique. However, it works well enough in this case.

推荐答案

开始使用中值函数只需要一个数组和一个可选的访问器。所以你可以使用与你使用max相同的方式:

To start with the median function just takes an array and an optional accessor. So you can use it the same way you use max:

var med = d3.median(data, function(d) { return +d.TotalEmployed2011; });

至于其他如果你拉出你的缩放行为,你可以控制它一点。例如代替

As for the others if you pull out your zoom behaviour you can control it a bit better. So for example instead of

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);

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

Then you can set the zoom level and translation directly:

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

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

限制平移范围有点棘手。你可以简单地不更新时,翻译或缩放不是你喜欢里面的缩放功能(或设置缩放的翻译到你需要的)。 (我想在你的情况下):

Restricting the panning range is a bit trickier. You can simply not update when the translate or scale is not to your liking inside you zoom function (or set the zoom's "translate" to what you need it to be). Something like (I think in your case):

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

如果你想放大以允许一个负的轴,但平移不是你会发现你陷入一些棘手的情况。

Keep in mind that if you want zooming in to allow a negative on the axis, but panning not to you will find you get into some tricky scenarios.

这可能是过时的,但检查out 在D3.js中缩放或平移时限制域

This might be dated, but check out Limiting domain when zooming or panning in D3.js

请注意,缩放行为确实具有在一点。但代码是在稍后更新中取出的。

Note also that the zoom behaviour did have functionality for limiting panning and zooming at one point. But the code was taken out in a later update.

这篇关于d3.js散点图 - 缩放/拖动边界,缩放按钮,重置缩放,计算中值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆