2013-02-25 80 views
6

की गणना करें मैंने ज़ूम/पैन कार्यक्षमता के साथ एक d3.js स्कैटर प्लॉट बनाया है। पूर्ण बात यहाँ देख सकते हैं (पूरी बात को देखने के लिए 'एक नई विंडो में खोलें' पर क्लिक करें): http://bl.ocks.org/129f64bfa2b0d48d27c9d3.js स्कैटर प्लॉट - ज़ूम/ड्रैग सीमाएं, ज़ूम बटन, ज़ूम रीसेट करें, औसत

, सुविधाओं की एक जोड़ी है कि मैं यह पता लगाने की असमर्थ रहे हैं कर रहे हैं कि मैं अच्छा लगेगा इसके साथ एक हाथ किसी ने मुझे सही दिशा में बात कर सकते हैं यदि:

  1. मैं, क्षेत्र के लिए एक्स/Y सीमाओं ज़ूम/पैन लागू करने के लिए इतना है कि आप इसे एक निश्चित बिंदु से नीचे खींच कर सकते हैं (उदाहरण के लिए शून्य)।
  2. मैं भी, गूगल मैप्स शैली ज़ूम बटन बनाने +/- पर एक वार कर दिया है किसी भी सफलता नहीं मिली। कोई विचार?

बहुत कम महत्वपूर्ण बात, वहाँ भी उन क्षेत्रों में जहाँ मैं एक समाधान खोज निकाला है के एक जोड़े हैं, लेकिन यह बहुत किसी न किसी तरह है, इसलिए यदि आप एक बेहतर समाधान है तो कृपया मुझे पता है:

  1. मैंने एक 'रीसेट ज़ूम' बटन जोड़ा है, लेकिन यह केवल ग्राफ को हटा देता है और वास्तव में वस्तुओं को ज़ूम करने के बजाय, एक नया स्थान बनाता है। आदर्श रूप से यह वास्तव में ज़ूम रीसेट करना चाहिए।
  2. मैं एक्स और वाई आंकड़ों की माध्यिका की गणना करने के अपने ही समारोह में लिखा है। हालांकि मुझे यकीन है कि 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; 
    }; 
    

एक बहुत ही सरल बनाया है (यानी वर्ष) जे एस के संस्करण के नीचे है। यदि आप कोई मदद बड़े पैमाने पर की सराहना की जाएगी 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 स्थिरांक का उपयोग करता स्थिति अधिक रखने के लिए या सही जगह पर कम। यह आदर्श नहीं है, और मैं अधिक सार्वभौमिक ध्वनि तकनीक के लिए सुझावों का मनोरंजन करने के इच्छुक हूं। हालांकि, यह इस मामले में काफी अच्छी तरह से काम करता है।

उत्तर

11

मंझला समारोह के साथ शुरू करने के लिए बस एक सरणी और एक वैकल्पिक एक्सेसर लेता है। तो आप इसे उसी तरह आप अधिकतम उपयोग करने का उपयोग कर सकते हैं:

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 में लिया गया था।

+0

धन्यवाद फिर से Superboggly! आपका ज़ूम इन/रीसेट कोड काम करता प्रतीत होता है, लेकिन स्केल केवल अगले ज़ूम ईवेंट (यानी ड्रैग या मूसहेल पर) में बदल जाता है। मुझे बटन क्लिक पर अपडेट करने में कठिनाई हो रही है। यह आसान होना चाहिए लेकिन मैं इसे समझ नहीं सकता। मेरा बटन क्लिक कोड है: d3.select ('# zoomIn')। कॉल (ज़ूम) .on ('क्लिक करें', फ़ंक्शन() { d3.event.preventDefault(); zm.scale (zm.scale () * 2); }); मैंने किसी भी लाभ के लिए .call(), ज़ूम() और .on() के विभिन्न उपयोगों का प्रयास नहीं किया है। – richardwestenra

+0

मैंने अपने [आखिरी jsfiddle] के नीचे एक त्वरित थोड़ा नीला ज़ूम वर्ग जोड़ा (http://jsfiddle.net/superboggly/SD5cK/2/)। आप जो भी खो रहे हैं वह स्केल सेट करने के बाद ज़ूम() करने के लिए एक कॉल है। ज़ूम फ़ंक्शन के बारे में सोचें जो आपने व्यवहार द्वारा गणना की गई ज़ूम स्थिति को लागू करने के रूप में प्रदान की है। – Superboggly

+0

ठीक है! क्रमबद्ध किया गया! यह पता चला है कि यह वही है जो मैं कर रहा था लेकिन यह मेरे लिए काम नहीं कर रहा था ... लंबी कहानी छोटी: जब आप संस्करण 2 का उपयोग कर रहे थे, तो आप डी 3 संस्करण 3 का उपयोग कर रहे थे, और यही वजह है कि मैंने आपका काम छोड़ दिया मेरे कोड में नीला वर्ग। V3 में अपग्रेड किया गया है और यह अब काम करता है। चीयर्स :) – richardwestenra

-1

मुझे पहिया को फिर से शुरू करना पसंद नहीं है। मैं स्कैटर प्लॉट की खोज कर रहा था जो ज़ूमिंग की अनुमति देता है। हाईचार्ट्स उनमें से एक है, लेकिन साजिश है, जो डी 3 पर आधारित है और न केवल ज़ूमिंग की अनुमति देता है, बल्कि स्कैटर प्लॉट पर भी लाइन डेटासेट भी हो सकता है, जिसे मैं अपने कुछ डेटासेट्स के साथ चाहता हूं, और दूसरे के साथ मिलना मुश्किल है साजिश पुस्तकालयों। मैं इसे आजमाइए चाहते हैं:

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

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

इस तरह अच्छा पुस्तकालय का उपयोग करके आप समय और दर्द का एक बहुत कुछ बचा सकता है।