2013-02-25 4 views
6

Ich habe ein d3.js Streudiagramm mit Zoom/Schwenk-Funktionalität erstellt. Sie können die ganze Sache hier sehen (klicken Sie auf "In einem neuen Fenster öffnen", um die ganze Sache zu sehen): http://bl.ocks.org/129f64bfa2b0d48d27c9d3.js Streudiagramm - zoomen/ziehen Grenzen, Zoom-Tasten, Zoom zurücksetzen, Median berechnen

Es gibt ein paar Funktionen, die ich nicht herausfinden konnte, dass ich würde lieben eine Hand mit ihm, wenn jemand mich in der richtigen Richtung zeigen kann:

  1. ich mag X/Y Zoom/pan-Grenzen auf den Bereich anzuwenden, so dass Sie es nicht unter einem bestimmten Punkt ziehen (zB Null).
  2. Ich habe auch einen Stich bei der Erstellung von Google Maps Stil +/- Zoom-Tasten gemacht, ohne Erfolg. Irgendwelche Ideen?

Viel weniger wichtig ist, gibt es auch ein paar Bereiche, in denen ich eine Lösung herausgefunden haben, aber es ist sehr rau, so dass, wenn Sie eine bessere Lösung haben, dann gehen Sie bitte lassen Sie mich wissen:

  1. Ich habe eine "Reset Zoom" -Taste hinzugefügt, aber es löscht nur die Grafik und erzeugt eine neue an ihrer Stelle, anstatt tatsächlich die Objekte zu zoomen. Idealerweise sollte der Zoom zurückgesetzt werden.
  2. Ich habe meine eigene Funktion geschrieben, um den Median der X- und Y-Daten zu berechnen. Ich bin mir jedoch sicher, dass es einen besseren Weg geben muss, dies mit d3.median zu tun, aber ich kann nicht herausfinden, wie es funktioniert.

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

Eine sehr vereinfacht (das heißt alte) Version des JS ist unten. Sie können die vollständige Skript finden bei 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)+")"; 
      }); 
    }; 
    } 
}); 

Jede Hilfe massiv geschätzt würde. Vielen Dank!

Edit: Hier ist eine Zusammenfassung der Fixes I, bezogen auf Superboggly Vorschläge unter:

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

Die Zoom-Übersetzung, die ich verwendet wird, sehr ad hoc und verwendet grundsätzlich abitrary Konstanten, die Stellung zu halten, mehr oder weniger am richtigen Ort. Es ist nicht ideal, und ich wäre bereit, Vorschläge für eine universellere Technik zu unterbreiten. In diesem Fall funktioniert es jedoch gut genug.

Antwort

11

Um mit der Median-Funktion zu beginnen, braucht man nur ein Array und einen optionalen Accessor. So können Sie es auf die gleiche Weise max verwenden verwenden:

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

Was die anderen, wenn Sie Ihre Zoomverhalten herausziehen Sie es ein bisschen besser steuern kann. So zum Beispiel statt

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

Versuch:

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

Dann Sie die Zoomstufe und Übersetzung direkt einstellen:

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

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

den Schwenkbereich Beschränkung ist ein bisschen schwieriger.Sie können einfach nicht aktualisieren, wenn die Übersetzung oder Skalierung in Ihrer Zoom-Funktion nicht Ihren Vorstellungen entspricht (oder stellen Sie die "Übersetzung" des Zooms auf das ein, was Sie benötigen). So etwas wie (ich glaube, in Ihrem Fall):

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

Beachten Sie, dass, wenn Sie in mögen Zoomen negativ auf der Achse zu erlauben, aber Schwenken nicht auf Sie Sie in einige schwierigen Szenarien bekommen finden.

Dies könnte datiert werden, aber auch Limiting domain when zooming or panning in D3.js

Hinweis überprüfen, dass die Zoom-Verhalten Funktionalität bei one point zur Begrenzung Schwenken und Zoomen hatte. Aber der Code wurde in einem späteren update herausgenommen.

+0

Danke nochmal Superboggly! Ihr ZoomIn/Reset-Code scheint zu funktionieren, aber der Maßstab ändert sich nur beim nächsten Zoom-Ereignis (d. H. Beim Ziehen oder Mausrad). Es fällt mir schwer, die Dinge per Knopfdruck zu aktualisieren. Es muss einfach sein, aber ich kann es einfach nicht herausfinden. Mein Tastenklick-Code ist: d3.select ('# zoomIn'). Aufruf (Zoom) .on ('click', function() { d3.event.preventDefault(); zm.scale (zm.scale () * 2); }); Ich habe verschiedene Verwendungen von .call(), zoom() und .on() vergeblich versucht. – richardwestenra

+0

Ich habe ein kleines blaues Zoom-Quadrat am unteren Rand meines [letzten Jfiddle für Sie] hinzugefügt (http://jsfiddle.net/superboggly/SD5cK/2/). Alles, was Sie verpassen, ist ein Aufruf zum Zoomen(), nachdem Sie die Skalierung festgelegt haben. Stellen Sie sich die Zoom-Funktion vor, die Sie angegeben haben, indem Sie den vom Verhalten berechneten Zoom-Zustand anwenden. – Superboggly

+0

okay! Sortiert! Es stellt sich heraus, dass es genau das ist, was ich gemacht habe, aber es funktionierte nicht für mich ... Lange Rede, kurzer Sinn: Du hast d3 Version 3 benutzt, während ich Version 2 benutzt habe, und deshalb hat es nicht funktioniert, als ich deine fallen gelassen habe blaues Quadrat in meinem Code. Habe auf v3 aktualisiert und es funktioniert jetzt. Prost :) – richardwestenra

-1

Ich mag es nicht, das Rad neu zu erfinden. Ich suchte nach Streudiagrammen, die das Zoomen ermöglichen. Highcharts ist einer von ihnen, aber es gibt plotly, das auf D3 basiert und nicht nur das Zoomen erlaubt, sondern Sie können auch Linien-Datasets auf dem Streudiagramm haben, die ich mit einigen meiner Datasets begehre, und das ist schwer mit anderen zu finden Plot-Bibliotheken. Ich würde es zu versuchen:

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

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

so schöne Bibliothek verwenden können Sie eine Menge Zeit und Schmerz speichern.