7

ズーム/パン機能を備えた d3.js 散布図を作成しました。ここですべてを見ることができます (すべてを見るには「新しいウィンドウで開く」をクリックしてください): http://bl.ocks.org/129f64bfa2b0d48d27c9

私が把握できていない機能がいくつかあります。誰かが私を正しい方向に向けることができれば、それを手に入れたいと思います。

  1. X/Y ズーム/パン境界を領域に適用して、特定のポイント (ゼロなど) より下にドラッグできないようにしたいと考えています。
  2. また、Google マップ スタイルの +/- ズーム ボタンの作成にも挑戦しましたが、成功しませんでした。何か案は?

それほど重要ではありませんが、私が解決策を見つけた領域がいくつかありますが、それは非常に大雑把なので、より良い解決策があれば教えてください。

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

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

私が使用したズーム変換は非常にアドホックで、基本的に任意の定数を使用して位置を多かれ少なかれ適切な場所に保ちます。これは理想的ではありません。より普遍的に健全な手法の提案を歓迎します。ただし、この場合は十分に機能します。

4

2 に答える 2

12

median関数から始めるには、配列とオプションのアクセサーを使用するだけです。したがって、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())]);
    }
    ....
}        

軸をネガティブにするためにズームインしたいが、パンしない場合は、いくつかのトリッキーなシナリオに入る可能性があることに注意してください。

これは日付が付けられている可能性がありますが、D3.jsでズームまたはパンするときにドメインを制限するを確認してください

ズーム動作には、あるポイントでのパンとズームを制限する機能があったことにも注意してください。しかし、コードは後のアップデートで削除されました。

于 2013-02-25T20:24:13.393 に答える
-1

車輪の再発明は好きではありません。ズームできる散布図を探していました。Highcharts はその 1 つですが、D3 に基づいた plotly があります。これは、ズームを可能にするだけでなく、散布図にライン データセットを含めることもできます。プロット ライブラリ。私はそれを試してみます:

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

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

このような優れたライブラリを使用すると、多くの時間と労力を節約できます。

于 2016-02-17T09:07:46.297 に答える