2012-10-25 14 views
17

मैं इस तरह एक डेटा संरचना है साथ नेस्टेड डेटा (मान लेते हैं कि डेटा संरचना गैर परक्राम्य है):का मेल जनक और d3.js

data = { 
    segments : [ 
     {x : 20, size : 10, colors : ['#ff0000','#00ff00']}, 
     {x : 40, size : 20, colors : ['#0000ff','#000000']} 
    ]}; 

d3.js जावास्क्रिप्ट पुस्तकालय का उपयोग करना, मैं चाहूँगा चार आयतों को आकर्षित करने के लिए, colors सरणी में प्रत्येक रंग के लिए एक। segments सरणी में प्रत्येक प्रविष्टि से जानकारी का उपयोग color सरणी में प्रत्येक रंग से संबंधित आयतों को आकर्षित करने के लिए किया जाता है। उदाहरण के लिए, लाल और हरे रंग आयतों चौड़ाई और 10 की ऊंचाई जिसके परिणामस्वरूप एचटीएमएल इस तरह दिखना चाहिए होगा:

<div id="container"> 
    <svg width="200" height="200"> 
     <g> 
      <rect x="20" y="20" width="10" height="10" fill="#ff0000"></rect> 
      <rect x="30" y="30" width="10" height="10" fill="#00ff00"></rect> 
     </g> 
     <g> 
      <rect x="40" y="40" width="20" height="20" fill="#0000ff"></rect> 
      <rect x="60" y="60" width="20" height="20" fill="#000000"></rect> 
     </g> 
    </svg> 
</div> 

मैं कुछ कोड है कि पूरा करता है के साथ आ गया है, लेकिन मैं का उपयोग कर के बारे में हिस्सा मिला भ्रमित होने के लिए data में घोंसले के दो अलग-अलग स्तरों से डेटा, और मुझे लगता है कि d3.js. के साथ इसे पूरा करने के लिए एक और बेवकूफ तरीका हो सकता है। यहाँ कोड (http://jsbin.com/welcome/39650/edit पर पूर्ण उदाहरण) है:

function pos(d,i) { return d.x + (i * d.size); } // rect position 
function size(d,i) { return d.size; }   // rect size 
function f(d,i) { return d.color; }    // rect color 

// add the top-level svg element and size it 
vis = d3 
    .select('#container') 
    .append('svg') 
    .attr('width',200) 
    .attr('height',200); 

// add the nested svg elements 
var nested = vis 
    .selectAll('g') 
    .data(data.segments) 
    .enter() 
    .append('g'); 

// Add a rectangle for each color 
nested 
    .selectAll('rect') 
    .data(function(d) { 
     // **** ATTENTION **** 
     // Is there a more idiomatic, d3-ish way to approach this? 
     var expanded = []; 
     for(var i = 0; i < d.colors.length; i++) { 
      expanded.push({ 
       color : d.colors[i], 
       x  : d.x 
       size : d.size }); 
     } 
     return expanded; 
    }) 
    .enter() 
    .append('rect') 
    .attr('x',pos) 
    .attr('y',pos) 
    .attr('width',size) 
    .attr('height',size) 
    .attr('fill',f); 

वहाँ d3.js का उपयोग कर एक डेटा संरचना में घोंसले के दो विभिन्न स्तरों से डेटा का उपयोग करने के लिए एक बेहतर और/या अधिक मुहावरेदार रास्ता नहीं है?

संपादित

यहाँ बंद विचार के लिए समाधान मैं के साथ आया था, meetamit's answer करने के लिए धन्यवाद है, और nautat's answer के लिए और अधिक मुहावरेदार d3.js खरोज धन्यवाद का उपयोग कर:

$(function() { 
    var 
    vis = null, 
    width = 200, 
    height = 200, 
    data = { 
     segments : [ 
      {x : 20, y : 0, size : 10, colors : ['#ff0000','#00ff00']}, 
      {x : 40, y : 0, size : 20, colors : ['#0000ff','#000000']} 
     ] 
    }; 

    // set the color 
    function f(d,i) {return d;} 

    // set the position 
    function pos(segment) { 
     return function(d,i) { 
     return segment.x + (i * segment.size); 
     }; 
    } 

    // set the size 
    function size(segment) { 
     return function() { 
     return segment.size; 
     }; 
    } 

    // add the top-level svg element and size it 
    vis = d3.select('#container').append('svg') 
     .attr('width',width) 
     .attr('height',height); 

    // add the nested svg elements 
    var nested = vis 
     .selectAll('g') 
      .data(data.segments) 
     .enter().append('g'); 

    // Add a rectangle for each color. Size of rectangles is determined 
    // by the "parent" data object. 
    nested 
    .each(function(segment, i) { 
     var 
      ps = pos(segment), 
      sz = size(segment); 

     var colors = d3.select(this) 
     .selectAll('rect') 
      .data(segment.colors) 
     .enter().append('rect') 
      .attr('x', ps) 
      .attr('y',ps) 
      .attr('width', sz) 
      .attr('height',sz) 
      .attr('fill', f); 
    }); 

}); 

यहाँ पूर्ण काम कर उदाहरण दिया गया है : http://jsbin.com/welcome/42885/edit

+0

आपको अपने एचटीएमएल कोड में नेस्टेड एसवीजी टैग क्यों चाहिए? – btel

+0

मैं एक साथ 'समूह' के अनुरूप सभी आयतों को एक साथ समूहीकृत करना चाहता हूं। अगर मैं सही ढंग से समझता हूं, तो मैं माता-पिता 'svg' तत्व के 'x' और' y' गुणों को बदलकर सभी आयतों को पहले' सेगमेंट 'से स्थानांतरित कर सकता हूं। यदि कोई बेहतर तरीका है, तो मुझे इसके बारे में जानना अच्छा लगेगा। –

+0

ट्रांसफॉर्म विशेषता के साथ समूह तत्व के बारे में: ' ...': http://www.w3.org/TR/SVG/coords.html#TransformAttribute। अगर मैं सही ढंग से समझता हूं तो आपके पास प्रत्येक ड्राइंग के लिए केवल एक svg होना चाहिए। – btel

उत्तर

27

आप बंद

var nested = vis 
    .selectAll('g') 
    .data(data.segments); 


nested.enter() 
    .append('g') 
    .each(function(segment, i) { 
    var colors = d3.select(this) 
     .selectAll('rect') 
     .data(segment.colors); 

    colors.enter() 
     .append('rect') 
     .attr('x', function(color, j) { return pos(segment, j); }) 
     // OR: .attr('x', function(color, j) { return segment.x + (j * segment.size); }) 
     .attr('width', function(color, j) { return size(segment); }) 
     .attr('fill', String); 
    }); 
+1

बंद करने का उपयोग करना मेरे लिए सबसे अच्छा लगता है। चीजों को काम करने के लिए मुझे आपके उदाहरण को थोड़ा सा संशोधित करना पड़ा, लेकिन सामान्य विचार सिर्फ वही है जो मैं ढूंढ रहा था। पूर्ण कार्य उदाहरण के लिए मेरे प्रश्न में संपादन देखें। –

1

मैं वास्तव में तत्व बनाने शुरू करने से पहले colors को फ़्लैट करने का प्रयास करूंगा। यदि डेटा में परिवर्तन होते हैं तो मैं इस फ़्लैट किए गए डेटा स्ट्रक्चर को अपडेट कर दूंगा। असली डी 3 संक्रमण संभव बनाने के लिए फ़्लैट किए गए डेटा को कहीं भी संग्रहीत करने की आवश्यकता है।

यहां एक लंबा उदाहरण है जो मेरे लिए काम करता है। योन इसे here कार्रवाई में देख सकता है।

var data = { 
    segments : [ 
     {x : 20, size : 10, colors : ['#ff0000','#00ff00']}, 
     {x : 40, size : 20, colors : ['#0000ff','#000000']} 
    ] 
}; 

function pos(d,i) { return d.x + (i * d.size); } // rect position 
function size(d,i) { return d.size; }   // rect size 
function f(d,i) { return d.color; }    // rect color 

function flatten(data) { 
    // converts the .colors to a ._colors list 
    data.segments.forEach(function(s,i) { 
     var list = s._colors = s._colors || []; 
     s.colors.forEach(function(c,j) { 
      var obj = list[j] = list[j] || {} 
      obj.color = c 
      obj.x = s.x 
      obj.size = s.size 
     }); 
    }); 
} 

function changeRect(chain) { 
    return chain 
    .transition() 
    .attr('x',pos) 
    .attr('y',pos) 
    .attr('width',size) 
    .attr('height',size) 
    .attr('fill',f) 
    .style('fill-opacity', 0.5) 
} 

vis = d3 
.select('#container') 
.append('svg') 
.attr('width',200) 
.attr('height',200); 

// add the top-level svg element and size it 
function update(){ 

    flatten(data); 

    // add the nested svg elements 
    var all = vis.selectAll('g') 
    .data(data.segments) 

    all.enter().append('g'); 
    all.exit().remove(); 

    // Add a rectangle for each color 
    var rect = all.selectAll('rect') 
    .data(function (d) { return d._colors; }, function(d){return d.color;}) 

    changeRect(rect.enter().append('rect')) 
    changeRect(rect) 

    rect.exit().remove() 
} 

function changeLater(time) { 
    setTimeout(function(){ 
     var ds = data.segments 
     ds[0].x = 10 + Math.random() * 100; 
     ds[0].size = 10 + Math.random() * 100; 
     ds[1].x = 10 + Math.random() * 100; 
     ds[1].size = 10 + Math.random() * 100; 
     if(time == 500) ds[0].colors.push("orange") 
     if(time == 1000) ds[1].colors.push("purple") 
     if(time == 1500) ds[1].colors.push("yellow") 
     update() 
    }, time) 
} 

update() 
changeLater(500) 
changeLater(1000) 
changeLater(1500) 

यहां महत्वपूर्ण flatten समारोह जो डेटा रूपांतरण और दुकानों करता है/माता-पिता डेटा तत्व में _colors संपत्ति के रूप में परिणाम पुनः उपयोग कर लेता:

यहाँ कोड है। एक और महत्वपूर्ण रेखा है;

.data(function (d) { return d._colors; }, function(d){return d.color;}) 

जो जहां डाटा (पहले पैरामीटर) और (दूसरा पैरामीटर) क्या प्रत्येक डेटा तत्व के लिए अद्वितीय आईडी है प्राप्त करने के लिए निर्दिष्ट करता है। यह संक्रमण के लिए मौजूदा रंगों की पहचान करने में मदद करता है, इत्यादि।

3

उपयोग कर सकते हैं आप निम्नलिखित की तरह कुछ कर सकते हैं अपने डेटा पुनर्गठन करने:

newdata = data.segments.map(function(s) { 
    return s.colors.map(function(d) { 
    var o = this; // clone 'this' in some manner, for example: 
    o = ["x", "size"].reduce(function(obj, k) { return(obj[k] = o[k], obj); }, {}); 
    return (o.color = d, o); 
    }, s); 
}); 

इस में अपने इनपुट डेटा को बदलने होगा:

// newdata: 
    [ 
     [ 
     {"size":10,"x":20,"color":"#ff0000"}, 
     {"size":10,"x":20,"color":"#00ff00"}], 
     [ 
     {"size":20,"x":40,"color":"#0000ff"}, 
     {"size":20,"x":40,"color":"#000000"} 
     ] 
    ] 

जो तो मानक नेस्टेड डेटा चयन पैटर्न में उपयोग किया जा सकता है:

var nested = vis.selectAll('g') 
    .data(newdata) 
    .enter().append('g'); 

nested.selectAll('rect') 
    .data(function(d) { return d; }) 
    .enter().append('rect') 
    .attr('x',pos) 
    .attr('y',pos) 
    .attr('width',size) 
    .attr('height',size) 
    .attr('fill',f); 

बीटीडब्ल्यू, यदि आप अधिक डी 3-बेवकूफ बनना चाहते हैं, तो मैं इंडेंटेशन शैली को जंजीर तरीकों के लिए थोड़ा सा बदल दूंगा। माइक ने चयन में हर बार आधे इंडेंटेशन का उपयोग करने का प्रस्ताव रखा। इससे यह स्पष्ट हो जाता है कि आप किस चयन पर काम कर रहे हैं। उदाहरण के लिए अंतिम कोड में; परिवर्तनीय nestedenter() चयन को संदर्भित करता है। 'चयन' अध्याय देखें: http://bost.ocks.org/mike/d3/workshop/

+0

इंडेंटेशन शैली के बारे में टिप के लिए धन्यवाद। मैंने देखा कि d3.js कोड लिखने वाले लोग इस तरह से इंडेंट कर रहे थे, लेकिन प्रेरणा को समझ में नहीं आया। लिंक बहुत उपयोगी था! –

+0

मैं कहूंगा कि यह वास्तव में चीजों को करने का डी 3 तरीका है (माइक Bostock के अनुसार)। यहां देखें http://bost.ocks.org/mike/nest/#data –