2016-01-13 3 views
2

Я пытаюсь визуализировать проданные предметы из таймсеров. Я использую Nick Rabinowitz's alluvial chart в качестве основы, но сделал несколько модификаций. Все остальное выглядит хорошо, но я хотел бы центрировать штабелированные столбцы по вертикали.Центрирование штабелированных стержней вертикально в d3

Это то, что мой график выглядит на данный момент:

enter image description here

/*Original code obtained from http://nickrabinowitz.com/projects/d3/alluvial/alluvial.html*/ 
 

 
var data = { 
 
    "times": [ 
 
    [{ 
 
     "id": "item1", 
 
     "nodeName": "Item 1 50/2015", 
 
     "nodeValue": 9, 
 
     "incoming": [] 
 
    }, { 
 
     "id": 1, 
 
     "nodeName": "Item 2 50/2015", 
 
     "nodeValue": 6, 
 
     "incoming": [] 
 
    }, { 
 
     "id": 2, 
 
     "nodeName": "Item 3 50/2015", 
 
     "nodeValue": 3, 
 
     "incoming": [] 
 
    }], 
 
    [{ 
 
     "id": "item12", 
 
     "nodeName": "Item 1 51/2015", 
 
     "nodeValue": 8, 
 
     "incoming": [] 
 
    }, { 
 
     "id": 4, 
 
     "nodeName": "Item 2 51/2015", 
 
     "nodeValue": 2, 
 
     "incoming": [] 
 
    }, { 
 
     "id": 5, 
 
     "nodeName": "Item 3 51/2015", 
 
     "nodeValue": 5, 
 
     "incoming": [] 
 
    }], 
 
    [{ 
 
     "id": 6, 
 
     "nodeName": "Item 1 52/2015", 
 
     "nodeValue": 1, 
 
     "incoming": [] 
 
    }, { 
 
     "id": 7, 
 
     "nodeName": "Item 2 52/2015", 
 
     "nodeValue": 7, 
 
     "incoming": [] 
 
    }, { 
 
     "id": 8, 
 
     "nodeName": "Item 3 50/2015", 
 
     "nodeValue": 4, 
 
     "incoming": [] 
 
    }] 
 
    ], 
 
    "links": [{ 
 
     "source": "item1", 
 
     "target": "item12", 
 
     "outValue": 9, 
 
     "inValue": 8 
 
    }, { 
 
     "source": "item12", 
 
     "target": 6, 
 
     "outValue": 8, 
 
     "inValue": 1 
 
    }, { 
 
     "source": 1, 
 
     "target": 4, 
 
     "outValue": 6, 
 
     "inValue": 2 
 
    }, { 
 
     "source": 4, 
 
     "target": 7, 
 
     "outValue": 2, 
 
     "inValue": 7 
 
    }, { 
 
     "source": 2, 
 
     "target": 5, 
 
     "outValue": 3, 
 
     "inValue": 5 
 
    } 
 
    /*, 
 
       { 
 
        "source": 5, 
 
        "target": 8, 
 
        "outValue": 5, 
 
        "inValue": 4 
 
       }*/ 
 
    ] 
 
}; 
 

 
/* Process Data */ 
 

 
// make a node lookup map 
 
var nodeMap = (function() { 
 
    var nm = {}; 
 
    data.times.forEach(function(nodes) { 
 
    nodes.forEach(function(n) { 
 
     nm[n.id] = n; 
 
     // add links and assure node value 
 
     n.links = []; 
 
     n.incoming = []; 
 
     n.nodeValue = n.nodeValue || 0; 
 
    }) 
 
    }); 
 
    console.log(nm); 
 
    return nm; 
 
})(); 
 

 
// attach links to nodes 
 
data.links.forEach(function(link) { 
 
    console.log(link); 
 
    nodeMap[link.source].links.push(link); 
 
    nodeMap[link.target].incoming.push(link); 
 
}); 
 

 
// sort by value and calculate offsets 
 
data.times.forEach(function(nodes) { 
 
    var nCumValue = 0; 
 
    nodes.sort(function(a, b) { 
 
    return d3.descending(a.nodeValue, b.nodeValue) 
 
    }); 
 
    nodes.forEach(function(n, i) { 
 
    n.order = i; 
 
    n.offsetValue = nCumValue; 
 
    nCumValue += n.nodeValue; 
 
    // same for links 
 
    var lInCumValue; 
 
    var lOutCumValue; 
 
    // outgoing 
 
    if (n.links) { 
 
     lOutCumValue = 0; 
 
     n.links.sort(function(a, b) { 
 
     return d3.descending(a.outValue, b.outValue) 
 
     }); 
 
     n.links.forEach(function(l) { 
 
     l.outOffset = lOutCumValue; 
 
     lOutCumValue += l.outValue; 
 
     }); 
 
    } 
 
    // incoming 
 
    if (n.incoming) { 
 
     lInCumValue = 0; 
 
     n.incoming.sort(function(a, b) { 
 
     return d3.descending(a.inValue, b.inValue) 
 
     }); 
 
     n.incoming.forEach(function(l) { 
 
     l.inOffset = lInCumValue; 
 
     lInCumValue += l.inValue; 
 
     }); 
 
    } 
 
    }) 
 
}); 
 
data = data.times; 
 

 
// calculate maxes 
 
var maxn = d3.max(data, function(t) { 
 
    return t.length 
 
    }), 
 
    maxv = d3.max(data, function(t) { 
 
    return d3.sum(t, function(n) { 
 
     return n.nodeValue 
 
    }) 
 
    }); 
 

 
/* Make Vis */ 
 

 
// settings and scales 
 
var w = 960, 
 
    h = 500, 
 
    gapratio = .5, 
 
    padding = 7, 
 
    x = d3.scale.ordinal() 
 
    .domain(d3.range(data.length)) 
 
    .rangeBands([0, w], gapratio), 
 
    y = d3.scale.linear() 
 
    .domain([0, maxv]) 
 
    .range([0, h - padding * maxn]), 
 
    area = d3.svg.area() 
 
    .interpolate('monotone'); 
 

 
// root 
 
var vis = d3.select("#alluvial") 
 
    .append("svg:svg") 
 
    .attr("width", w) 
 
    .attr("height", h); 
 

 
// time slots 
 
var times = vis.selectAll('g.time') 
 
    .data(data) 
 
    .enter().append('svg:g') 
 
    .attr('class', 'time') 
 
    .attr("transform", function(d, i) { 
 
    return "translate(" + x(i) + ",0)" 
 
    }); 
 

 
// node bars 
 
var nodes = times.selectAll('g.node') 
 
    .data(function(d) { 
 
    return d 
 
    }) 
 
    .enter().append('svg:g') 
 
    .attr('class', 'node'); 
 

 
nodes.append('svg:rect') 
 
    .attr('fill', 'steelblue') 
 
    .attr('y', function(n, i) { 
 
    return y(n.offsetValue) + i * padding; 
 
    }) 
 
    .attr('width', x.rangeBand()) 
 
    .attr('height', function(n) { 
 
    return y(n.nodeValue) 
 
    }) 
 
    .append('svg:title') 
 
    .text(function(n) { 
 
    return n.nodeName 
 
    }); 
 

 
// links 
 
var links = nodes.selectAll('path.link') 
 
    .data(function(n) { 
 
    return n.links || [] 
 
    }) 
 
    .enter().append('svg:path') 
 
    .attr('class', 'link') 
 
    .attr('d', function(l, i) { 
 
    var source = nodeMap[l.source]; 
 
    var target = nodeMap[l.target]; 
 
    var gapWidth = x(0); 
 
    var bandWidth = x.rangeBand() + gapWidth; 
 

 
    var sourceybtm = y(source.offsetValue) + 
 
     source.order * padding + 
 
     y(l.outOffset) + 
 
     y(l.outValue); 
 
    var targetybtm = y(target.offsetValue) + 
 
     target.order * padding + 
 
     y(l.inOffset) + 
 
     y(l.inValue); 
 
    var sourceytop = y(source.offsetValue) + 
 
     source.order * padding + 
 
     y(l.outOffset); 
 
    var targetytop = y(target.offsetValue) + 
 
     target.order * padding + 
 
     y(l.inOffset); 
 

 
    var points = [ 
 
     [x.rangeBand(), sourceytop], 
 
     [x.rangeBand() + gapWidth/5, sourceytop], 
 
     [bandWidth - gapWidth/5, targetytop], 
 
     [bandWidth, targetytop], 
 
     [bandWidth, targetybtm], 
 
     [bandWidth - gapWidth/5, targetybtm], 
 
     [x.rangeBand() + gapWidth/5, sourceybtm], 
 
     [x.rangeBand(), sourceybtm] 
 
    ]; 
 

 
    return area(points); 
 
    });
body { 
 
    margin: 3em; 
 
} 
 
.node { 
 
    stroke: #fff; 
 
    stroke-width: 2px; 
 
} 
 
.link { 
 
    fill: #000; 
 
    stroke: none; 
 
    opacity: .3; 
 
} 
 
.node { 
 
    stroke: none; 
 
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script> 
 
<div id="alluvial"></div>

Вот JSFiddle, если вы хотите играть с кодом.

Решение, вероятно, лежит где-то в подсчете полной высоты бара и вычисляет смещения узла от центральной точки.

Способ структурирования исходного кода выглядит как вычисление смещений на узел, а затем использование этих смещений для расчета позиций узлов. Я, вероятно, должен иметь возможность модифицировать это вычисленное смещение в какой-то момент в какой-то точке, но я просто не могу понять, как и где. Если это возможно.

Если это невозможно, есть ли другой способ в d3 для достижения визуально подобных результатов?

ответ

1

Вы могли бы попробовать рассчитали максимально полную высоту с помощью (я просто добавил строки, которые изменяются, остальное то же самое):

//calculate the max full height 
var maxHeight=0; 
data.times.forEach(function(nodes,p) { 
    var curHeight=0; 
    nodes.forEach(function(n) { 
    curHeight+=n.nodeValue; 
    }); 
    if(curHeight > maxHeight) maxHeight=curHeight 
}); 

, а затем добавить (maxHeight/2 - curHeight/2) на смещение, curHeight является общей высота узлов для каждой полосы.

Для этого вы можете добавить пару строк в цикле вычисления смещения:

// sort by value and calculate offsets 
data.times.forEach(function(nodes,p) { 
    var nCumValue = 0; 
    nodes.sort(function(a, b) { 
    return d3.descending(a.nodeValue, b.nodeValue) 
    }); 

    var bandHeight = 0; 
    nodes.forEach(function(n) { 
    bandHeight+=n.nodeValue; 
    }); 

    nodes.forEach(function(n, i) { 
    n.order = i; 
    n.offsetValue = nCumValue + (maxHeight/2-bandHeight/2); 

Here's a JSFiddle с этими изменениями.

+0

Ну, это было простое решение. И это сработало. Спасибо! – locationunknown

Смежные вопросы