简体   繁体   English

D3放大画笔范围

[英]D3 zoom in on brush extent

I am working on a web tool that should create a scatterplot with given data. 我正在开发一个Web工具,它应该创建一个包含给定数据的散点图。 Since I am new to anything visualizing (and not an expert on JavaScript either), I decided going with D3. 由于我对任何可视化的东西都是新手(也不是JavaScript的专家),所以我决定选择D3。

Following tutorials, observing examples and trying stuff I got a scatterplot going with brush selection (which does nothing yet). 下面的教程,观察示例和尝试的东西,我得到一个散点图与刷选择(它什么也没做)。 Now since thegoal is plotting genes, a lot of them will be extremely close to each other in terms of x and y coordinates. 现在由于目标是绘制基因,因此在x和y坐标方面,它们中的很多都将非常接近。 So I would like to implement something like this , but in both the x and y direction. 所以我想实现的东西像这样的 ,但在X和Y方向上。 Basically zoom in on the brushed section. 基本上放大拉丝部分。

So far I am unable to create this behaviour, at this point my axes do change, but no longer show values and none of the zooming happens. 到目前为止,我无法创建此行为,此时我的轴确实发生了变化,但不再显示值,也没有任何缩放发生。 You can see it live on my toy subdomain . 你可以在我的玩具子域上看到它。 I am not sure where it goes wrong, so any help is appreciated. 我不知道哪里出了问题,所以任何帮助都表示赞赏。

The javascript used on the toy domain: 玩具域使用的javascript:

// General variables

var svg, width, height;
var padding = 30;

var dataset = [
              [ 5,     20 ],
              [ 480,   90 ],
              [ 250,   50 ],
              [ 100,   33 ],
              [ 330,   95 ],
              [ 410,   12 ],
              [ 475,   44 ],
              [ 25,    67 ],
              [ 85,    21 ],
              [ 220,   88 ]
          ];

function drawGraph(){
    scatterplot_area_size = $("#scatterplot").width();
    width = scatterplot_area_size;
    height = scatterplot_area_size * 0.75;

    console.log(width);

    // Retrieve the interval of the x and y data
    var maxX = d3.max(dataset, function(d) {
        return d[0];  //References first value in each sub-array
    });

    var minX = d3.min(dataset, function(d) {
        return d[0];  //References first value in each sub-array
    });

    var maxY = d3.max(dataset, function(d) {
        return d[1];  //References second value in each sub-array
    });

    var minY = d3.min(dataset, function(d) {
        return d[1];  //References second value in each sub-array
    });

    // Create a (square) scatterplot area in the div with id scatterplot
    // which spans the entire div in width
    svg = d3.select("#scatterplot")
            .append("svg")
            .attr("width", width)
            .attr("height", height);

    // plot all points
    var points = svg.append("g")
        .attr("class", "point")
        .selectAll("point")
        .data(dataset)
        .enter()
        .append("circle");


    console.log(minX + " " + minY);

    // Create x and y scales
    var x = d3.scale.linear()
                     .domain([minX, maxX])
                     .range([padding, (width-padding)]);

    var y = d3.scale.linear()
                     .domain([minY, maxY])
                     .range([(height-padding), padding]); // Reverse the scale to let high values show at the top and low values at the bottom

    // Set the x and y positions as well as the radius (hard coded 5)
    points.attr("cx", function(d) {
        return x(d[0]);
    })
   .attr("cy", function(d) {
        return y(d[1]);
    })
    .attr("r", 5);

    // Create the x and y axes
    var xAxis = d3.svg.axis()
                  .scale(x)
                  .ticks(10)
                  .orient("bottom");

    var yAxis = d3.svg.axis()
                  .scale(y)
                  .ticks(10)
                  .orient("left");

    // Add the axes to the scatterplot
    svg.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + (height-padding) + ")")
        .call(xAxis);

    svg.append("g")
        .attr("class", "y axis")
        .attr("transform", "translate(" + padding + ",0)")
        .call(yAxis);

    // Allow for brushing (selection of points)
    var brush = d3.svg.brush()
        .x(x)
        .y(y)
        .on("brush", brushmove)
        .on("brushend", brushend);

    svg.append("g")
        .attr("class", "brush")
        .call(brush)
        .selectAll('rect')
        .attr('height', height);

    function brushmove() {
      var extent = brush.extent();
      points.classed("selected", function(d) {
        return extent[0][0] <= d[0] && d[0] <= extent[1][0]
                && extent[0][1] <= d[1] && d[1] <= extent[1][1];
      });
    }

    function brushend() {
        x.domain(brush.extent());
        y.domain(brush.extent());

        transition_data();
        reset_axis();

        points.classed("selected", false);
        d3.select(".brush").call(brush.clear());
    }

    function transition_data() {
        console.log("transistion data called");
        d3.selectAll("point")
        .transition()
        .duration(500)
        .attr("cx", function(d) { console.log(d[0]); return x(d[0]); })
        .attr("cy", function(d) { return y(d[1]); });
    }

    function reset_axis() {
        svg.transition().duration(500)
        .select(".x.axis")
        .call(xAxis);

        svg.transition().duration(500)
        .select(".y.axis")
        .call(yAxis);
    }
}

// Wait until the document is loaded, then draw the graph.
$( document ).ready(function() {
    drawGraph();
});

// If the window is resized, redraw graph to make it fit again.
$( window ).resize(function() {
    if(typeof svg != 'undefined'){
        $("#scatterplot").empty();
    } 
    drawGraph();
});

There are two errors in your code: 您的代码中有两个错误:

  1. In your function brushend() you are setting the domains of your scales using the return value of brush.extent() in both cases. 在您的函数brushend()您将使用brush.extent()的返回值在两种情况下设置缩放的域。 Notice, however, that the extent returned by your brush is a two-dimensional array like [[xMin,yMin],[xMax,yMax]] . 但请注意,画笔返回的范围是二维数组,如[[xMin,yMin],[xMax,yMax]] You have to set your domains using 您必须使用设置域名

     var extent = brush.extent(); x.domain([extent[0][0],extent[1][0]]); // x.domain([xMin,xMax]) y.domain([extent[0][1],extent[1][1]]); // y.domain([yMin,yMax]) 

    These adjustments will have your axes scale correctly while the points are still fixed. 当点仍然固定时,这些调整将使轴正确缩放。

  2. You are inserting and manipulating your data points with d3.selectAll("point") which refers to them as being of type svg:point . 您正在使用d3.selectAll("point")插入和操作数据点,它们将它们称为svg:point类型。 However, there is no such type of svg element. 但是,没有这种类型的svg元素。 Though your call to d3.selectAll("point") is still valid it will always yield an empty selection because there is no element to be selected. 虽然你对d3.selectAll("point")调用仍然有效,但它总是会产生一个空选择,因为没有要选择的元素。 For this reason your first call to the function when setting up the svg does work, since it will put all the data bound to the enter selection giving the desired result. 因此,在设置svg时首次调用该函数确实有效,因为它会将所有绑定到enter选择的数据放入所需的结果中。 For the sake of correctness you should correct this to 为了正确起见,你应该纠正这个问题

     // plot all points var points = svg.append("g") .attr("class", "point") // .selectAll("point") .selectAll("circle") // <-- This will work .data(dataset) .enter() .append("circle"); 

    When trying to update your points after having set the scales' domains the faulty code will not work because in this case the empty selection is meaningless and will have no effect on any element. 在设置了比例的域之后尝试更新你的点时,错误的代码将不起作用,因为在这种情况下,空选择是没有意义的,并且对任何元素都没有影响。 Since in the above quoted statement you have already stored a reference to your selection in var points there is no need to select them again. 由于在上面引用的语句中,您已经在var points存储了对选择的引用,因此无需再次选择它们。 You could just rewrite your update as follows: 您可以按如下方式重写更新:

     function transition_data() { // d3.selectAll("point") // Instead of this... points // <-- ...use the reference to your selection .transition() .duration(500) .attr("cx", function(d) { return x(d[0]); }) .attr("cy", function(d) { return y(d[1]); }); } 

I put together a working JSFiddle to demonstrate the solution. 我整理了一个有效的JSFiddle来演示解决方案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM